@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Source+Sans+3:wght@400;600&display=swap');

:root {
  --ink: #13213e;
  --ink-soft: #262f42;
  --muted: #6b7480;
  --line: #e3e6ec;
  --paper: #ffffff;
  /* Page canvas sits a shade off pure white so white surfaces (cards,
     sidebar, topbar) visibly lift off it — --card is the same family, a
     touch deeper still, used for fills *inside* a white surface (hover
     states, the segmented control's track) rather than the canvas itself. */
  --bg-page: #f7f5f1;
  --card: #f0ede7;
  --card-border: rgba(19, 33, 62, 0.08);
  --shadow-card: 0 1px 2px rgba(19, 33, 62, 0.05), 0 10px 24px -10px rgba(19, 33, 62, 0.16);
  --shadow-card-hover: 0 2px 4px rgba(19, 33, 62, 0.06), 0 16px 32px -12px rgba(19, 33, 62, 0.2);
  --accent: #2e7d46;
  --accent-soft: #e7f0ea;
  --ok: var(--accent);
  /* Per-area category colors — Financial and Schedule both reuse --accent
     green (there's a single green accent now; no separate brass/gold). */
  --slate: #51637f;
  --slate-soft: #e8edf3;
  --navy-soft: #e7eaf1;

  /* ---- sidebar (desktop persistent nav) dark theme ----
     The sidebar itself is a dark navy surface (--ink) rather than white, so
     it needs its own text/icon/hover/active tokens — the light-canvas
     versions above are all calibrated for a white background and read as
     near-invisible or too-low-contrast against navy. Bottom-nav and the
     topbar are unaffected; they stay on the light canvas. */
  --sidebar-text: rgba(255, 255, 255, 0.85);
  --sidebar-text-strong: #ffffff;
  --sidebar-text-muted: rgba(255, 255, 255, 0.55);
  --sidebar-icon: rgba(255, 255, 255, 0.6);
  --sidebar-hover-bg: rgba(255, 255, 255, 0.07);
  --sidebar-active-bg: rgba(255, 255, 255, 0.11);
  --sidebar-active-bar: #8fa6d1;
  --sidebar-line: rgba(255, 255, 255, 0.12);
  /* Brightened green for the active nav icon — plain --accent is calibrated
     for a white background and reads as too dark/muted against navy. */
  --accent-on-dark: #6fcf97;

  --serif: "Cormorant Garamond", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --sans: "Source Sans 3", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --radius: 12px;
  --radius-sm: 8px;
  --wrap: 1320px;

  /* One spacing scale, used everywhere instead of one-off pixel values. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  --sidebar-w: 196px;
  --bottom-nav-h: 60px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  /* A gentle tonal drift on the canvas rather than a flat fill — kept close
     enough to --bg-page that cards, dashboards, and text sitting on top
     stay just as legible as they were on the flat color. */
  background: linear-gradient(160deg, #faf8f4 0%, var(--bg-page) 45%, #f1eee7 100%);
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Belt-and-suspenders: nothing in this layout should ever need horizontal
   page scroll, on any screen size. */
html { overflow-x: hidden; }
body { overflow-x: hidden; max-width: 100vw; }

a { color: var(--accent); }

.topbar {
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  box-shadow: 0 1px 4px rgba(19, 33, 62, 0.04);
  position: sticky;
  top: 0;
  z-index: 20;
  padding-top: var(--safe-top);
}
.topbar-inner {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 10px var(--sp-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.wordmark {
  font-family: var(--serif);
  color: var(--ink);
  text-decoration: none;
  display: flex;
  align-items: center;
}
.logo-img {
  height: 32px;
  width: auto;
  display: block;
}

/* margin-left: auto (not just .topbar-inner's justify-content:space-between)
   is what actually pins this to the right — space-between only creates a
   gap *between* two-or-more items, so on desktop, where .wordmark is
   display:none and .session is topbar-inner's only remaining flex child,
   space-between alone collapses to flex-start and the chip would end up
   flush-left instead of pinned right. auto margins push regardless of
   how many siblings are actually present. */
.session { display: flex; align-items: center; margin-left: auto; }

/* ---- user chip (topbar, both mobile and desktop) ---- */
.user-chip { position: relative; }
.user-chip-summary {
  display: flex; align-items: center; gap: 8px;
  list-style: none; cursor: pointer; user-select: none;
  padding: 4px 8px 4px 4px; border-radius: 999px;
}
.user-chip-summary::-webkit-details-marker { display: none; }
.user-chip-summary::marker { content: ""; }
.user-chip-summary:hover { background: var(--card); }
.user-avatar {
  width: 28px; height: 28px; border-radius: 50%; flex: none;
  background: var(--accent-soft); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
}
.user-chip-name { font-size: 14px; color: var(--ink-soft); font-weight: 500; white-space: nowrap; }
.user-chip-caret { display: flex; color: var(--muted); }
.user-chip-caret svg { width: 13px; height: 13px; display: block; transition: transform 0.15s; }
.user-chip[open] .user-chip-caret svg { transform: rotate(180deg); }
.user-chip-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  min-width: 190px; background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-shadow: 0 8px 24px rgba(20, 30, 50, 0.14);
  padding: var(--sp-3);
}
.user-chip-menu-name { font-weight: 600; color: var(--ink); font-size: 14px; }
.user-chip-menu-title { font-size: 12px; color: var(--muted); margin-top: 2px; }
.user-chip-menu-link {
  display: block; margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--line); font-size: 14px; color: var(--ink-soft); text-decoration: none;
}
.user-chip-menu-link:hover { color: var(--accent); }
@media (max-width: 480px) {
  .user-chip-name { display: none; }
}
.btn {
  font: inherit;
  font-size: 14px;
  padding: 8px 16px;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
  transition: border-color .15s, background .15s;
}
.btn:hover { border-color: var(--accent); }
.btn-primary {
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}
.btn-primary:hover { background: #12203a; border-color: #12203a; }

/* width: 100% is load-bearing here, not decorative — .wrap is a flex item
   inside .app-main's column flex context, and a margin: auto cross-axis
   item without an explicit width shrinks to its content's natural size
   instead of stretching, so max-width alone silently had no effect on
   pages with less content than the cap. */
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: var(--sp-5) var(--sp-5) var(--sp-6); }

.page-head { margin: 0 0 var(--sp-4); }
.eyebrow {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--sp-2);
}
h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 30px;
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 var(--sp-2);
}
/* No fixed ch cap — with --wrap widened, a hardcoded character count made
   ledes wrap a line early even with plenty of room; the page-head/.wrap
   container width is the only bound that actually matters now. */
.lede { color: var(--muted); margin: 0; font-size: 15px; }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-4);
  margin-top: 0;
}
.card {
  background: var(--paper);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: var(--sp-4);
  box-shadow: var(--shadow-card);
}
.card + .card { margin-top: var(--sp-3); }
.row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; padding: 12px 0; border-bottom: 1px solid var(--line);
}
.row:last-child { border-bottom: 0; }
.row .when { color: var(--muted); font-size: 14px; min-width: 120px; }
.row .what { font-weight: 500; }
/* Neutral by default (status tags, entity sub-types, time chips) — the
   restrained per-area colors below are opt-in via a second class, applied
   only where a tag actually represents one of the four sidebar Areas, so
   green/navy/slate read as a deliberate category cue rather than decoration
   slapped on every pill in the app. Financial and Schedule share the same
   green — there's a single green accent now, no separate gold. */
.tag {
  font-size: 12px; padding: 3px 9px; border-radius: 999px;
  background: var(--card); color: var(--ink-soft);
}
.tag-financial, .tag-schedule { background: var(--accent-soft); color: var(--accent); }
.tag-portfolio { background: var(--navy-soft); color: var(--ink); }
.tag-property { background: var(--slate-soft); color: var(--slate); }

.placeholder-note {
  margin-top: 14px; padding: 10px 14px;
  border: 1px dashed var(--line); border-radius: var(--radius-sm);
  background: #fff; color: var(--muted); font-size: 13px;
}

/* ---- sign-in / landing (unauthenticated) ----
   Split-screen entrance: a branded navy panel (left) beside a plain
   sign-in panel (right) — the "front door" of the portal, styled closer
   to a private bank's entrance than a SaaS landing page. .signin-split
   carries .gate itself (the section auth.js shows/hides), so the
   three-class override is needed for the same reason .sidebar-link.gate.show
   is: outrank plain .gate.show's display:block by specificity, not source
   order, so this renders as a row instead of a block once shown. */
.signin-split { min-height: 100vh; }
.signin-split.gate.show { display: flex; }

.signin-branding {
  flex: 0 0 46%;
  min-width: 0;
  position: relative;
  overflow: hidden;
  /* A layered navy rather than a flat fill — lighter near the top-left
     where the logo sits, deepening toward the bottom-right for real
     dimension instead of a flat poster-color panel. */
  background: linear-gradient(160deg, #21375f 0%, var(--ink) 45%, #0a1526 100%);
  display: flex;
  align-items: center;
  padding: var(--sp-7) var(--sp-6);
}
/* A faint abstract topographic linework — a handful of flowing contour
   lines, tiled at low opacity — for texture that doesn't compete with the
   copy (not a literal illustration). Sits behind .signin-branding-inner
   regardless of DOM order, same as the vignette below — that's what the
   inner element's own stacking context (position:relative + z-index) is
   for, not paint order. */
.signin-branding::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.08;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cpath d='M-40,90 C120,30 220,150 380,90 C480,50 560,110 660,80' stroke='%233d5a8a' stroke-width='1.4' fill='none'/%3E%3Cpath d='M-40,190 C120,250 220,150 380,210 C480,250 560,170 660,210' stroke='%233d5a8a' stroke-width='1.4' fill='none'/%3E%3Cpath d='M-40,300 C120,240 220,360 380,290 C480,240 560,330 660,290' stroke='%233d5a8a' stroke-width='1.4' fill='none'/%3E%3Cpath d='M-40,410 C120,470 220,370 380,430 C480,470 560,390 660,430' stroke='%233d5a8a' stroke-width='1.4' fill='none'/%3E%3Cpath d='M-40,510 C120,450 220,560 380,500 C480,450 560,540 660,500' stroke='%233d5a8a' stroke-width='1.4' fill='none'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 600px 600px;
  pointer-events: none;
}
.signin-branding::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.35) 100%);
  pointer-events: none;
}
.signin-branding-inner { position: relative; z-index: 1; max-width: 460px; width: 100%; padding-top: var(--sp-4); }
/* Same recoloring trick as .sidebar-logo: the mark is navy-on-transparent,
   invisible on this same navy panel without it. */
.signin-logo-light { height: 50px; width: auto; display: block; filter: brightness(0) invert(1); margin-bottom: var(--sp-6); }
/* A gradient fade rather than a flat bar, with a faint glow of its own —
   a little presence without turning into a solid rule. Green, not gold:
   --accent-on-dark is the same brightened green already used for
   legibility against this navy (see the sidebar's active-icon comment) —
   the rest of the app settled on one green accent, not a second color. */
.signin-accent-rule {
  display: block; width: 68px; height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, var(--accent-on-dark), rgba(111, 207, 151, 0));
  box-shadow: 0 0 14px rgba(111, 207, 151, 0.35);
  margin-bottom: var(--sp-4);
}
.signin-branding .signin-eyebrow { color: var(--accent-on-dark); font-weight: 700; letter-spacing: 0.16em; margin: 0 0 var(--sp-4); }
/* The clear focal point, pulled back a step from the last pass (it was
   overwhelming the panel). Line breaks are hardcoded in the markup, not
   left to wrap wherever the column happens to end. */
.signin-headline {
  font-family: var(--serif); font-weight: 500; font-size: 48px; line-height: 1.12; letter-spacing: -0.01em;
  color: #fff; margin: 0 0 var(--sp-5);
}
.signin-sub { font-size: 14px; line-height: 1.6; color: rgba(255, 255, 255, 0.6); margin: 0; max-width: 34ch; }

.signin-panel {
  flex: 1 1 auto;
  background: var(--bg-page);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-7) var(--sp-6);
}
/* A defined card, not bare text on blank canvas — gives the sign-in block
   its own visual weight so it reads as a considered object rather than
   floating in empty space next to the branding panel. */
.signin-panel-inner {
  max-width: 370px; width: 100%; text-align: center;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: var(--sp-5);
  /* Lighter and broader than the standard .card shadow — a soft, wide
     cast shadow rather than a card popping off the page. */
  box-shadow: 0 2px 4px rgba(19, 33, 62, 0.04), 0 28px 48px -20px rgba(19, 33, 62, 0.14);
}
.signin-panel-heading { font-family: var(--serif); font-weight: 500; font-size: 22px; color: var(--ink); margin: 0 0 var(--sp-2); }
.signin-panel-subtext { font-size: 13px; color: var(--muted); margin: 0 0 var(--sp-4); }
.signin-panel .security-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; padding: 3px 10px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent);
  margin-bottom: var(--sp-4);
}
/* .btn's default element is <a>, inline by default — width:100% alone is a
   no-op on an inline box, which let the label wrap mid-word instead of
   centering on one line. display:flex is what actually makes the 100%
   width (and centering) take effect; the gap makes room for the Microsoft
   mark without a separate margin rule. */
.signin-panel .btn-primary {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: var(--sp-3) var(--sp-4); font-size: 15px;
}
.signin-panel .btn-primary .ms-logo { width: 16px; height: 16px; flex: none; }
.signin-panel .signin-note { margin: var(--sp-4) auto 0; font-size: 12px; color: var(--muted); max-width: 32ch; }
/* A discreet trailing link, set off by its own hairline rather than sitting
   flush under the note — same recipe as .user-chip-menu-link's separator. */
.signin-support {
  display: block; margin-top: var(--sp-4); padding-top: var(--sp-3);
  border-top: 1px solid var(--line); font-size: 12px; color: var(--muted); text-decoration: none;
}
.signin-support:hover { color: var(--ink-soft); }

/* Between the full split (>1100px) and the stack (≤860px), the branding
   panel is narrower than the headline's desktop scale assumes — without
   this, a line wraps mid-word inside the ~46% column. */
@media (max-width: 1100px) and (min-width: 861px) {
  .signin-headline { font-size: 36px; }
  .signin-branding-inner { max-width: 380px; }
}

@media (max-width: 860px) {
  .signin-split.gate.show { flex-direction: column; }
  .signin-branding { flex: 0 0 auto; padding: var(--sp-6) var(--sp-5); }
  .signin-branding-inner { padding-top: 0; }
  .signin-logo-light { height: 38px; margin-bottom: var(--sp-4); }
  .signin-headline { font-size: 27px; }
  .signin-sub { max-width: none; }
  .signin-panel { padding: var(--sp-6) var(--sp-5); justify-content: center; }
  .signin-panel-inner { padding: var(--sp-4); }
}

.gate { display: none; }
.gate.show { display: block; }

/* ---- persistent nav: sidebar (desktop) + bottom tab bar (mobile) ---- */
.app-shell { display: flex; align-items: stretch; min-height: 100vh; }
.app-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }
.app-main > .wrap { flex: 1 1 auto; }
/* The sidebar is now static markup with no fade of its own — it should
   read as the one thing that stays put across a navigation. This fade lives
   only on the content column, so a full-page (multi-page-app) navigation
   still reads as "the content changed" rather than "the whole page reloaded". */
@keyframes contentFadeIn { from { opacity: 0; } to { opacity: 1; } }
.app-main > .wrap { animation: contentFadeIn 160ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  .app-main > .wrap { animation: none; }
}

/* Data areas (dashboard lists, schedule, notifications, team updates...)
   fade in whenever fetched content actually replaces a placeholder or an
   older render — see page-cache.js's triggerFadeIn. Deliberately quicker
   than the page-level contentFadeIn above: this fires far more often (every
   background refresh that finds new data), so it needs to read as a quiet
   settle-in, not a repeated attention-grab. */
@keyframes dataFadeIn { from { opacity: 0; } to { opacity: 1; } }
.fade-in { animation: dataFadeIn 120ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  .fade-in { animation: none; }
}

.nav-icon { display: flex; flex: none; }
.nav-icon svg { width: 20px; height: 20px; display: block; }

/* Fixed, not sticky — on any page whose content runs taller than one
   viewport, a `position: sticky` sidebar only remains pinned to the
   viewport for part of the scroll range (its containing block's own
   overflow handling determines the rest), so once you scrolled far enough
   the whole navy panel would vanish and the canvas background showed
   through instead. `position: fixed` sidesteps that entirely — the sidebar
   is always exactly one viewport tall and always visible, independent of
   how much content sits next to it. app-main gets an explicit left margin
   below to make room for it, since a fixed element no longer takes up space
   in the flex row. */
.sidebar {
  flex: 0 0 var(--sidebar-w);
  flex-direction: column;
  /* Same layered-navy idea as .signin-branding, just gentler — a narrow
     column reads a flat fill as flatter than a wide panel does, so this
     stays subtle rather than echoing that gradient's full contrast. */
  background: linear-gradient(180deg, #1c2f52 0%, var(--ink) 45%, #0d1729 100%);
  box-shadow: 4px 0 24px -12px rgba(19, 33, 62, 0.4);
  padding: var(--sp-3) var(--sp-3) var(--sp-5);
  position: fixed;
  top: 0;
  left: 0;
  width: var(--sidebar-w);
  height: 100vh;
  overflow-y: auto;
  z-index: 25;
  /* Firefox: thin thumb, track blends into the sidebar instead of showing
     the browser's default gray. Webkit gets the equivalent via the
     pseudo-elements below. */
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
}
.sidebar::-webkit-scrollbar { width: 6px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.18); border-radius: 3px; }
.sidebar::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.28); }
/* Mobile default: even when the role check adds .show, the sidebar itself
   stays hidden — the bottom nav is the mobile equivalent. Widened out again
   at desktop widths below. */
.sidebar.gate.show { display: none; }

.sidebar-logo { display: block; margin: 0 var(--sp-1) var(--sp-5); padding: var(--sp-1) 0; }
/* The logo file itself is navy-on-transparent — invisible against this same
   navy sidebar without recoloring it. It's a single-color mark, so crushing
   to black then inverting turns it white while keeping the original alpha
   shape, without needing a second logo asset. */
.sidebar-logo img { height: 54px; width: auto; display: block; margin: 0 auto; filter: brightness(0) invert(1); }

.sidebar-links { display: flex; flex-direction: column; gap: var(--sp-1); }
.sidebar-link {
  align-items: center; gap: var(--sp-3);
  padding: var(--sp-2); border-radius: var(--radius-sm);
  color: var(--sidebar-text); text-decoration: none; font-size: 14px;
  min-height: 40px;
}
/* .sidebar-link always carries the shared .gate class — display must live
   here, at higher specificity than plain .gate.show, or a plain
   `.sidebar-link { display: flex }` and `.gate { display: none }` (equal
   specificity) would resolve by source order alone, showing every link
   regardless of role. */
.sidebar-link.gate.show { display: flex; }
.sidebar-link .nav-icon { color: var(--sidebar-icon); }
.sidebar-link:hover { background: var(--sidebar-hover-bg); }
/* A subtly lighter navy panel + a light-blue left bar is the one consistent
   "you are here" cue, plus the icon itself switches to green (see the
   .active .nav-icon rule below) — every other icon, on every other page,
   stays the same neutral color. Per-area icon color-coding was retired in
   favor of this single, unambiguous "you are here" signal. */
.sidebar-link.active { background: var(--sidebar-active-bg); color: var(--sidebar-text-strong); font-weight: 600; box-shadow: inset 3px 0 0 var(--sidebar-active-bar); }
.sidebar-link.active .nav-icon { color: var(--accent-on-dark); }
.sidebar-link:focus-visible { outline: 2px solid var(--sidebar-active-bar); outline-offset: 1px; }

.sidebar-admin { margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--sidebar-line); }
.sidebar-admin.gate.show { display: block; }
.sidebar-section-label {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--sidebar-text-muted); margin: 0 0 var(--sp-2) var(--sp-2);
}
.sidebar-section-label--spaced { margin-top: var(--sp-4); }

/* Pinned to the very bottom of the sidebar via the parent's column flex —
   collapses to zero extra space (and just scrolls with the rest) if the nav
   list ever grows tall enough to fill the viewport, rather than overlapping
   anything. data-session-sidebar is the mount point auth.js fills in; the
   layout/divider styling lives here so auth.js only ever owns the content. */
.sidebar-footer { margin-top: auto; padding-top: var(--sp-4); border-top: 1px solid var(--sidebar-line); }
.sidebar-user { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2); }
.sidebar-user-avatar {
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  background: rgba(255, 255, 255, 0.12); color: var(--sidebar-text-strong);
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
}
.sidebar-user-text { display: flex; flex-direction: column; min-width: 0; }
.sidebar-user-name {
  font-size: 14px; font-weight: 600; color: var(--sidebar-text-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-user-title {
  font-size: 12px; color: var(--sidebar-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-signout {
  display: block; margin-top: 2px; padding: var(--sp-1) var(--sp-2) 0;
  font-size: 12px; color: var(--sidebar-text-muted); text-decoration: none;
}
.sidebar-signout:hover { color: var(--sidebar-text-strong); }

.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  background: var(--paper); border-top: 1px solid var(--line);
  box-shadow: 0 -1px 4px rgba(19, 33, 62, 0.04);
  padding: var(--sp-1) var(--sp-1) calc(var(--sp-1) + var(--safe-bottom));
  align-items: stretch; justify-content: space-around;
  overflow-x: auto;
}
.bottom-nav.gate.show { display: flex; }
.bottom-nav-link {
  flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; flex: 1 1 0; min-width: 56px; min-height: 48px;
  padding: var(--sp-1); color: var(--muted); text-decoration: none;
  font-size: 11px; border-radius: var(--radius-sm);
}
/* See the .sidebar-link.gate.show comment above — same equal-specificity trap. */
.bottom-nav-link.gate.show { display: flex; }
.bottom-nav-link.active { color: var(--ink); font-weight: 600; }
.bottom-nav-link.active .nav-icon { color: var(--accent); }
.bottom-nav-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

@media (min-width: 641px) {
  .sidebar.gate.show { display: flex; }
  .bottom-nav.gate.show { display: none; }
  /* Desktop's own sidebar already carries the logo and (now) the signed-in
     user's profile/sign-out — the topbar would just be an empty bordered
     strip once both are hidden, so it's removed outright instead of kept
     as dead chrome. Untouched below 641px, where it's still the only place
     either one appears (the sidebar stays hidden on mobile). */
  body:has(.sidebar.show) .topbar { display: none; }
  /* The sidebar is position:fixed (see above) and so no longer occupies
     space in the flex row itself — app-main needs an explicit offset or
     it would render underneath it, full-width. */
  body:has(.sidebar.show) .app-main { margin-left: var(--sidebar-w); }
}
@media (max-width: 640px) {
  body:has(.bottom-nav.show) .app-main { padding-bottom: calc(var(--bottom-nav-h) + var(--safe-bottom) + var(--sp-4)); }
}

.backlink {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 14px; color: var(--ink-soft); text-decoration: none; margin-bottom: 18px;
}
.backlink:hover { color: var(--accent); }

.footer {
  width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 28px 24px;
  color: var(--muted); font-size: 12px; border-top: 1px solid var(--line);
}

@media (prefers-reduced-motion: reduce) {
  .portfolio-tile { transition: none; }
}

/* ---- preview mode ---- */
.preview-banner {
  background: var(--ink);
  color: #fff;
  font-size: 13px;
  text-align: center;
  padding: 10px 16px;
  letter-spacing: 0.01em;
}
.preview-banner b { color: #fff; font-weight: 600; }
.pill-preview {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 5px 12px;
  border-radius: 999px;
}

.detail { list-style: none; margin: 0; padding: 0; }
.detail li { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.detail li:last-child { border-bottom: 0; }
.detail .lab { color: var(--muted); font-size: 14px; }
.detail .val { text-align: right; }

.check { list-style: none; margin: 0; padding: 0; }
.check li { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
.check .box { width: 18px; height: 18px; border: 1.5px solid var(--line); border-radius: 5px; flex: none; }
.check .box.done { background: var(--ok); border-color: var(--ok); position: relative; }
.check .box.done::after { content: "✓"; color: #fff; font-size: 12px; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.check .done-text { color: var(--muted); text-decoration: line-through; }

.card h3 {
  font-family: var(--serif);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 var(--sp-3);
  font-size: 18px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.card h3::before {
  content: ""; display: inline-block; flex: none;
  width: 3px; height: 15px; border-radius: 2px; background: var(--accent);
}
.card-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 var(--sp-3); }
.card-head h3 { margin: 0; }

/* ---- editing controls ---- */
.area-actions { display: flex; gap: var(--sp-2); align-items: center; margin-bottom: var(--sp-4); }
/* See the .sidebar-link.gate.show comment — same equal-specificity trap.
   .area-actions is only ever paired with .gate on notes.html's staff-only
   action row; without this, .gate.show's display:block (2 classes) beats
   .area-actions's display:flex (1 class) regardless of source order,
   stacking the row's children vertically instead of laying them out inline. */
.area-actions.gate.show { display: flex; }
.owner-hint { font-size: 13px; color: var(--muted); margin-left: auto; }
.mini-btn {
  font: inherit; font-size: 13px; padding: 6px 12px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  border-radius: var(--radius-sm); cursor: pointer;
}
.mini-btn:hover { border-color: var(--accent); }
.mini-btn.danger:hover { border-color: #b4453b; color: #b4453b; }
.editpanel { display: none; }
.editpanel.show { display: block; }
.erow { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.erow input, .erow select {
  font: inherit; font-size: 14px; padding: 7px 9px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff; color: var(--ink-soft);
}
.erow input:focus, .erow select:focus { outline: none; border-color: var(--accent); }
.add-row { margin-top: 6px; }
.editpanel h4 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 20px 0 10px; font-weight: 600; }
.check li .box { cursor: pointer; }

/* ---- publish form ---- */
.form-row { display: flex; flex-direction: column; gap: 6px; margin-bottom: 20px; }
.form-row label {
  font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
.form-row .field {
  font: inherit; font-size: 14px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: #fff; color: var(--ink-soft); width: 100%;
}
.form-row .field:focus { outline: none; border-color: var(--accent); }
.form-row textarea.field { resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }
.form-actions { display: flex; align-items: center; gap: 14px; }
.status-msg { font-size: 14px; min-height: 1.4em; }
.status-msg.ok { color: var(--accent); }
.status-msg.err { color: #b4453b; }
.publish-progress {
  display: block; width: 100%; margin-top: 14px;
  height: 8px; border-radius: 999px; border: 0; overflow: hidden;
}
.publish-progress[hidden] { display: none; }
.publish-progress::-webkit-progress-bar { background: var(--line); border-radius: 999px; }
.publish-progress::-webkit-progress-value { background: var(--accent); border-radius: 999px; transition: width .15s ease; }
.publish-progress::-moz-progress-bar { background: var(--accent); border-radius: 999px; }

/* ---- financial dashboards ---- */
.dash-summary { color: var(--muted); font-size: 13px; margin: 0 0 var(--sp-4); }
.dash-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-4);
}
.dash-grid > .card + .card { margin-top: 0; }
@media (max-width: 640px) {
  .dash-grid { grid-template-columns: 1fr; }
}
.dash-card { padding: var(--sp-5); display: flex; flex-direction: column; }
/* Lighter than the shared .card h3 (18px/600) — these dashboard titles sit
   above a description line and a "Not yet published"/link row already
   doing the visual work, so a heavier heading here reads as more weight
   than the card needs. Same family/color, just smaller and thinner. */
.dash-card h3 { font-size: 15px; font-weight: 500; }
.dash-card p.dash-desc { color: var(--muted); font-size: 14px; margin: 0 0 var(--sp-3); }
.dash-meta { font-size: 13px; color: var(--muted); margin: 0 0 var(--sp-3); }
.dash-frame { width: 100%; min-height: 150px; margin: 0; border: 0; display: block; }
.dash-card--openable { cursor: pointer; transition: border-color .15s; }
.dash-card--openable:hover { border-color: var(--accent); }
.dash-card .open-dash-link { margin-top: auto; align-self: flex-start; }
@media (max-width: 640px) {
  .dash-card { padding: var(--sp-4); }
}

/* ---- portfolio ---- */
.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-4);
}
.portfolio-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-2);
  background: var(--paper);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: var(--sp-5);
  text-decoration: none;
  color: var(--ink-soft);
  box-shadow: var(--shadow-card);
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
.portfolio-tile:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: var(--shadow-card-hover); }
.portfolio-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.portfolio-tile h3 {
  font-family: var(--serif);
  font-weight: 500;
  color: var(--ink);
  margin: 0;
  font-size: 18px;
}
.detail .val--unset { color: var(--muted); font-style: italic; }
.portfolio-tile p { margin: 0; color: var(--muted); font-size: 13px; }
.entity-overview-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
  margin: 0 0 var(--sp-2); padding-top: var(--sp-4); border-top: 1px solid var(--line);
}
.entity-overview { color: var(--muted); font-size: 14px; margin: 0; }

/* ---- dashboard chrome for the standalone viewer page ---- */
/* Just the back button now (no title/"updated by" line) — a hard-capped
   40px strip rather than sizing to content, so nothing (a bigger font, an
   inherited line-height, extra text wrapping) can ever grow it: height,
   min-height, and max-height are all pinned to the same value, and overflow
   is clipped as a last-resort backstop. align-items:center places the link
   in the middle of that fixed strip instead of relying on padding to
   position it. */
.dash-chrome-bar {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  height: 40px;
  min-height: 40px;
  max-height: 40px;
  box-sizing: border-box;
  overflow: hidden;
  margin: 0;
  padding: 0 12px;
  line-height: 1;
  border-bottom: 1px solid var(--line);
  flex: none; background: var(--bg-page);
}
/* .backlink's own margin-bottom is meant for it sitting above a page-head
   elsewhere in the app — inside this fixed-height bar it would just get
   clipped by the overflow:hidden above rather than pushing the bar taller,
   but zeroing it here keeps the link visibly centered instead of nudged up.
   line-height is set again here directly (not just relying on inheriting it
   from .dash-chrome-bar) so the link's own text box can't end up taller than
   intended regardless of what wraps it. Slightly heavier weight and the
   brand green (not the default .backlink ink-soft) so it reads as the one
   deliberate action in an otherwise empty bar. */
.dash-chrome-bar .backlink { margin: 0; line-height: 1; font-weight: 600; color: var(--accent); }
/* No margin of its own, and no assumption that the bar above it is any
   particular height — flex:1 in .dash-view's column layout means this
   simply fills whatever space the (now-fixed-height) bar doesn't take, so it
   starts immediately below the bar with no gap. */
.dash-chrome-content { flex: 1; overflow: auto; background: #fff; margin: 0; min-height: 0; }

/* ---- standalone dashboard viewer page ---- */
.dash-view-body { margin: 0; height: 100vh; overflow: hidden; }
.dash-view { display: flex; flex-direction: column; height: 100vh; }
/* The extra height is real device safe-area (notch/status bar) on an
   installed PWA, not slack — on a normal browser tab --safe-top is 0 and
   this reduces to the plain 40px above. Overriding all three of height/
   min-height/max-height again here (not just adding padding) keeps the same
   hard cap in place rather than reopening a route to unbounded growth. */
.dash-view .dash-chrome-bar {
  height: calc(40px + var(--safe-top));
  min-height: calc(40px + var(--safe-top));
  max-height: calc(40px + var(--safe-top));
}

/* ---- team notes ---- */
.notes-panel { margin-bottom: 20px; }
.notes-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-2); flex-wrap: wrap;
}
/* A thin green mark ahead of the label is the shared "section header" cue —
   a bit more weight and a deliberate accent instead of a plain bold label. */
.notes-panel-head h2 {
  font-family: var(--serif); font-weight: 600; color: var(--ink); margin: 0; font-size: 20px;
  display: flex; align-items: center; gap: 10px;
}
.notes-panel-head h2::before {
  content: ""; display: inline-block; flex: none;
  width: 3px; height: 16px; border-radius: 2px; background: var(--accent);
}

.note-feed { display: flex; flex-direction: column; gap: var(--sp-2); }
.note-card {
  background: var(--paper); border: 1px solid var(--card-border); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--shadow-card);
}
.note-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin-bottom: var(--sp-2); flex-wrap: wrap; }
.note-tags { display: flex; gap: var(--sp-1); flex-wrap: wrap; }
.note-actions { display: flex; gap: var(--sp-2); }
.note-title { font-family: var(--serif); font-weight: 500; color: var(--ink); font-size: 17px; margin: 0 0 4px; overflow-wrap: break-word; }
.note-body { color: var(--ink-soft); font-size: 14px; margin: 0 0 var(--sp-2); white-space: pre-wrap; overflow-wrap: break-word; }
.note-meta { color: var(--muted); font-size: 12px; margin: 0; }

.note-edit-form .form-row { margin-bottom: var(--sp-3); }
.note-edit-form .form-actions { margin-top: var(--sp-2); }

/* ---- home command center ---- */
/* Compact action-center header: greeting + one context line on the left,
   search/notifications/add-update/profile controls on the right — replaces
   the old three-line eyebrow+h1+lede+stats stack to cut vertical space. */
.page-head--action {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; margin: 0 0 var(--sp-4);
}
.page-head-main h1 { margin: 0; }
.home-context { color: var(--muted); font-size: 13px; margin: 4px 0 0; }
.page-head-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* The topbar's own [data-session] chip already covers mobile (the sidebar,
   and this header control row's copy of it, are desktop-only concerns) —
   showing both here and in the topbar at once would just duplicate the
   same user chip on small screens. */
.page-head-controls .session { display: none; }
@media (min-width: 641px) {
  .page-head-controls .session { display: flex; }
}
.global-search {
  display: flex; align-items: center; gap: 6px; min-width: 200px;
  padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper); position: relative;
}
.global-search-icon { display: flex; flex: none; color: var(--muted); }
.global-search-icon svg { width: 15px; height: 15px; display: block; }
.global-search-input {
  border: 0; outline: 0; background: transparent; flex: 1; min-width: 0;
  font: inherit; font-size: 13px; color: var(--ink-soft);
}
@media (max-width: 700px) {
  .page-head--action { flex-direction: column; align-items: stretch; }
  .global-search { order: 3; width: 100%; }
}

/* ---- global search results dropdown ---- */
.search-results {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 40;
  width: max-content; min-width: 100%; max-width: min(420px, 90vw);
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-shadow: 0 8px 24px rgba(20, 30, 50, 0.14);
  padding: var(--sp-2) 0; max-height: 360px; overflow-y: auto;
}
.search-results-group + .search-results-group {
  border-top: 1px solid var(--line); margin-top: 4px; padding-top: 4px;
}
.search-results-group-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin: 0; padding: 4px 14px;
}
.search-result-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  width: 100%; font: inherit; text-align: left; font-size: 14px; color: var(--ink-soft);
  padding: 7px 14px; border: none; background: transparent; cursor: pointer;
}
.search-result-row:hover, .search-result-row:focus-visible { background: var(--card); color: var(--ink); outline: none; }
.search-result-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result-meta { flex: none; font-size: 12px; color: var(--muted); }
.search-empty { padding: 10px 14px; font-size: 13px; color: var(--muted); margin: 0; }

/* Two sections (Notifications / Office Notes), each full width, stacked
   with a flex column + gap. */
.home-sections { display: flex; flex-direction: column; gap: var(--sp-3); }

/* A single slim line of text, no border or fill — the honest-empty-state
   and loading-state recipe for both home sections. Deliberately not
   .placeholder-note (that reads as its own dashed empty card, which is
   exactly the "large empty box" this is meant to avoid inside a section
   that already has its own card chrome). */
.slim-note { margin: 0; font-size: 13px; color: var(--muted); }

/* ---- Notifications ---- */
.pill-row { display: flex; flex-wrap: wrap; gap: 8px; }
.notif-pill {
  display: inline-flex; align-items: center; gap: 6px; max-width: 240px;
  font: inherit; font-size: 13px; padding: 7px 12px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper); color: var(--ink-soft); cursor: pointer;
}
.notif-pill:hover { border-color: var(--accent); }
.notif-pill-icon { flex: none; width: 14px; height: 14px; display: flex; }
.notif-pill-icon svg { width: 100%; height: 100%; display: block; }
.notif-pill-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notif-pill-status {
  flex: none; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; opacity: 0.75;
}
/* Needs your response: the same red already used for destructive actions
   (.mini-btn.danger) elsewhere — this is the one pill state that means "you
   have to do something." Sent-by-you-and-still-open gets the calmer slate
   tint instead of that same red, or the brand green — both would misread as
   either "urgent" or "just another button" rather than "worth a glance,
   nothing to do yet". Once resolved, either direction fades to the same
   neutral, muted pill. */
.notif-pill--needs-you { border-color: rgba(180, 69, 59, 0.35); background: rgba(180, 69, 59, 0.06); color: var(--ink); font-weight: 600; }
.notif-pill--needs-you .notif-pill-icon { color: #b4453b; }
.notif-pill--sent { border-color: var(--slate); background: var(--slate-soft); color: var(--ink); font-weight: 600; }
.notif-pill--sent .notif-pill-icon { color: var(--slate); }
.notif-pill--done { opacity: 0.55; }
.notif-pill--more { font-weight: 600; }

/* Status badge inside the detail popup's meta line — same red/green/neutral
   vocabulary as the pill modifiers above, just inline text rather than a
   whole pill. */
.notif-status-badge { font-weight: 700; }
.notif-status-badge--open { color: var(--ink-soft); }
.notif-status-badge--approved { color: var(--accent); }
.notif-status-badge--declined { color: #b4453b; }

.notif-list { display: flex; flex-direction: column; gap: 2px; margin-top: var(--sp-3); }
.notif-list-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  font: inherit; text-align: left; font-size: 14px; color: var(--ink-soft);
  padding: 8px 4px; border: none; border-bottom: 1px solid var(--line);
  background: transparent; cursor: pointer;
}
.notif-list-row:last-child { border-bottom: 0; }
.notif-list-row:hover { color: var(--ink); }
.notif-list-row--done { opacity: 0.55; }
.notif-list-row-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notif-list-row-status { flex: none; font-size: 12px; color: var(--muted); }
/* .notif-list-row is also used as a real <a href> on Home's What's New
   card (clicking navigates, rather than opening a popup) — resets the
   browser's default link underline/color to match the button variant. */
a.notif-list-row { text-decoration: none; }
.whats-new-meta { color: var(--muted); font-weight: 400; }

/* ---- Office Notes ----
   Same pill-and-popup mechanic as Notifications above, deliberately plainer
   so the two read as distinct: a flat neutral fill regardless of state (no
   status to color-code — a note is just a note, not open/resolved), no bold
   weight, no icon color beyond muted. Informational reading, not an action
   item. */
.note-pill {
  display: inline-flex; align-items: center; gap: 6px; max-width: 240px;
  font: inherit; font-size: 13px; padding: 7px 12px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--ink-soft); cursor: pointer;
}
.note-pill:hover { border-color: var(--accent); background: var(--paper); }
.note-pill .note-pill-icon { flex: none; width: 14px; height: 14px; display: flex; color: var(--muted); }
.note-pill .note-pill-icon svg { width: 100%; height: 100%; display: block; }
.note-pill-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-pill--more { font-weight: 600; }

/* ---- modal (notification detail/respond, "+N more" list, compose) ---- */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(19, 33, 62, 0.45);
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-4);
}
.modal-backdrop[hidden] { display: none; }
.modal {
  position: relative; background: var(--paper); border-radius: var(--radius);
  box-shadow: 0 24px 64px -16px rgba(19, 33, 62, 0.35);
  width: 100%; max-width: 480px; max-height: calc(100vh - 64px); overflow-y: auto;
  padding: var(--sp-5);
}
.modal-close {
  position: absolute; top: var(--sp-3); right: var(--sp-3);
  width: 30px; height: 30px; border-radius: 50%; border: none;
  background: transparent; color: var(--muted); font-size: 20px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.modal-close:hover { background: var(--card); color: var(--ink); }
.modal-title { font-family: var(--serif); font-weight: 500; font-size: 20px; color: var(--ink); margin: 0 0 4px; padding-right: 30px; }
.modal-meta { font-size: 13px; color: var(--muted); margin: 0 0 var(--sp-3); }
/* Office Notes' popup puts owner/date last (headline, summary, next step,
   *then* owner · date) rather than up top under the title like a
   notification's sender/due line, so this flips the margin accordingly. */
.modal-meta--footer { margin: var(--sp-3) 0 0; }
.modal-detail-text { font-size: 14px; color: var(--ink-soft); white-space: pre-wrap; overflow-wrap: break-word; margin: 0 0 var(--sp-4); }
.modal-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: var(--sp-3); }
.modal-status-line { font-size: 13px; color: var(--muted); margin: 0 0 var(--sp-3); }
.modal-comment { border-top: 1px solid var(--line); padding-top: var(--sp-3); margin-top: var(--sp-3); }
.modal-comment textarea {
  font: inherit; font-size: 14px; width: 100%; min-height: 60px; resize: vertical;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: #fff; color: var(--ink-soft); margin-bottom: 8px;
}
.modal-comment textarea:focus { outline: none; border-color: var(--accent); }
.modal-history { border-top: 1px solid var(--line); margin-top: var(--sp-3); padding-top: var(--sp-3); display: flex; flex-direction: column; gap: 8px; }
.modal-history-item { font-size: 13px; color: var(--ink-soft); margin: 0; }
.modal-history-item b { color: var(--ink); }
.modal-history-time { color: var(--muted); font-size: 12px; }
/* The publish form's textarea.field is monospace (it pastes raw HTML) — the
   notification compose form's Detail field is plain prose, so this scoped
   override (3 classes, beats that 2-class rule regardless of source order)
   resets it back to the app's normal text font within the modal only. */
.modal .form-row textarea.field { font-family: inherit; font-size: 14px; }

/* ---- schedule ---- */
.day-heading {
  font-family: var(--serif); font-weight: 600; color: var(--ink);
  font-size: 16px; margin: var(--sp-4) 0 var(--sp-2);
  display: flex; align-items: center; gap: 10px;
}
.day-heading::before {
  content: ""; display: inline-block; flex: none;
  width: 3px; height: 14px; border-radius: 2px; background: var(--accent);
}
.day-heading:first-child { margin-top: 0; }

/* ---- calendar (Outlook-style month grid) ---- */
.cal-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-4); flex-wrap: wrap; }
.cal-toolbar-nav { display: flex; align-items: center; gap: var(--sp-2); }
.cal-week-arrow {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.cal-week-arrow:hover { border-color: var(--accent); color: var(--accent); }
.cal-week-arrow svg { width: 16px; height: 16px; }
.cal-month-label { font-family: var(--serif); font-weight: 600; font-size: 20px; color: var(--ink); margin: 0; }

.cal-month-weekdays {
  display: grid; grid-template-columns: repeat(7, 1fr);
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted);
  padding: 0 2px 8px;
}
.cal-month-weekdays span { text-align: center; }

/* The 1px grid gap painted in --line, with every cell on --paper, draws
   crisp thin gridlines without per-cell border rules doubling up at shared
   edges. */
.cal-month-days {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden;
}
.cal-day-cell {
  background: var(--paper); min-height: 108px; padding: 6px; cursor: pointer;
  display: flex; flex-direction: column; gap: 4px;
}
.cal-day-cell:hover { background: var(--card); }
.cal-day-num { font-family: var(--serif); font-size: 14px; font-weight: 600; color: var(--ink); }
/* Today gets a filled accent circle around its number (not just a tinted
   cell) so it still reads clearly once the cell is full of pills. */
.cal-day-cell.is-today { background: var(--accent-soft); }
.cal-day-cell.is-today:hover { background: var(--accent-soft); }
.cal-day-cell.is-today .cal-day-num {
  color: #fff; background: var(--accent); border-radius: 50%; font-size: 12px;
  width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
}
.cal-day-cell.is-outside { background: var(--bg-page); }
.cal-day-cell.is-outside:hover { background: var(--card); }
.cal-day-cell.is-outside .cal-day-num { color: var(--muted); font-weight: 400; }

.cal-day-events { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Category-colored, same soft-background/saturated-text recipe as the .tag
   family — a pill reads as "this category" at a glance, not decoration. */
.cal-pill {
  display: block; width: 100%; font: inherit; font-size: 11.5px; text-align: left;
  padding: 2px 6px; border: 0; border-radius: 5px; cursor: pointer;
  background: var(--card); color: var(--ink-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-pill--household { background: var(--slate-soft); color: var(--slate); }
.cal-pill--travel { background: #f6ead9; color: #9a6a2c; }
.cal-pill--vendor { background: var(--accent-soft); color: var(--accent); }
.cal-pill--family { background: #f3e6ea; color: #96475f; }
.cal-pill--staff { background: var(--navy-soft); color: var(--ink); }
.cal-day-more {
  font: inherit; font-size: 11.5px; text-align: left; padding: 2px 6px; border: 0;
  background: transparent; color: var(--muted); font-weight: 600; cursor: pointer;
}
.cal-day-more:hover { color: var(--accent); }

/* Mobile fallback (see the breakpoint below): a plain day-by-day agenda —
   the 7-column grid gets illegibly cramped under ~640px. */
.cal-month-agenda { display: none; }
@media (max-width: 640px) {
  .cal-month-grid { display: none; }
  .cal-month-agenda { display: block; }
  .cal-agenda-card { cursor: pointer; }
}

/* ---- calendar (Notes tab: property checklists) ---- */
.cal-property-group-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin: 0 0 var(--sp-2);
}
.cal-property-group-label:not(:first-child) { margin-top: var(--sp-4); }
.cal-property-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-2); }
.cal-property-chip {
  font: inherit; font-size: 14px; padding: 7px 16px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper); color: var(--ink-soft); cursor: pointer;
}
.cal-property-chip:hover { border-color: var(--accent); }
.cal-property-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }

.cal-checklists-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin: var(--sp-5) 0 var(--sp-3); }
.cal-checklists-head h3 { font-family: var(--serif); font-weight: 600; font-size: 17px; color: var(--ink); margin: 0; }

/* Checkable item lines inside the checklist detail modal. Native checkbox
   tinted via accent-color rather than a custom-built control — plenty
   premium enough for a short, frequently-clicked list, without the extra
   markup/JS a fully custom checkbox would need. */
.cl-item-row { display: flex; align-items: center; gap: 10px; padding: 8px 2px; border-bottom: 1px solid var(--line); }
.cl-item-row:last-child { border-bottom: 0; }
.cl-item-label { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; cursor: pointer; }
.cl-item-checkbox { flex: none; width: 18px; height: 18px; accent-color: var(--accent); cursor: pointer; }
.cl-item-text { font-size: 14px; color: var(--ink-soft); overflow-wrap: anywhere; }
.cl-item-row.is-checked .cl-item-text { color: var(--muted); text-decoration: line-through; }
.cl-item-actions { flex: none; display: flex; gap: 4px; }
.cl-item-actions button {
  width: 26px; height: 26px; border-radius: 50%; border: none;
  background: transparent; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.cl-item-actions button:hover { background: var(--card); color: var(--ink); }
.cl-item-actions svg { width: 14px; height: 14px; }
.cl-item-edit-row { display: flex; gap: 8px; padding: 8px 2px; align-items: center; }
.cl-item-edit-form { display: flex; gap: 8px; flex: 1 1 auto; }
.cl-item-edit-row input.field { flex: 1 1 auto; }
.cl-add-item-form { display: flex; gap: 8px; margin-top: var(--sp-3); }
.cl-add-item-form input.field { flex: 1 1 auto; }
.cl-checklist-progress { font-size: 13px; color: var(--muted); margin: 0 0 var(--sp-3); }

/* .form-row .field-checkbox (two classes) is needed, not .field-checkbox
   alone, to outrank .form-row label's own (one class + the `label` type
   selector) specificity — otherwise this nested checkbox label would keep
   the uppercase/bold styling meant for the row's main field label. */
.form-row .field-checkbox {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px;
  font-size: 13px; font-weight: 400; text-transform: none; letter-spacing: normal;
  color: var(--ink-soft);
}

/* ---- property (residences / boats) ---- */
.segmented {
  display: inline-flex; gap: 2px; padding: 3px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); margin-bottom: var(--sp-5);
}
.segmented-btn {
  font: inherit; font-size: 14px; padding: 7px 18px;
  border: 0; background: transparent; color: var(--muted);
  border-radius: 999px; cursor: pointer;
}
.segmented-btn.active { background: var(--paper); color: var(--ink); font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }

.last-updated { font-size: 13px; color: var(--muted); margin: 0 0 var(--sp-4); }

.detail-block { padding: var(--sp-3) 0; border-bottom: 1px solid var(--line); }
.detail-block:last-child { border-bottom: 0; }
.detail-block-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); margin: 0 0 6px;
}
.detail-block-val { margin: 0; white-space: pre-wrap; font-size: 14px; }
.detail-block-val.val--unset { color: var(--muted); font-style: italic; }

.field-hint { font-size: 12px; color: var(--muted); margin: -2px 0 0; font-weight: 400; }

/* ---- library (document repository: categories + files-as-pills) ---- */
.lib-actions-row { display: flex; justify-content: flex-end; margin: var(--sp-3) 0; }

.lib-pills { display: flex; flex-wrap: wrap; gap: 10px; margin-top: var(--sp-2); }
.lib-pill {
  display: inline-flex; align-items: center; gap: 8px; max-width: 320px;
  padding: 6px 10px 6px 6px; background: var(--paper); border: 1px solid var(--line);
  border-radius: 999px; cursor: pointer; transition: border-color .15s, box-shadow .15s;
}
.lib-pill:hover, .lib-pill:focus-visible { border-color: var(--accent); box-shadow: 0 2px 8px rgba(20, 30, 50, .1); }
.lib-pill--busy { opacity: 0.55; pointer-events: none; }
.lib-pill-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.lib-pill-name {
  font-size: 13px; font-weight: 500; color: var(--ink); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 210px;
}
.lib-pill-meta { font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lib-pill-delete {
  background: none; border: none; color: var(--muted); cursor: pointer;
  width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; flex: none; margin-left: 2px;
}
.lib-pill-delete svg { width: 12px; height: 12px; display: block; }
.lib-pill-delete:hover { color: #b4453b; background: #fdeceb; }

.lib-icon {
  width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; flex: none;
}
.lib-icon svg { width: 14px; height: 14px; display: block; }
.lib-icon--pdf { background: #fdeceb; color: #b4453b; }
.lib-icon--doc { background: #e7f0fa; color: #2f6fb0; }
.lib-icon--xls { background: var(--accent-soft); color: var(--accent); }
.lib-icon--ppt { background: #fdecd9; color: #c1691b; }
.lib-icon--image { background: #f1e9fb; color: #7c4dbd; }
.lib-icon--archive { background: var(--card); color: var(--ink-soft); }
.lib-icon--generic { background: var(--card); color: var(--muted); }

@media (max-width: 480px) {
  .lib-pill { max-width: none; width: 100%; }
  .lib-pill-name { max-width: none; }
}

/* ---- mobile: comfortable tap targets, no dead corners ---- */
@media (max-width: 640px) {
  .btn, .mini-btn { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
  .form-row .field { min-height: 44px; }
  .erow input, .erow select { min-height: 40px; }
  .check li { padding: 10px 0; }
  .check .box { width: 22px; height: 22px; }
  .card, .note-card { padding: var(--sp-3); }
}
