:root {
  /* Regal Emerald theme — white base, royal blue primary accent, green secondary accent, black text */
  --bg: #ffffff;
  --surface: #f5f7fb;
  --surface-2: #e8ecf5;
  --border: #d7dce6;
  --text: #14161a;
  --muted: #5b6472;

  --royal: #2f3fa8;
  --royal-dark: #24337f;
  --green: #1f8a57;
  --green-dark: #186b43;

  /* Skyline Blue gradient — sampled directly from the nav destination
     artwork (static/icons/nav/*.png, e.g. calendar.png's diagonal reads
     roughly #29d6ff at the top to #0057e7 at the bottom) so every
     gradient button/bar in the app visually matches those icons instead
     of introducing a third, unrelated blue. Applied to the clear
     "buttons and bars" surfaces (primary buttons, active segmented
     toggles, progress/funnel bars) — confirmed with the user off a
     screenshot of the five nav icons ("I want this colour theme to be
     applied to all buttons and bars"). Left alone: `--royal` itself
     (still used as plain text/border/status color in dozens of places a
     gradient can't apply to, e.g. Calendar's Call-Back event color) and
     the mobile tab bar's active-pill highlight (a translucent white
     pill over the already-gradient nav art, not a flat color surface).
  */
  --grad-sky: #29d6ff;
  --grad-royal: #0057e7;
  --btn-gradient: linear-gradient(135deg, var(--grad-sky), var(--grad-royal));

  --danger: #c0392b;
  --danger-dark: #a5301f;
  --success: #1f8a57;
  --radius: 14px;

  /* Soft elevation instead of a hard border for surface panels — the flat
     1px-border look reads as a form/spreadsheet; a faint shadow reads as a
     card floating slightly above the page, which is the more "premium
     software" cue. Border stays, just softer, since some hairline definition
     still helps on plain white. */
  --shadow-card: 0 1px 2px rgba(20, 22, 26, 0.04), 0 8px 24px -16px rgba(20, 22, 26, 0.12);
  --shadow-float: 0 10px 30px -8px rgba(20, 22, 26, 0.22), 0 2px 8px rgba(20, 22, 26, 0.08);

  --font-heading: "Poppins", Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
}

* { box-sizing: border-box; }

/* Mobile browsers auto-boost font sizes on their own text-inflation
   heuristic when a page looks "hard to read" on a small screen — it can
   scale text well past the rem/px values actually set in CSS, and does it
   silently, with no way to see it from a desktop browser at a narrow
   width (that heuristic is real-device/viewport-specific, not just a
   width media query). This is what kept overflowing fixed-width label
   columns elsewhere in the app no matter how small the CSS said the text
   should be — opting out here makes every element render at exactly its
   specified size on every device. */
/* -1px off the browser's 16px default — confirmed with the user: reduce
   the overall app's font size (and, since nearly everything else in this
   file sizes itself in rem, every icon along with it) by one notch. The
   mobile tab bar is the one deliberate exception (see .tabbar/.tab-btn/
   .tab-logo-img below, converted to fixed px so they render at exactly
   the same physical size regardless of this root value) — confirmed with
   the user: "except the menu bar." */
/* This app has no dark theme of its own anywhere — every color in this
   file is a hardcoded light value. Without an explicit color-scheme,
   though, a phone's own system dark mode still reaches in and re-skins
   *native* form UI (a <select>'s dropdown list, a date/time input's
   picker) to dark colors on its own, since those aren't drawn from this
   stylesheet at all — confirmed with the user off a screenshot of a
   black dropdown list on Android ("I dont want the black default phone
   background... I want it be white like in the desktop following our
   theme"). `color-scheme: light` tells the browser this page is
   light-only, so it renders those native widgets with light chrome
   regardless of the OS/browser's own theme setting. */
html { font-size: 15px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; color-scheme: light; }

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
}

h1, h2, h3, h4, .btn, .tab-btn, .nav-item, .status-pill, input, select, textarea, button, th {
  font-family: var(--font-heading);
}

.hidden { display: none !important; }
.muted { color: var(--muted); font-size: 0.85rem; }

/* ---------- Card elevation ---------- */
/* One shared rule for every "panel" surface in the app (dashboard cards,
   lead cards, kanban columns, filter/stat/report/info panels, the leads
   table, modals) rather than repeating box-shadow on each of their
   individual rules below — swaps the previous flat-border-only look for a
   soft floating-card look throughout. */
.dash-card,
.lead-card,
.kanban-column,
.stat-card,
.report-card,
.info-panel,
.contacts-panel,
.pipeline-panel,
.comments-panel,
.leads-filter-panel,
.leads-table-wrap,
.insights-card,
.modal-content,
.mini-row,
.contact-row,
.bulk-scan-card-row {
  box-shadow: var(--shadow-card);
}

/* ---------- App shell / sidebar ---------- */
.app-shell { display: flex; min-height: 100vh; }

/* `position: sticky; top: 0; height: 100vh` — a real bug in the previous
   round's fix, caught from screenshots of a long/scrolled page (Clients'
   table, Insights): `.sidebar` isn't sticky, so without an explicit
   height it just stretches to match `.main-col`'s own height (a plain
   flex row, `align-items: stretch` by default) — on a page taller than
   the viewport, that's however tall the *page* is, not the *screen*.
   `justify-content: center` was then centering the logo+nav block within
   that whole page-height column, so its on-screen position silently
   depended on how long the current page was and how far it was scrolled
   — the exact "nav ends up somewhere near the bottom of the visible
   area, or half off-screen" bug the screenshots showed, not the fixed
   "sits nicely down from the top" look that was intended. Pinning the
   sidebar to the viewport with `position: sticky` fixes the root cause
   (its own height is now always exactly 100vh, regardless of the main
   column's height), so centering within it is centering within the
   actual visible screen, consistent no matter how long the page is or
   how far it's scrolled. `overflow-y: auto` is a safety net for a
   viewport too short to fit the logo + 5 nav items at all, not something
   expected to engage on a normal desktop window. */
.sidebar {
  width: 220px;
  flex-shrink: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
/* Now a <button> (was a plain div) — confirmed with the user: on desktop
   this is the only logo that should be clickable to Home, so the
   topbar's own separate logo button is hidden on desktop entirely (see
   #topbar-logo-btn below) rather than the two duplicating each other.
   No border-bottom any more — confirmed with the user off a reference
   screenshot of another app's sidebar ("I dont want that seperation is
   there between the logo and the menu bar"): the logo and the nav list
   below it now read as one continuous block instead of two sections
   split by a visible line. */
.sidebar-header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.1rem 1rem;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
}
/* Both +10% — confirmed with the user ("make the logo size and font
   size larger by 10%"): 2rem/2.4rem -> 2.2rem/2.64rem for the icon, and
   .logo-text gets an explicit 1.1rem (it previously had no font-size of
   its own at all, just inheriting the plain 1rem body default). */
.sidebar-logo-img { height: 2.2rem; width: auto; max-width: 2.64rem; display: block; flex-shrink: 0; }
.logo-text { font-weight: 600; letter-spacing: 0.02em; font-size: 1.1rem; }

/* Slightly more breathing room between items and a touch more side
   padding than before — confirmed with the user off the same reference
   screenshot ("look spaced out it is are more center aligned in the
   menu column, I want you do the same"), matching that reference's
   airier, more evenly-padded nav list rather than the tighter default. */
.sidebar-nav { display: flex; flex-direction: column; padding: 0.75rem 0.65rem; gap: 0.3rem; }
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.65rem 0.9rem;
  border-radius: var(--radius);
  background: none;
  border: none;
  color: var(--muted);
  font-size: 0.92rem;
  text-align: left;
  cursor: pointer;
}
.nav-item .icon { width: 1.1rem; text-align: center; }
.nav-icon-img { width: 1.1rem; height: 1.1rem; object-fit: contain; flex-shrink: 0; }
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item.active { background: var(--surface-2); color: var(--royal); font-weight: 600; }

.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: calc(0.85rem + env(safe-area-inset-top)) 1.25rem 0.85rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
/* Logo (left, links home) — actions (right). No page-title heading here
   any more (confirmed with the user: it just repeated the nav selection
   that's already visible, e.g. "Leads" showing on top of the Leads page
   for no reason) — removed entirely rather than just hidden, so there's
   no stray centered gap left behind. */
.topbar-logo-btn { flex-shrink: 0; max-width: 2.4rem; overflow: hidden; background: none; border: none; padding: 0; margin: 0; cursor: pointer; display: flex; align-items: center; }
/* Desktop has the sidebar's own clickable logo (.sidebar-header, same
   Home destination) right next to this one — confirmed with the user
   ("remove this second logo"), a second logo in the topbar was pure
   duplication there. Mobile has no sidebar at all, so this stays as the
   only persistent logo/Home-tap target outside the tab bar. */
@media (min-width: 900px) {
  .topbar-logo-btn { display: none; }
}
.topbar-logo-img { height: 1.9rem; width: auto; max-width: 100%; display: block; }
/* margin-left: auto (not just relying on .topbar's justify-content:
   space-between) — with .topbar-logo-btn hidden on desktop (see above),
   .topbar-actions became the ONLY flex child of .topbar there, and
   space-between has nothing left to space between, so it fell back to
   sitting flush left instead of at the right edge where it belongs.
   margin-left: auto pushes it to the right regardless of how many
   siblings it has, so it's correct on both desktop (one sibling, hidden)
   and mobile (two siblings, logo + actions) without a media query. */
.topbar-actions { display: flex; align-items: center; gap: 0.6rem; flex-shrink: 0; margin-left: auto; }
/* Full-width dropdown search panel — replaces the old always-visible
   .topbar-search input (which was also always hidden below 900px, so
   mobile had no topbar search at all) with a single icon button that
   opens this panel on every screen size. Confirmed with the user: "a
   drop down that cover[s] the width of the page appears where you can
   type in a search and also recent search history." Positioned
   `absolute` relative to `.topbar` (already a positioned element via
   `position: sticky`, so this needs no separate JS-computed offset the
   way the old mobile account-menu did) at `top: 100%`, so it sits flush
   below the topbar regardless of the topbar's own height changing with
   `env(safe-area-inset-top)` on notched phones. */
.global-search-panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-float);
  padding: 1rem 1.5rem;
  z-index: 25;
}
.global-search-input { font-size: 1rem; }
.recent-searches { margin-top: 0.75rem; }
.recent-searches-label { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin-bottom: 0.4rem; }
.recent-search-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  cursor: pointer;
  font-size: 0.9rem;
}
.recent-search-row:hover { background: var(--surface-2); }
.recent-search-row-remove { flex-shrink: 0; background: none; border: none; color: var(--muted); cursor: pointer; padding: 0.2rem 0.4rem; font-size: 0.9rem; }
/* .btn-primary's width:100% is defined later in this file and would
   otherwise win at equal specificity — .btn.topbar-create-icon (two
   classes) outranks it regardless of source order. */
.btn.topbar-create-icon {
  width: auto;
  flex-shrink: 0;
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1;
  padding: 0.45rem 0.75rem;
}
.topbar-install { width: auto; white-space: nowrap; flex-shrink: 0; }

/* Full width, hugging the sidebar — no centering gap. A hard max-width cap
   made wide desktop screens look boxed-in with dead space on both sides. */
#app { max-width: 1800px; margin: 0; padding: 1.25rem 1.5rem; width: 100%; padding-bottom: 5rem; }

.view-header h2 { margin: 0 0 0.25rem; }
.view-header { margin-bottom: 0.9rem; }
.leads-view-header,
.dash-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.leads-view-header h2,
.dash-header h2 { margin: 0; }
.leads-view-header .btn,
.dash-header .btn { width: auto; flex-shrink: 0; }
.dash-header p { margin: 0.2rem 0 0; }
.leads-header-actions { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.leads-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.leads-select-btn { width: auto; flex-shrink: 0; display: none; }
.leads-select-btn.force-visible { display: inline-block; }
.leads-select-btn.active { background: var(--btn-gradient); color: #ffffff; }
.leads-bulk-actions { display: flex; gap: 0.5rem; flex: 1 1 auto; min-width: 0; }
.leads-bulk-actions .btn { flex: 1; white-space: nowrap; }
@media (max-width: 480px) {
  .leads-bulk-actions .btn { font-size: 0.85rem; padding: 0.6rem 0.5rem; }
}

/* inline-flex, not flex — a plain flex container is still a block box
   that stretches to its parent's full width by default. That was fine
   for the Leads List/Kanban toggle (already nested inside a flex row
   alongside the Select button, which constrains it), but the newer
   Insights/Calendar segmented toggles sit directly inside .view-header
   with nothing else beside them, so the bar stretched all the way across
   the page with visible empty space/border past the buttons. inline-flex
   sizes the whole bar to its buttons' actual content, everywhere this
   class is used. */
.view-toggle { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.view-toggle-btn {
  background: var(--surface);
  border: none;
  color: var(--muted);
  padding: 0.5rem 0.9rem;
  font-size: 0.85rem;
  font-family: var(--font-heading);
  cursor: pointer;
}
.view-toggle-btn + .view-toggle-btn { border-left: 1px solid var(--border); }
.view-toggle-btn.active { background: var(--btn-gradient); color: #ffffff; }

/* ---------- Mobile bottom tab bar (hidden on desktop) ---------- */
/* A floating rounded bar with margin on all sides, rather than a flat strip
   flush with the screen edges — reads as a lifted, tappable object instead
   of a docked toolbar, closer to the premium-app reference the bar was
   redesigned against. */
/* ~30% opaque + a lighter backdrop blur — confirmed with the user off a
   screenshot of the Leads page ("the other tabs become too blurry cant
   read it clearly, can we make the bar more transparent so that the
   other pages are more visible but not too much"), turned down slightly
   from the original 40%/14px so more of whatever's scrolling underneath
   shows through the glass, without going fully see-through. rgba here is
   var(--bg) (#ffffff) at 0.32 alpha — kept literal since CSS variables
   can't be alpha-blended directly without also defining an RGB-triplet
   custom property purely for this one spot. */
/* Every measurement here is a fixed px value, not rem — deliberately, so
   the mobile tab bar stays exactly the same physical size regardless of
   html's root font-size (see the app-wide -1px reduction above);
   confirmed with the user as the one thing that should NOT shrink along
   with everything else. Values are the old 1rem=16px equivalents of what
   this used to be in rem, kept identical. */
.tabbar {
  display: none;
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, 0.32);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 24px;
  box-shadow: var(--shadow-float);
  padding: 6.4px;
  z-index: 10;
  /* Forces its own GPU compositing layer — without this, a fixed element
     can visibly flicker/vanish for a frame on long scrolls on Android
     Chrome, most noticeable on the app's taller scrolling views. */
  transform: translateZ(0);
  will-change: transform;
  backface-visibility: hidden;
}
/* Each button now shows a full logo image (icon + the tab's name baked
   into the graphic itself, e.g. "leads-full.png") instead of a bare icon
   with no visible label — confirmed with the user: these should be "the
   exact logos with the names as written in the logo." No separate <span>
   text needed since the image already carries it. This replaces the
   earlier icon-only design (five equal buttons now, no raised center
   button — that treatment existed to compensate for the old icon-only
   bar's ambiguity, which a self-labeled logo no longer has). */
.tab-btn {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: 17.6px;
  color: var(--muted);
  padding: 6.4px 2.4px;
  cursor: pointer;
  transition: background 0.12s ease, filter 0.15s ease, opacity 0.15s ease;
}
.tab-logo-img { height: 37.6px; width: auto; max-width: 100%; object-fit: contain; }
.tab-btn.active { background: rgba(255, 255, 255, 0.55); filter: none; opacity: 1; }
/* Confirmed with the user: the active tab's white pill highlight alone
   didn't read clearly enough against the bar's own frosted-glass
   background (see .tabbar above) to tell which page you're on. Dimming
   every *other* button instead makes the active one stand out by
   contrast — still fully tappable (opacity is purely visual, it doesn't
   affect hit-testing), just visually receded. No blur any more — a
   follow-up report ("other tabs become too blurry cant read it
   clearly") found the blur(1px) this originally shipped with made the
   other icons themselves illegible, not just muted; opacity alone still
   reads as clearly de-emphasized without that. */
.tab-btn:not(.active) { opacity: 0.6; }

@media (max-width: 899px) {
  .sidebar { display: none; }
  .tabbar { display: flex; }
  #app { padding: 0.85rem 0.75rem 6.25rem; }
  .topbar { padding-left: 1rem; padding-right: 1rem; }

  /* Select toggle only makes sense where the checkbox gutter was actually
     costing screen width — the mobile card list, not the desktop table. */
  .leads-toggle-row { width: 100%; }
  .leads-select-btn { display: inline-block; }
}

/* ---------- Buttons / inputs ---------- */
.btn {
  border: none;
  border-radius: var(--radius);
  padding: 0.6rem 1rem;
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  transition: box-shadow 0.15s ease, background 0.15s ease, transform 0.1s ease;
  /* .btn is also used on plain <a> tags (the call/WhatsApp quick-action
     links) — without this they'd render with a default link underline. */
  text-decoration: none;
  display: inline-block;
  text-align: center;
}
.btn-primary {
  background: var(--btn-gradient);
  color: #ffffff;
  width: 100%;
  box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--grad-royal) 55%, transparent);
}
.btn-primary:active { background: var(--royal-dark); transform: scale(0.98); }
.btn-ghost { background: transparent; color: var(--text); }
.btn-icon { background: var(--surface-2); font-size: 1.1rem; padding: 0.4rem 0.6rem; }
.btn-danger { background: var(--danger); color: #ffffff; }
.btn-danger:active { background: var(--danger-dark); }
.btn-secondary { background: var(--surface-2); color: var(--text); }
.btn-secondary:active { background: var(--border); }

.filters { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
.filter-row { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }

/* Day Planner's Max-meetings/Start-date row: the date input previously
   inherited the global input{width:100%} rule with nothing to stop it, so
   it wrapped onto its own line below its label while the number input (with
   an inline width override) stayed inline — same label pattern, two
   different layouts. Both fields now get an explicit width and sit inline
   with their label, on every screen size. */
.plan-filter-row { gap: 1.5rem; margin-bottom: 1.25rem; }
.plan-filter-row label { display: flex; align-items: center; gap: 0.6rem; }
.plan-filter-row input[type="number"] { width: 4.5rem; }
#plan-candidates { margin-bottom: 1.25rem; }
#build-plan-btn { margin-bottom: 1.25rem; }
.plan-filter-row input[type="date"] { width: auto; }
input, select, textarea {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius);
  padding: 0.55rem 0.7rem;
  font-size: 0.95rem;
  width: 100%;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--royal); }
/* A read-only value shown where an editable input/select usually sits
   (e.g. Lead Detail's "Date captured," immutable after creation — see
   CLAUDE.md) — same padding/sizing as a real input so it still lines up
   with every other Pipeline field, just no border/background to signal
   it isn't one. display:block + width:100% deliberately mirrors the
   global input/select rule's own width:100% (a <span> isn't targeted by
   that rule the way the <input> this replaced was) — without it, this
   compact inline span no longer forces its own line break the way the
   old input accidentally did, and its following sibling label ("Scheduled
   date/time") crowds onto the same line instead of wrapping below it. */
.field-static { display: block; width: 100%; padding: 0.55rem 0; font-size: 0.95rem; color: var(--text); }
/* Strips the native magnifying-glass icon some mobile browsers draw
   inside a type="search" input by default (Search filter field, the
   topbar's global search) — confirmed with the user ("remove logos in
   front of search"). The clear/"x" button some browsers also draw is
   left alone; it's genuinely useful, unlike the decorative icon. */
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[type="search"]::-webkit-search-decoration { display: none; }
.checkbox-label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; width: auto; }
.hot-toggle-text-mobile { display: none; }
.checkbox-label input { width: auto; accent-color: var(--green); }
/* Bare checkboxes outside a form context (row selectors, plan candidates) must
   never inherit the global input width:100% — that stretch is what made the
   day-planner checkboxes look centered in an empty row. */
input[type="checkbox"] { width: auto; accent-color: var(--royal); }

/* ---------- Lead cards (Home / Day Planner) ---------- */
.lead-list { display: flex; flex-direction: column; gap: 0.6rem; }
.lead-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 0.9rem;
  cursor: pointer;
}
.lead-card:hover, .lead-card:active { background: var(--surface-2); }
.lead-card-top { display: flex; justify-content: space-between; align-items: start; gap: 0.5rem; }
.lead-card-name { font-weight: 600; font-family: var(--font-heading); }
.lead-card-name .hot-dot,
.mini-row-name .hot-dot,
.plan-call-row-name .hot-dot { color: var(--green); margin-right: 0.3rem; }
.lead-card-sub { color: var(--muted); font-size: 0.85rem; margin-top: 0.15rem; }
.lead-card-actions { display: flex; gap: 0.4rem; margin-top: 0.6rem; }
.lead-card-actions .btn-icon { padding: 0.3rem 0.55rem; font-size: 0.95rem; }
.empty-state { color: var(--muted); text-align: center; padding: 2rem 1rem; }

.status-pill {
  font-size: 0.72rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--surface-2);
  white-space: nowrap;
}
.status-pill.won { background: #e3f5ea; color: var(--green-dark); }
.status-pill.lost { background: #fbeae8; color: var(--danger-dark); }
.status-pill.fresh { background: #e8ebf9; color: var(--royal); }

/* The Leads table's Status column is a real <select> styled to still read
   as a pill — confirmed with the user ("the status field I should be to
   edit it directly there... it should show all other status") — so
   clicking it opens every other status inline, right there in the row,
   instead of requiring a trip into Lead Detail first. select.status-pill
   beats the global input/select rule's border/width/padding on
   specificity alone (a class beats a bare element selector), so only the
   chrome-specific bits (border, background arrow) need overriding here. */
select.status-pill {
  border: none; width: auto; cursor: pointer; padding-right: 1.35rem;
  -webkit-appearance: none; appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 0.65rem) center, calc(100% - 0.4rem) center;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
  opacity: 0.92;
}
select.status-pill:focus { outline: none; box-shadow: 0 0 0 2px var(--royal); }

/* ---------- Home dashboard ---------- */
.alert-item {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  border-radius: var(--radius);
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.5rem;
  font-size: 0.9rem;
}
.alert-item.urgent { background: #fbeae8; color: var(--danger-dark); border: 1px solid #f0c4be; }
.alert-item.info { background: #e8ebf9; color: var(--royal-dark); border: 1px solid #c7cdef; }
.alert-item.clickable { cursor: pointer; }
.alert-item.clickable:hover { filter: brightness(0.97); }
.alerts-ok { color: var(--muted); font-size: 0.9rem; margin-bottom: 0.75rem; }

.stat-card.stat-alert .stat-value { color: var(--danger); }
.stat-card.stat-gold .stat-value { color: var(--green-dark); }
.stat-card.clickable { cursor: pointer; }
.stat-card.clickable:hover { background: var(--surface-2); }

.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1rem;
  margin-top: 1.25rem;
}
.dash-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  transition: box-shadow 0.3s, border-color 0.3s;
}
.dash-card.flash-highlight { border-color: var(--green); box-shadow: 0 0 0 2px var(--green-dark); }
/* Same flash treatment for a Kanban column jumped to via jumpToKanban()
   (Fresh/Untouched-leads stat cards and the Untouched alert, all now
   landing on Kanban instead of the flat Leads list — see app.js). */
.kanban-column.flash-highlight { border-color: var(--green); box-shadow: 0 0 0 2px var(--green-dark); }
.dash-card-wide { grid-column: span 2; }
@media (max-width: 899px) { .dash-card-wide { grid-column: span 1; } }

.dash-card-header { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.75rem; }
.dash-card-header h3 { margin: 0; font-size: 0.95rem; }
.dash-subhead { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 1rem 0 0.5rem; }
/* Today's-actions and Hot-leads are the two cards whose lists genuinely
   grow unbounded (every missed call/meeting, every going-cold hot lead —
   unlike Fresh leads, capped at 8 server-side, or Product interest, a
   fixed handful of categories). Relying on CSS Grid's default row-stretch
   to keep this pair visually even broke down in practice: with enough
   missed items Today's actions ran to 900px+ while Hot leads, sharing the
   same row, still only had ~300px of real content — confirmed with the
   user off a screenshot where the untouched card's box visibly stopped
   short instead of stretching to match. A *fixed* height sidesteps that
   entirely instead of debugging why stretch wasn't taking effect: both
   cards get the same literal `max-height` regardless of content, so
   they're always the same "window" size, with the header staying put and
   only the list body below it scrolling once content overflows. Desktop
   only (matches `.dash-card-wide`'s own 900px breakpoint just above) —
   confirmed with the user ("in the desktop version"); on a single mobile
   column there's no sibling card to visually match, so the list is left
   to grow naturally rather than nesting a nested-scroll region inside a
   page that's already scrolling. */
@media (min-width: 900px) {
  .dash-card-scroll { max-height: 420px; overflow-y: auto; padding-right: 0.35rem; }
}

.mini-list { display: flex; flex-direction: column; gap: 0.4rem; }
.mini-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.5rem 0.65rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  font-size: 0.87rem;
}
.mini-row:hover { border-color: var(--royal); }
/* flex: 1 makes this the one child that actually grows/shrinks to fill
   the row — without it, .mini-row-actions/.mini-row-tag's horizontal
   position tracked the name/sub text's own length (longer text pushed
   them further right than shorter text on the row above/below), so
   every row's icon cluster landed at a different x-position instead of
   lining up in a shared column. min-width: 0 is still required alongside
   flex: 1 for the ellipsis truncation below to actually kick in — a flex
   item's min-width defaults to its content size otherwise, which the
   ellipsis rule can never shrink past. */
.mini-row-main { min-width: 0; flex: 1 1 auto; }
.mini-row-name { font-family: var(--font-heading); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mini-row-sub { color: var(--muted); font-size: 0.78rem; margin-top: 0.1rem; }
.mini-row-tag { font-size: 0.72rem; white-space: nowrap; flex-shrink: 0; }
.mini-row-actions { display: flex; gap: 0.3rem; flex-shrink: 0; }
.mini-row-actions .btn-icon { padding: 0.25rem 0.4rem; font-size: 0.82rem; }
.tag-overdue { color: var(--danger); }
.tag-time { color: var(--royal); }
.mini-empty { color: var(--muted); font-size: 0.85rem; padding: 0.4rem 0; }

.bar-row { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.45rem; font-size: 0.85rem; }
.bar-label { width: 130px; flex-shrink: 0; color: var(--muted); }
.bar-track { flex: 1; background: var(--surface-2); border-radius: 999px; height: 1.1rem; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 999px; background: var(--btn-gradient); }
.bar-count { width: 2rem; text-align: right; font-family: var(--font-heading); }

.category-row { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.55rem; font-size: 0.87rem; padding: 0.2rem; border-radius: 6px; }
.category-row.clickable { cursor: pointer; }
.category-row.clickable:hover { background: var(--surface-2); }
.category-name { width: 130px; flex-shrink: 0; font-family: var(--font-heading); }
.category-meta { color: var(--muted); font-size: 0.78rem; }

.callback-banner {
  background: #e3f5ea;
  color: var(--green-dark);
  border: 1px solid #b7e2c9;
  border-radius: var(--radius);
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.9rem;
  font-size: 0.9rem;
}

/* ---------- Leads table view ---------- */
.leads-layout { display: flex; gap: 1.25rem; align-items: flex-start; }
.leads-filter-panel {
  width: 220px;
  flex-shrink: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.leads-filter-panel h3 { margin: 0; font-size: 0.95rem; }
.filter-field { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.8rem; color: var(--muted); }

/* Clients filter panel only — two quick toggles just below the search
   box, confirmed with the user ("add Upcoming Re-orders and Upcoming
   Payments button just below the search bar"). Stacked, not side by
   side, so each button's full label stays readable at the filter
   panel's own 220px width instead of wrapping mid-word. */
.clients-upcoming-buttons { display: flex; flex-direction: column; gap: 0.4rem; }
.clients-upcoming-btn { width: 100%; font-size: 0.82rem; padding: 0.5rem 0.6rem; }
.clients-upcoming-btn.active { background: var(--btn-gradient); color: #ffffff; }

/* Clients list/detail — see CLAUDE.md's "Clients" section. A lighter page
   than Leads (no sidebar filter panel), so its own search box needs an
   explicit cap or it stretches the full page width on desktop. */
.clients-search-field { max-width: 26rem; }
.client-detail-heading { margin: 0; }
.order-card { border-top: 3px solid var(--royal); }
.order-card + .order-card { margin-top: 0.9rem; }
/* Mobile Clients card row's Outstanding figure — confirmed with the user
   ("mark it light red"), a lighter tint of --danger rather than the same
   strong red used for delete buttons/overdue tags, since this is just a
   number to notice, not a warning to act on immediately. */
.outstanding-amount { color: #d9736a; font-weight: 500; }

.leads-table-wrap {
  flex: 1;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow-x: auto;
}
/* Fixed layout + explicit column widths (via <colgroup> in the markup) so
   long company names can't force the table wider than its container — that
   was why several columns were falling off the right edge of the screen. */
.data-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 0.88rem; }
.data-table th {
  text-align: left;
  padding: 0.65rem 0.9rem;
  border-bottom: 1px solid var(--border);
  color: var(--muted);
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.data-table td {
  padding: 0.65rem 0.9rem;
  border-bottom: 1px solid var(--border);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.data-table tbody tr { cursor: pointer; }
.data-table tbody tr:hover { background: var(--surface-2); }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table td.lead-name-cell { font-weight: 600; font-family: var(--font-heading); white-space: normal; }
.data-table th.checkbox-col, .data-table td.checkbox-col { width: 2.25rem; overflow: visible; }
.data-table td.checkbox-col { cursor: default; }

.col-checkbox { width: 2.25rem; }
/* Every other column has NO width of its own: in a fixed-layout table the
   columns left without one split the remaining space equally, which gives
   evenly spaced, aligned columns (header and cells share the same padding). */
.data-table td.lead-phone-cell { overflow: visible; text-overflow: clip; white-space: normal; }   /* a single number never breaks mid-digit */
.data-table td.lead-phone-cell.multi { overflow-wrap: anywhere; }   /* a field holding two numbers wraps instead of running into the next column */

/* A data table just doesn't work at phone width — fighting column
   percentages to fit 5+ columns into ~350px hit real browser table-layout
   quirks (a hidden column's <col> still reserves its width in a fixed
   layout, and table-layout:auto instead overflows the viewport needing
   horizontal scroll). Below 640px, show the same card-list pattern Home
   and Day Planner already use instead of a table at all. */
.leads-mobile-list { display: none; }
@media (max-width: 640px) {
  .leads-table-wrap table { display: none; }
  .leads-mobile-list { display: flex; }
}

.bulk-toolbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  padding: 0.6rem 0.9rem;
}
/* Above the Kanban board the toolbar stands on its own, so it gets its own frame. */
.bulk-toolbar-board { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 0.75rem; }
.bulk-count { font-family: var(--font-heading); font-weight: 600; color: var(--royal); white-space: nowrap; }
.bulk-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.bulk-actions select { width: auto; min-width: 130px; }
.bulk-actions .btn, .bulk-toolbar > .btn, .bulk-commit .btn { width: auto; }
/* Clear / Delete / the one Apply sit together in the corner. */
.bulk-commit { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }

@media (max-width: 899px) {
  /* .leads-layout keeps align-items:flex-start from its row-mode base rule
     even once flex-direction flips to column here — that makes every child
     shrink to its own content width instead of stretching, which is why the
     filter panel needed an explicit width:100% below. leads-table-wrap (and
     everything inside it: the mobile card list, each lead card) needed the
     exact same override and never got it — that's the actual reason lead
     cards stayed narrower than the filter panel above them. */
  .leads-layout { flex-direction: column; }
  .leads-filter-panel { width: 100%; flex-direction: column; align-items: stretch; gap: 0.6rem; }
  .leads-table-wrap { width: 100%; }
  .filter-field {
    flex-direction: row; align-items: center; justify-content: space-between;
    gap: 0.75rem; min-width: 0;
  }
  .filter-field select, .filter-field input {
    width: auto; flex: 1 1 60%; max-width: 62%; font-size: 0.85rem;
  }

  /* "Hot only" reads as a checkbox from across the room on a phone — a
     bright toggle chip is easier to hit and to notice at a glance. */
  .hot-toggle {
    justify-content: center; border: 1px solid var(--border); border-radius: 999px;
    padding: 0.6rem 1rem; font-weight: 700; color: var(--danger); gap: 0.4rem;
  }
  .hot-toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
  .hot-toggle:has(input:checked) { background: var(--danger); color: #fff; border-color: var(--danger); }
  .hot-toggle-text-desktop { display: none; }
  .hot-toggle-text-mobile { display: inline; }

  /* Lead list rows: smaller, top-aligned checkbox so the lead card itself
     (name/company/status) reads as the primary content, not a co-equal column. */
  #leads-mobile-list .plan-lead-row { align-items: flex-start; gap: 0.5rem; }
  #leads-mobile-list .plan-lead-checkbox { margin-top: 0.2rem; width: 0.95rem; height: 0.95rem; }
  #leads-mobile-list .lead-card { padding: 0.9rem 1rem; }
  #leads-mobile-list .lead-card-name { font-size: 1.05rem; }
  #leads-mobile-list .lead-card-sub { font-size: 0.9rem; }

  /* Checkbox hidden until "Select" is toggled on — a row's card then reads
     full-width, matching the Filter Leads panel's own edges, instead of
     permanently reserving a checkbox gutter most visits never use. */
  #leads-mobile-list .plan-lead-checkbox { display: none; }
  #leads-mobile-list.select-mode .plan-lead-checkbox { display: block; }

  /* Pipeline: every field on one line (fixed-width label so it never wraps,
     the control taking the rest) — a fixed label width means every
     select/input ends up the same size regardless of its own label length. */
  .pipeline-panel .form-row { flex-direction: column; gap: 0.5rem; }
  .pipeline-panel label:not(.checkbox-label) {
    display: flex; flex-direction: row; align-items: center; gap: 0.6rem;
  }
  .pipeline-panel label:not(.checkbox-label) .field-label {
    flex: 0 0 9.6rem; white-space: nowrap; font-size: 0.78rem;
  }
  .pipeline-panel label:not(.checkbox-label) > select,
  .pipeline-panel label:not(.checkbox-label) > input {
    width: auto; flex: 1 1 auto; min-width: 0; font-size: 0.85rem;
  }

  /* Bulk toolbar on a phone: the four selections two per row, and the
     Clear / Delete / Apply buttons on one row underneath. */
  .bulk-actions select { flex: 1 1 calc(50% - 0.25rem); min-width: 0; }
  .bulk-commit { width: 100%; justify-content: space-between; }

  /* Day Planner's Max-meetings/Start-date row: each label previously sized
     itself to its own text ("Max meetings/day" vs "Start date"), so the two
     inputs that followed started at two different x-positions once the row
     wrapped onto separate lines. Same fixed-width label-column treatment as
     Pipeline/Card builder above — both labels the same width, both inputs
     sharing one left edge. */
  .plan-filter-row { flex-direction: column; align-items: stretch; gap: 0.5rem; }
  .plan-filter-row label { flex-direction: row; align-items: center; gap: 0.6rem; }
  .plan-filter-row .field-label { flex: 0 0 9.6rem; white-space: nowrap; font-size: 0.78rem; }
  .plan-filter-row input { flex: 1 1 auto; width: auto; min-width: 0; }
}

/* ---------- Kanban board ---------- */
.kanban-board {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 0.5rem;
  align-items: flex-start;
}
.kanban-column {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  width: 270px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 220px);
}
.kanban-column.drag-over { border-color: var(--royal); box-shadow: 0 0 0 2px var(--royal); }
.kanban-column-header {
  padding: 0.75rem 0.9rem;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.85rem;
  flex-shrink: 0;
}
.kanban-column-count {
  background: var(--surface-2);
  color: var(--muted);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  font-size: 0.75rem;
  margin-left: auto;
}
/* Both checkboxes (column "select all" and each card's own) are hidden
   until "Select" is toggled on, same reasoning and the same
   .lead-row-checkbox class the List view's mobile cards already use —
   confirmed with the user ("in Kanban view when I click on select, the
   selection box doesnt appear... kindly add that"), Select mode simply
   didn't do anything to Kanban's DOM before this. #leads-select-mode-btn
   is desktop-hidden by default (the table's own checkbox column is
   always visible there, so Select never had anything to reveal) — see
   applyLeadsViewMode()'s new .force-visible toggle in app.js for why
   Kanban needs it shown on desktop too, unlike the List view. */
.kanban-column-select-all { display: none; flex-shrink: 0; }
#leads-kanban-mode.select-mode .kanban-column-select-all { display: block; }
.kanban-cards { padding: 0.6rem; display: flex; flex-direction: column; gap: 0.6rem; overflow-y: auto; }
.kanban-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.6rem 0.7rem;
  cursor: grab;
  font-size: 0.85rem;
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}
.kanban-card:active { cursor: grabbing; }
.kanban-card.dragging { opacity: 0.4; }
.kanban-card:hover { border-color: var(--royal); }
.kanban-card-checkbox { display: none; flex-shrink: 0; margin-top: 0.2rem; }
#leads-kanban-mode.select-mode .kanban-card-checkbox { display: block; }
.kanban-card-body { flex: 1; min-width: 0; }
.kanban-card-name { font-family: var(--font-heading); font-weight: 600; }
.kanban-card-sub { color: var(--muted); font-size: 0.78rem; margin-top: 0.15rem; }
.kanban-card-tags { display: flex; gap: 0.35rem; margin-top: 0.4rem; flex-wrap: wrap; }
.kanban-empty { color: var(--muted); font-size: 0.8rem; padding: 0.5rem; text-align: center; }

/* ---------- Reports ---------- */
.report-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1rem; }
.report-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; }
.webhook-example {
  background: var(--surface-2);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  font-size: 0.85rem;
  overflow-x: auto;
  margin: 0.85rem 0;
}
.report-card h3 { margin: 0 0 0.6rem; font-size: 0.95rem; }
.report-card .data-table td:last-child, .report-card .data-table th:last-child { text-align: right; }

/* ---------- Analytics ---------- */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.75rem; margin-bottom: 1.5rem; }
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; }
.stat-card .stat-value { font-family: var(--font-heading); font-size: 1.7rem; font-weight: 700; color: var(--royal); }
.stat-card .stat-label { color: var(--muted); font-size: 0.8rem; margin-top: 0.25rem; }

.funnel-title { font-size: 0.95rem; margin: 0 0 0.75rem; }
.funnel-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.55rem; }
.funnel-label { width: 150px; flex-shrink: 0; font-size: 0.85rem; color: var(--muted); }
.funnel-bar-track { flex: 1; background: var(--surface-2); border-radius: 999px; height: 1.3rem; overflow: hidden; }
.funnel-bar-fill { height: 100%; background: var(--btn-gradient); border-radius: 999px; }
.funnel-count { width: 2.4rem; text-align: right; font-size: 0.85rem; font-family: var(--font-heading); }

.insights-card {
  margin-top: 1.75rem;
  background: var(--surface);
  border: 1px solid var(--green-dark);
  border-radius: var(--radius);
  padding: 1rem;
}
.insights-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.6rem; }
.insights-header h3 { margin: 0; font-size: 0.95rem; color: var(--green-dark); }
.insights-header .btn { width: auto; }
.insights-list { display: flex; flex-direction: column; gap: 0.5rem; padding: 0; margin: 0; list-style: none; }
.insights-list li {
  font-size: 0.9rem;
  padding-left: 1rem;
  position: relative;
}
.insights-list li::before {
  content: "✦";
  color: var(--green);
  position: absolute;
  left: 0;
}

/* ---------- Import CSV modal ---------- */
.import-mapping-table { margin: 0.75rem 0; }
.import-summary { display: flex; gap: 1.5rem; margin: 0.75rem 0; }
.import-summary .stat-card { flex: 1; padding: 0.75rem; }
.import-sample-wrap { max-height: 220px; overflow-y: auto; margin-bottom: 1rem; }

/* ---------- New/edit lead modal ---------- */
.upload-box {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: 2.5rem 1rem;
  text-align: center;
  cursor: pointer;
  color: var(--muted);
}
.upload-box input { display: none; }

.capture-optional {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 0.75rem;
  margin-bottom: 1rem;
}
.capture-optional-heading { margin: 0 0 0.5rem; font-size: 0.88rem; }
.capture-buttons-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.capture-buttons-row + .capture-buttons-row { margin-top: 0.6rem; }
.capture-buttons-row .btn { padding: 0.45rem 0.8rem; font-size: 0.85rem; }
.capture-buttons-row-label { width: 100%; font-size: 0.82rem; color: var(--muted); }
.upload-box-inline {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  color: var(--muted);
  font-size: 0.88rem;
}
.upload-box-inline input { display: none; }

.lead-form { display: flex; flex-direction: column; gap: 0.6rem; }
.lead-form label { font-size: 0.85rem; color: var(--muted); display: flex; flex-direction: column; gap: 0.25rem; }
/* .lead-form label's own flex-direction: column (a plain class+element
   selector, specificity 0,1,1) beats .checkbox-label's flex-direction:
   row intent (specificity 0,1,0) wherever the two land on the same
   <label> — inside the Manage Tiers "Add a tier"/inline-edit forms this
   put the checkbox above its own wrapped label text instead of beside it
   (confirmed with the user off a screenshot: "make it in one line...
   like the tick is to the left"). This explicit override (specificity
   0,2,1) restores the row layout specifically for a .checkbox-label
   inside a .lead-form, without touching .checkbox-label's own base rule
   (still correct everywhere it's used outside a .lead-form, e.g. the Day
   Planner's "Hot leads only" filter). */
.lead-form label.checkbox-label { flex-direction: row; align-items: center; gap: 0.4rem; }
.form-row { display: flex; gap: 0.6rem; }
/* min-width:0 overrides the flex-item default of min-width:auto, which
   otherwise refuses to shrink a column below its content's natural width
   (a long placeholder like "e.g. IIJS 2026, Instagram Ad") — that's what
   was pushing the New Lead modal wider than the viewport on phones. */
.form-row > label { flex: 1; min-width: 0; }
.form-actions { display: flex; gap: 0.6rem; margin-top: 0.5rem; }
.form-actions .btn { flex: 1; }

.comment-modal-textarea { width: 100%; resize: vertical; }
#schedule-modal-input { width: 100%; }

.modal {
  position: fixed;
  inset: 0;
  background: rgba(20, 22, 26, 0.55);
  z-index: 20;
  display: flex;
  align-items: flex-end;
}
.modal-content {
  background: var(--bg);
  width: 100%;
  max-height: 92vh;
  overflow-y: auto;
  border-radius: 1rem 1rem 0 0;
  padding: 1rem;
  position: relative;
  padding-bottom: 2rem;
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 24px rgba(20, 22, 26, 0.15);
}
.modal-close { position: absolute; top: 0.5rem; right: 0.5rem; }

.comment-list { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.5rem; }
.comment-item { background: var(--surface); border-radius: var(--radius); padding: 0.5rem 0.7rem; font-size: 0.9rem; }
.comment-item time { color: var(--muted); font-size: 0.75rem; display: block; margin-top: 0.2rem; }

/* Lead Story: a chronological timeline of everything logged against this
   lead (creation + every comment, including the auto-prefixed "[Status →
   Won] ..." entries the comment-gating system already writes) — confirmed
   with the user as "a history of the lead ... so can use it for
   generating reports and analytics." Deliberately built from data that
   already exists (Lead.created_at + Comment rows) rather than a new
   backend event-log model. */
.lead-story-list { display: flex; flex-direction: column; gap: 0; margin-top: 0.25rem; }
.lead-story-item { display: flex; gap: 0.7rem; padding: 0.45rem 0; position: relative; }
.lead-story-item::before {
  content: ""; position: absolute; left: 0.2rem; top: 1.4rem; bottom: -0.45rem;
  width: 1px; background: var(--border);
}
.lead-story-item:last-child::before { display: none; }
.lead-story-dot {
  flex: 0 0 auto; width: 0.5rem; height: 0.5rem; border-radius: 50%;
  margin-top: 0.35rem; background: var(--muted);
}
.lead-story-item.lead-story-created .lead-story-dot { background: var(--green); }
.lead-story-item.lead-story-change .lead-story-dot { background: var(--royal); }
.lead-story-body { flex: 1 1 auto; min-width: 0; }
.lead-story-text { font-size: 0.9rem; }
.lead-story-body time { color: var(--muted); font-size: 0.75rem; display: block; margin-top: 0.1rem; }

.install-banner {
  position: fixed;
  bottom: 5rem;
  left: 0.75rem;
  right: 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--green-dark);
  border-radius: var(--radius);
  padding: 0.75rem;
  z-index: 15;
  font-size: 0.85rem;
  box-shadow: 0 4px 12px rgba(20, 22, 26, 0.18);
}
.install-banner p { margin: 0 0 0.5rem; }
.install-banner strong { color: var(--green-dark); }

.toast {
  position: fixed;
  bottom: 6rem;
  left: 50%;
  transform: translateX(-50%);
  background: var(--text);
  color: var(--bg);
  padding: 0.6rem 1rem;
  border-radius: var(--radius);
  z-index: 30;
  font-size: 0.9rem;
  box-shadow: 0 4px 12px rgba(20, 22, 26, 0.2);
}

/* ---------- Lead detail page ---------- */
.lead-detail-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.lead-detail-title { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap; }
.lead-detail-title h2 { margin: 0; }

.info-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  margin-bottom: 1rem;
}
.info-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.8rem; }
.info-item { display: flex; flex-direction: column; gap: 0.2rem; }
.info-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.info-value { font-size: 0.95rem; }

@media (max-width: 899px) {
  /* City/Geocoding/Source share one horizontal line (each still a
     compact label-above-value pair) instead of three stacked full-width
     rows — tuned for exactly these 3 items; Address and Capture method
     were deliberately dropped from this view (still editable via "Edit
     info") rather than adding a 4th/5th squeezed-in column here. This has
     to come after the base .info-grid rule above — same specificity, so
     source order decides, and the media query alone doesn't win ties. */
  .info-grid { display: flex; flex-direction: row; gap: 0.5rem; }
  .info-item { flex: 1; flex-direction: column; align-items: flex-start; gap: 0.15rem; min-width: 0; }
  .info-label { font-size: 0.62rem; }
  .info-value { font-size: 0.85rem; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100%; }
}

.form-section-heading { font-size: 0.85rem; color: var(--muted); margin: 0.4rem 0 0; text-transform: uppercase; letter-spacing: 0.04em; }
.form-section-note { margin: 0; }

.contact-entry {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 0.6rem;
  margin-bottom: 0.5rem;
}
#add-contact-entry-btn { width: auto; }

.contacts-panel,
.pipeline-panel,
.comments-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  margin-bottom: 1rem;
}
.contacts-panel h3,
.pipeline-panel h3,
.comments-panel h3 { margin: 0 0 0.75rem; font-size: 0.95rem; color: var(--royal); }
.pipeline-panel > .form-row,
.pipeline-panel > label { margin-bottom: 0.6rem; }

.pipeline-panel-header,
.custom-form-section-header { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.75rem; }
.pipeline-panel-header h3,
.custom-form-section-header h3 { margin: 0; }
.pipeline-panel-header .checkbox-label { margin: 0; }
/* Custom Lead Form page: every row-level action (Edit, + Add field,
   + Add status) uses .btn-primary for the Skyline Blue gradient — but
   .btn-primary defaults to width:100% (a mobile-first "fill the row"
   button style meant for a form's own submit button, not a small inline
   action). Scoped overrides keep both usages correct without touching
   .btn-primary's own base rule, which plenty of real full-width submit
   buttons elsewhere still rely on. */
.custom-form-section-header .btn-primary,
.contact-row-actions .btn-primary,
.pipeline-panel-header .btn-primary { width: auto; }
.field-visibility-select { width: auto; }
/* A .custom-form-section-header ("Statuses" / "Additional fields") sits
   right after Product Interest's own row with no gap between them —
   reported by the user off two screenshots ("the add status button
   spacing is not great... same thing with the add status button" on
   both mobile and desktop) — margin-bottom alone (above) doesn't help
   when the *previous* sibling is a plain .contact-row with no margin of
   its own. Harmless as a first-child too (Customer info/Contacts), so
   this is unconditional rather than scoped to a "not first child" case. */
.custom-form-section-header { margin-top: 1rem; }
/* Status rows: the color swatch (a plain inline-block <span>, styled via
   .calendar-legend-swatch — that class's own flex-shrink:0 assumes a
   flex parent it doesn't have here) defaults to baseline vertical-align
   next to the status name text, reported by the user as visibly
   misaligned. Centers it on the text's line box instead. */
.contact-row-name .calendar-legend-swatch { vertical-align: middle; margin-right: 0.3rem; }
/* Customer info / Contacts / Additional fields / Statuses' rows —
   reported by the user as looking sparse one per row on desktop ("just
   one field in one row looks bad, add two in each row", then "status
   too i wanted 2 in one row for desktop" as a follow-up once Statuses
   itself turned out not to have been included the first time). Scoped
   to this specific list type (a dedicated class, not the shared
   .contact-list) so Product-interest-options' own list and every other
   .contact-list usage elsewhere in the app (Lead Detail's real Contacts
   panel, etc.) are unaffected. Mobile keeps the existing single-column
   stack — .contact-list's own flex-direction:column. */
@media (min-width: 900px) {
  /* Compound selector (.contact-list.custom-form-field-list), not just
     .custom-form-field-list alone — .contact-list's own base rule
     (display:flex, defined later in this file) ties on specificity with
     a single-class selector, and source order would let it win; the
     compound form's higher specificity (0,2,0 vs 0,1,0) wins regardless
     of where either rule sits in the file. */
  .contact-list.custom-form-field-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
  }
}

.contact-list { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.75rem; }
/* A status hidden from the New Lead/Lead Detail picker (models.py's
   Status.is_hidden) — dimmed rather than labeled, matching this page's
   "no extra text" treatment elsewhere (see the Custom Lead Form polish
   note in CLAUDE.md). */
.contact-row.is-hidden-row { opacity: 0.5; }
.contact-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 0.75rem;
  flex-wrap: wrap;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.6rem 0.8rem;
}
/* A real min-width (not 0) is what makes flex-wrap actually trigger below —
   with min-width:0 the name column can shrink forever instead of ever
   wrapping the actions to their own line, which is what was squeezing
   names down to one letter per line once a 5th icon (Save contact) made
   the actions row too wide for narrow screens. */
.contact-row-main { flex: 1 1 140px; min-width: 140px; }
.contact-row-name { font-family: var(--font-heading); font-weight: 600; font-size: 0.92rem; overflow-wrap: break-word; }
.primary-contact-badge { color: #b8860b; }
.contact-row-sub { color: var(--muted); font-size: 0.82rem; margin-top: 0.15rem; overflow-wrap: break-word; }
.locality-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--surface-2);
  border-radius: 999px;
  padding: 0.15rem 0.5rem;
  margin: 0.15rem 0.3rem 0.15rem 0;
}
.locality-remove-btn { border: none; background: none; cursor: pointer; color: var(--muted); font-size: 0.85rem; padding: 0; }
.locality-remove-btn:hover { color: var(--danger); }
.contact-row-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; justify-content: flex-end; }
.contact-row-actions .btn-icon { padding: 0.3rem 0.5rem; font-size: 0.95rem; }
.contact-edit-form { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 0.75rem; }

.delete-lead-btn { width: auto; }

/* ---------- Day planner ---------- */
.plan-today-panel { margin-bottom: 1rem; }
.plan-today-panel h3 { margin: 0 0 0.75rem; font-size: 0.95rem; }

.plan-lead-row { display: flex; align-items: center; gap: 0.75rem; }
.plan-lead-checkbox { flex-shrink: 0; }
.plan-lead-card { flex: 1; min-width: 0; margin-bottom: 0; }

/* Day Planner's own compact call-row (planLeadRow() in app.js) — company,
   one contact, one Call button, nothing else. The left border color
   reuses calendarEventColorClass()'s exact status→class mapping (see
   .cal-event-* below) so a lead reads the same color here as it does on
   Calendar, just as a border accent instead of a filled chip. */
/* Padding/gap trimmed down from this row's original size — confirmed
   with the user off a screenshot ("I want all 3 written one single
   line! Make the font smaller!"), the "3" being the name, contact, and
   Call button, previously the name+contact stacked on two lines. */
.plan-call-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.45rem 0.7rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-left: 4px solid var(--muted);
  border-radius: 8px;
  cursor: pointer;
}
.plan-call-row:hover { border-color: var(--royal); border-left-color: inherit; }
/* Name + contact now sit on one line (were stacked) — a flex row with
   baseline alignment so the smaller contact text doesn't look vertically
   off against the name next to it. */
.plan-call-row-main {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  white-space: nowrap;
}
.plan-call-row-name { font-family: var(--font-heading); font-weight: 500; font-size: 0.85rem; flex-shrink: 0; max-width: 60%; overflow: hidden; text-overflow: ellipsis; }
/* The "·" separator lives here, not in the row's own HTML string, so an
   empty/missing contact ("No contact on file" is still real text, always
   shown) never needs special-casing to hide a dangling separator. */
.plan-call-row-contact { color: var(--muted); font-size: 0.76rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.plan-call-row-contact::before { content: "· "; }
/* width: auto — .btn-primary defaults to width: 100% (a mobile-first
   full-width block button), which inside this flex row made the button
   claim the entire row as its flex-basis and squeeze the name/contact
   text down to 0 width. Same opt-out .dash-header/.bulk-actions/
   .insights-header .btn already use for the same reason. */
.plan-call-btn { flex-shrink: 0; width: auto; padding: 0.32rem 0.6rem; font-size: 0.78rem; white-space: nowrap; }
.plan-call-row-nophone { flex-shrink: 0; font-size: 0.72rem; }
/* .cal-event-status carries its own `background` rule (filled chips,
   for actual Calendar events) — reusing the class name here is
   deliberate (same status→color mapping, see calendarEventColorClass()
   in app.js), but without resetting `background` back to the row's own
   var(--bg), that filled-chip background would apply here too and paint
   the whole row solid instead of just accenting its left border. */
.plan-call-row.cal-event-status { background: var(--bg); border-left-color: var(--cal-status-color, #5b6472); }

.plan-batch { margin-bottom: 1rem; }
.plan-batch h3 { margin: 0 0 0.4rem; font-size: 1rem; color: var(--royal); }

@media (min-width: 640px) {
  .modal { align-items: center; justify-content: center; }
  .modal-content { max-width: 480px; border-radius: 1rem; max-height: 85vh; }
  .modal-content.modal-content-wide { max-width: 640px; }
}

/* ---------- Bulk card scan ---------- */
.bulk-scan-list { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.75rem 0; max-height: 45vh; overflow-y: auto; }
.bulk-scan-card-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.65rem 0.85rem;
}
.bulk-scan-card-row.excluded { opacity: 0.5; }
.bulk-scan-card-company { font-family: var(--font-heading); font-weight: 600; }
.bulk-scan-card-sub { color: var(--muted); font-size: 0.82rem; margin-top: 0.1rem; }
.bulk-scan-exclude-btn { width: auto; flex-shrink: 0; }

/* ---------- Login screen ---------- */
.login-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  padding: 1.5rem;
}
.login-card {
  background: var(--bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: 2rem 1.75rem;
  width: 100%;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  text-align: center;
}
.login-logo-img { width: 56px; height: 56px; margin: 0 auto; object-fit: contain; }
.login-card h1 { margin: 0 0 0.4rem; font-size: 1.3rem; }
.login-card label { text-align: left; font-size: 0.85rem; color: var(--muted); display: flex; flex-direction: column; gap: 0.25rem; }
.login-error { color: var(--danger); font-size: 0.85rem; margin: 0; }

/* ---------- Account menu ---------- */
.account-menu-wrap { position: relative; }
.account-btn-img { width: 1.15rem; height: 1.15rem; object-fit: contain; display: block; }
.account-menu {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
  padding: 0.5rem;
  min-width: 200px;
  width: max-content;
  max-width: min(260px, calc(100vw - 2rem));
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.account-menu-email {
  font-size: 0.8rem;
  color: var(--muted);
  padding: 0.4rem 0.6rem 0.6rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.2rem;
  overflow-wrap: break-word;
}
.account-menu-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: none;
  border: none;
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  font-size: 0.9rem;
  white-space: nowrap;
  cursor: pointer;
  color: var(--text);
}
.account-menu-icon { width: 1.05rem; height: 1.05rem; object-fit: contain; flex-shrink: 0; }
.account-menu-item:hover { background: var(--surface-2); }

/* Below 640px the menu stays right-anchored under the account button
   (same shape as desktop) instead of stretching edge-to-edge across the
   whole screen — confirmed with the user off a screenshot ("this profile
   box looks too big, size it out properly and move to the right"), a
   direct reversal of the earlier full-width-pinned version (kept below
   only in the git history's memory, not as a comment — that version
   existed to stop "Enable notifications" wrapping onto two lines at the
   time, back when these menu items were emoji + longer text with no
   nowrap; the new icon-image + `white-space: nowrap` combination above
   fixes that same wrapping without needing the menu to span the screen).
   `position: fixed` is kept (not reverted to `absolute`) specifically so
   the existing JS-computed inline `top` (account-btn's click handler in
   app.js, tracking the topbar's actual bottom edge under
   env(safe-area-inset-top) on notched phones) keeps working unchanged —
   only `left`/`right`/width here changed. */
@media (max-width: 640px) {
  .account-menu {
    position: fixed;
    left: auto;
    right: 1rem;
    min-width: 0;
    width: max-content;
    max-width: calc(100vw - 2rem);
  }
}

/* ---------- Calendar ---------- */
/* Event color comes from the lead's status — every status has its own color
   (see statusCalendarColor() in app.js). The color is passed in inline as
   --cal-status-color, so one rule paints all of them. */
.cal-event-status { background: var(--cal-status-color, #5b6472); }
/* Client reorder-cadence/payment-due annotations (Month view only — see
   app.js's clientEventsOn()) — deliberately distinct from the 4 real
   scheduled-status colors above, since these are read-only projections,
   not something a rep can drag around. Reorder reuses a teal (a
   forward-looking "money coming in" cue, distinct from Meeting Fixed's
   green); payment reuses the same light red the Clients list's own
   Outstanding figure already uses, for one consistent "money owed" cue
   across the app. */
.cal-event-reorder { background: #0e9488; }
.cal-event-payment { background: #d9736a; }

.calendar-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.calendar-nav-row { display: flex; align-items: center; gap: 0.5rem; }
.calendar-range-label { margin: 0 0 0 0.4rem; font-size: 1.05rem; white-space: nowrap; }

/* Day/3-day/Week time-grid */
.calendar-time-grid {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg);
}
.calendar-day-headers-row { display: flex; border-bottom: 1px solid var(--border); }
/* 2.5rem (down from 3rem) — confirmed with the user off a screenshot
   where Week view's "Sun 20"–"Thu 24" headers wrapped onto two lines
   while "Fri 25"/"Sat 26" (shorter-rendering glyphs at the same width)
   stayed on one, an inconsistent, "shabby" look ("move the time's line
   to the left... so that everything fits in one line"). Freeing a bit of
   width from this hour-label column, alongside the font-weight drop
   below, is what actually closes the gap — .calendar-hour-labels (the
   matching column in the scrollable body below) must stay in sync with
   this exact value or the header/body columns drift out of alignment. */
.calendar-hour-labels-spacer { flex: 0 0 2.5rem; }
.calendar-day-header {
  flex: 1;
  min-width: 0;
  text-align: center;
  padding: 0.5rem 0.15rem;
  font-size: 0.82rem;
  /* 500, not 600 — confirmed with the user ("decrease the boldness by
     10%"); font-weight is a coarse, fixed-step property here (Poppins is
     only loaded at 500/600/700/800 in index.html's <link>, see "Nav
     rebuild" under Tech stack), so one full step down is the closest
     real equivalent to a literal 10% and is also just visibly narrower,
     which is the actual point — bold glyphs are wider than the same
     text set lighter, and that width difference is what was pushing
     the longer weekday+daynum strings onto a second line. */
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--muted);
  border-left: 1px solid var(--border);
}
.calendar-day-header.is-today { color: var(--royal); background: color-mix(in srgb, var(--royal) 8%, transparent); }

.calendar-time-body { display: flex; max-height: 60vh; overflow-y: auto; }
.calendar-hour-labels { flex: 0 0 2.5rem; }
.calendar-hour-row {
  height: 60px;
  font-size: 0.68rem;
  color: var(--muted);
  padding: 0.15rem 0.2rem;
  box-sizing: border-box;
  border-bottom: 1px solid var(--border);
}
.calendar-day-columns { display: flex; flex: 1; }
.calendar-day-column {
  flex: 1;
  min-width: 0;
  position: relative;
  height: calc(60px * 24);
  border-left: 1px solid var(--border);
  /* 24 evenly-spaced hour gridlines behind the events, without needing 24 DOM rows */
  background-image: repeating-linear-gradient(to bottom, var(--border) 0, var(--border) 1px, transparent 1px, transparent 60px);
}
/* Drop target while dragging an event — same treatment Kanban's own
   drag-over column already uses. */
.calendar-day-column.drag-over { box-shadow: inset 0 0 0 2px var(--royal); }
.calendar-event {
  position: absolute;
  left: 0.2rem;
  right: 0.2rem;
  height: 26px;
  border-radius: 6px;
  color: #ffffff;
  font-size: 0.72rem;
  font-weight: 500;
  padding: 0.15rem 0.4rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: grab;
  box-shadow: 0 1px 3px rgba(20, 22, 26, 0.2);
  /* Tells the browser not to handle a touch starting here as its own
     scroll/pan gesture — without this, dragging an event on a real touch
     device fought with the page/time-grid's own scroll instead of
     following the finger (wireCalendarDragPointer() in app.js handles
     the movement itself via pointermove). */
  touch-action: none;
}
.calendar-event:active { cursor: grabbing; }
.calendar-event.dragging { opacity: 0.4; }

/* Month grid */
.calendar-month-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--border);
  gap: 1px;
}
.calendar-month-cell {
  background: var(--bg);
  min-height: 5.5rem;
  padding: 0.3rem;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.calendar-month-cell.is-outside { background: var(--surface); }
.calendar-month-cell.is-outside .calendar-month-daynum { color: var(--muted); }
.calendar-month-cell.is-today { background: color-mix(in srgb, var(--royal) 6%, var(--bg)); }
.calendar-month-cell.drag-over { box-shadow: inset 0 0 0 2px var(--royal); }
.calendar-month-daynum { font-size: 0.78rem; font-weight: 600; }
.calendar-month-cell.is-today .calendar-month-daynum { color: var(--royal); }
.calendar-month-chip {
  color: #ffffff;
  font-size: 0.68rem;
  border-radius: 4px;
  padding: 0.08rem 0.3rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: grab;
  touch-action: none;
}
.calendar-month-chip.dragging { opacity: 0.4; }
.calendar-month-more { font-size: 0.68rem; color: var(--muted); padding: 0 0.2rem; }

@media (max-width: 640px) {
  /* Prev/Today/Next removed on mobile only — confirmed with the user off
     a phone screenshot ("remove this today and two back and forth
     button, just keep the October 2026 there"). Not removed on desktop:
     the swipe gesture that makes these redundant (wireCalendarSwipe() in
     app.js, added the same session) is touch-only, so a mouse-only
     desktop user would otherwise lose the only way to change the visible
     range. `.calendar-range-label`'s own left margin (spacing it from the
     now-hidden `›` button) is reset to 0 here so it doesn't read as an
     unexplained indent once it's the row's only child. */
  #calendar-prev-btn, #calendar-today-btn, #calendar-next-btn { display: none; }
  .calendar-range-label { margin-left: 0; }
  /* Always two rows (date label, then the Day/3Day/Week/Month toggle),
     never one — confirmed with the user ("when I click on day... the
     day/week bar jumps up next to the date, dont make that happen").
     .calendar-toolbar's base rule is `flex-wrap: wrap`, which only
     breaks onto a second line once the two children don't fit on one —
     true for Week/Month's longer range label ("Sep 20 – Sep 26") but not
     for Day view's shorter one ("Fri 25 Sept"), so the toggle visibly
     jumped up beside the label the moment you switched to Day. A fixed
     column layout removes that content-length dependency entirely: the
     two rows are always stacked, regardless of which view is active. */
  .calendar-toolbar { flex-direction: column; align-items: flex-start; }
  .calendar-day-header { font-size: 0.72rem; }
  .calendar-range-label { font-size: 0.92rem; }
  .calendar-month-cell { min-height: 3.6rem; }
  .calendar-month-chip, .calendar-month-more { display: none; }
  /* Narrow screens show a colored dot per day instead of name chips — the
     chips just don't fit at this width. */
  .calendar-month-cell.has-events::after {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--royal);
    align-self: center;
    margin-top: auto;
  }
}

/* Legend tab — one row per status (built-in and custom), each with a live
   swatch that is itself the button opening the color-wheel picker below
   (see openColorPicker() in app.js). Two columns where there's room. */
.calendar-legend-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 0.6rem;
  margin-top: 1rem;
}
.calendar-legend-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.calendar-legend-swatch {
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 6px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  padding: 0;
  cursor: pointer;
}
.calendar-legend-label { flex: 1 1 auto; font-size: 0.9rem; }

/* ---------- Custom color-wheel picker modal ---------- */
/* Centered on every screen size, unlike the rest of the app's modals
   (New Lead, comment, schedule, etc.), which are deliberately bottom
   sheets on mobile (.modal's base align-items: flex-end) and only
   switch to centered above the 640px breakpoint. Confirmed with the
   user off a screenshot ("this came to the bottom! move it to the
   center") — the wheel is a precision visual picker, not a form list,
   so anchoring it to the bottom edge (and clipping it against the
   keyboard-safe-area padding meant for scrollable form content) never
   fit this one the way it fits the others. Scoped to #color-picker-modal
   specifically rather than changing .modal's own base rule, which would
   have flipped every other modal in the app to centered-on-mobile too. */
#color-picker-modal { align-items: center; justify-content: center; }
.color-picker-modal-content {
  max-width: 300px;
  width: calc(100% - 2rem);
  text-align: center;
  border-radius: 1rem;
  border-top: none;
  max-height: 90vh;
}
.color-wheel-wrap {
  position: relative;
  width: 220px;
  height: 220px;
  margin: 0.75rem auto;
}
#color-wheel-canvas { width: 220px; height: 220px; border-radius: 50%; touch-action: none; cursor: crosshair; }
.color-wheel-cursor {
  position: absolute;
  width: 1.1rem;
  height: 1.1rem;
  margin-left: -0.55rem;
  margin-top: -0.55rem;
  border-radius: 50%;
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 1px rgba(20, 22, 26, 0.35), var(--shadow-card);
  pointer-events: none;
}
.color-picker-value-row { display: flex; align-items: center; gap: 0.6rem; margin: 0.5rem 0; text-align: left; }
.color-picker-value-row input[type="range"] { flex: 1; width: auto; }
.color-picker-preview-row { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.75rem; }
.color-picker-swatch { width: 2rem; height: 2rem; border-radius: 8px; flex-shrink: 0; border: 1px solid var(--border); }
.color-picker-hex-input { flex: 1; text-transform: lowercase; }
