/* MaidThis brand kit v2.0, franchisor palette. Internal tools surface. */

@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600;700;800&family=Open+Sans:wght@400;500;600;700&display=swap');

:root {
  --mt-teal: #5AB3C9;
  --mt-white: #FFFFFF;
  --mt-black: #0F1821;
  --mt-navy: #2C3E50;
  --mt-ocean: #0C85C2;
  --mt-aqua: #A7DBE7;
  --mt-gray: #E6F1F4;
  --mt-green: #7CCA5B;
  --mt-yellow: #FFB600;
  --mt-red: #E5484D;
  --mt-window: #7BAFC4; /* arrival window state, ops console */

  --teal-100: #EAF6F9;
  --teal-200: #C6E6ED;
  --teal-300: #9CD3DE;
  --teal-400: #7AC3D2;
  --teal-500: #5AB3C9;
  --teal-600: #3E95AC;
  --teal-700: #2B7387;
  --teal-800: #1E5464;

  --ocean-100: #E1F1FA;
  --ocean-200: #A9D6EE;
  --ocean-300: #6EB8DE;
  --ocean-500: #0C85C2;
  --ocean-700: #085E8A;

  --navy-100: #E9ECF0;
  --navy-300: #8A9AAB;
  --navy-500: #2C3E50;
  --navy-700: #1C2A38;

  --bg-page: #FFFFFF;
  --bg-muted: #E6F1F4;
  --bg-soft: #F6FAFB;
  --bg-inverse: #0F1821;
  --bg-brand: #5AB3C9;

  --fg-primary: #2C3E50;
  --fg-secondary: #546778;
  --fg-tertiary: #8A9AAB;
  --fg-inverse: #FFFFFF;
  --fg-brand: #0C85C2;

  --border-subtle: #DCE7EC;
  --border-default: #C6D4DB;
  --border-strong: #2C3E50;

  --state-success: #7CCA5B;
  --state-warning: #FFB600;
  --state-info: #0C85C2;
  --state-error: #E5484D;

  --shadow-sm: 0 2px 10px rgba(44, 62, 80, 0.06);
  --shadow-md: 0 10px 28px rgba(44, 62, 80, 0.10);
  --shadow-lg: 0 22px 48px rgba(44, 62, 80, 0.14);
  --shadow-ocean: 0 12px 26px rgba(12, 133, 194, 0.22);

  --pill: 999px;
  --radius-sm: 12px;
  --radius-md: 18px;
  --radius-lg: 22px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --focus-ring: 0 0 0 3px rgba(12, 133, 194, 0.18);

  /* How much of a tool's own --card-accent survives when the nav launcher panel uses it as bare
     ink (an icon, a "you are here" tag) rather than behind a tinted chip. Every accent in
     src/tools.js was picked as dark ink on a white card, so 100% is a no-op here in light mode.
     Same measured fix as --home-ink-keep/--he-ink-keep: on a dark ground five of the eleven fail
     outright (roughly 1.3:1, not dim, invisible), because a bare accent was never designed to sit
     directly on dark. Only needed here because the nav panel dropped the tinted-chip background
     the card grid used to hide this behind; see the dark blocks below for the lift. */
  --nav-ink-keep: 100%;
  --nav-ink-toward: #FFFFFF;
}

/* ---------- Dark mode, site-wide ----------
 *
 * Added August 2026. The Business Dashboard and the Lead Tracker each proved a dark theme on one
 * page first before this existed; the Dashboard's approach is what this is lifted from almost
 * unchanged, because it already survived a real, complex page. **The trick is remapping tokens,
 * not restyling components**: `--mt-white` becomes a dark surface and `--mt-navy` becomes light
 * text, so any component already written against the shared token system is correct in both
 * themes for free. A new component should keep doing that rather than adding its own rules under
 * a dark selector.
 *
 * **Gated on `html.dark-ready`, and that gate is load-bearing, not decoration.** The first version
 * of this applied to bare `:root`, site-wide, on the reasoning that an unadopted page would just
 * show a dark nav over an otherwise light page. Tried against the Pricing Playbook, a page that
 * was never audited for this, that reasoning turned out wrong: pricing.css reads several of these
 * SAME tokens for text colour while keeping its own literal light backgrounds, so the "unaffected"
 * page actually rendered pale grey text on white, unreadable in several places, the moment a
 * visitor's OS preferred dark. A silent site-wide flip cannot tell a page that is safe from one
 * that only looks safe, so it does not get to run anywhere it has not been checked. **`dark-ready`
 * is added to `<html>` by hand, per page, only once that page has actually been looked at in both
 * themes**: currently `views/home.ejs`, `views/roster.ejs`, `views/ops-console.ejs`, and
 * `views/dashboard.ejs`. Adding it to a new page without checking the page first reproduces
 * exactly this bug on whatever page you just added it to.
 *
 * **Three states, not two, on a page that has opted in.** `data-theme` is stamped on <html> only
 * once somebody has actually pressed the toggle (`public/js/site-theme.js`, wired from the button
 * `partials/nav.ejs` renders on every page, adopted or not), so an account that has never touched
 * it follows the operating system through `prefers-color-scheme`. That media query is guarded by
 * `:not([data-theme="light"])` so an explicit light choice always beats a dark OS setting, and
 * `[data-theme="dark"]` is repeated verbatim so the explicit choice wins the other direction too.
 * Same structure the Dashboard and Lead Tracker already used.
 *
 * **The toggle button itself, and its icon swap, are NOT gated.** It renders and works identically
 * on every page, because it controls one shared preference regardless of which pages currently
 * act on it; gating the icon would make the button look broken on an unadopted page instead of
 * simply having nothing to do there yet, which is the honest state.
 *
 * The Dashboard's OWN dark block, previously self-contained under this exact same
 * `[data-theme="dark"]` selector, now inherits from here instead and only keeps what is genuinely
 * its own (`--dash-*`). The Lead Tracker is untouched: it uses a deliberately separate attribute,
 * `data-ld-theme`, and stays its own island for now. */
@media (prefers-color-scheme: dark) {
  :root.dark-ready:not([data-theme="light"]) {
    --bg-page: #0F1821;
    --bg-muted: #17222D;
    --bg-soft: #17222D;
    --bg-inverse: #FFFFFF;

    --fg-primary: #E6F1F4;
    --fg-secondary: #A7B6C2;
    --fg-tertiary: #6E7E8C;
    --fg-inverse: #0F1821;

    --border-subtle: #23303B;
    --border-default: #2C3E50;
    --border-strong: #E6F1F4;

    --mt-white: #17222D;
    --mt-navy: #E6F1F4;
    --mt-gray: #1C2A34;

    /* The scale ramps, inverted: 100 was the palest tint in light mode (a soft panel background)
       and becomes the darkest here for the same job; 700/800 was the highest-contrast text and
       becomes the lightest, for the same reason. Anything reading var(--teal-700) as text on a
       var(--teal-100) panel keeps working unmodified, same pairing, same contrast direction. */
    --teal-100: #132229;
    --teal-200: #15272F;
    --teal-300: #1A323C;
    --teal-400: #7AC3D2;
    --teal-500: #5AB3C9;
    --teal-600: #6FC3D6;
    --teal-700: #8FD3E4;
    --teal-800: #B9E5EF;

    --ocean-100: #0F2029;
    --ocean-200: #163247;
    --ocean-300: #2A4A63;
    --ocean-500: #4FB3E8;
    --ocean-700: #7AC7EE;

    --navy-100: #1A2732;
    --navy-300: #6E7E8C;
    --navy-500: #E6F1F4;
    --navy-700: #F2F7F9;

    --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.28);
    --shadow-md: 0 10px 28px rgba(0, 0, 0, 0.34);
    --shadow-lg: 0 22px 48px rgba(0, 0, 0, 0.4);
    --shadow-ocean: 0 12px 26px rgba(79, 179, 232, 0.28);

    --nav-ink-keep: 50%;
    --nav-ink-toward: #DCE9EE;
  }
}
:root.dark-ready[data-theme="dark"] {
  --bg-page: #0F1821;
  --bg-muted: #17222D;
  --bg-soft: #17222D;
  --bg-inverse: #FFFFFF;

  --fg-primary: #E6F1F4;
  --fg-secondary: #A7B6C2;
  --fg-tertiary: #6E7E8C;
  --fg-inverse: #0F1821;

  --border-subtle: #23303B;
  --border-default: #2C3E50;
  --border-strong: #E6F1F4;

  --mt-white: #17222D;
  --mt-navy: #E6F1F4;
  --mt-gray: #1C2A34;

  --teal-100: #132229;
  --teal-200: #15272F;
  --teal-300: #1A323C;
  --teal-400: #7AC3D2;
  --teal-500: #5AB3C9;
  --teal-600: #6FC3D6;
  --teal-700: #8FD3E4;
  --teal-800: #B9E5EF;

  --ocean-100: #0F2029;
  --ocean-200: #163247;
  --ocean-300: #2A4A63;
  --ocean-500: #4FB3E8;
  --ocean-700: #7AC7EE;

  --navy-100: #1A2732;
  --navy-300: #6E7E8C;
  --navy-500: #E6F1F4;
  --navy-700: #F2F7F9;

  --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.28);
  --shadow-md: 0 10px 28px rgba(0, 0, 0, 0.34);
  --shadow-lg: 0 22px 48px rgba(0, 0, 0, 0.4);
  --shadow-ocean: 0 12px 26px rgba(79, 179, 232, 0.28);

  --nav-ink-keep: 50%;
  --nav-ink-toward: #DCE9EE;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: 'Open Sans', system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: var(--fg-primary);
  background: var(--bg-page);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, button, .pill, nav a { font-family: 'Montserrat', system-ui, sans-serif; }
a { color: var(--mt-ocean); text-decoration: none; }
strong { font-weight: 700; }

:is(button, a, input, [tabindex]):focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--pill);
}

/* ---------- Shared top nav ----------
 *
 * Three parts: the brand, a strip of tool links that trims itself to the space available, and a
 * launcher holding the complete list. Admin and the user's own controls sit at the right hand end,
 * outside the strip, so they can never be pushed off by a tool being added.
 *
 * **68px tall at every width, and it has to stay that way.** Other pages anchor sticky furniture to
 * that number (`.ops-toolbar { top: 68px }`, the rails on the Home Profile pages). The launcher panel
 * is absolutely positioned below the bar rather than pushing it taller, for the same reason.
 */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 100;
  height: 68px;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-subtle);
}
.site-nav .inner {
  position: relative;
  max-width: 1200px;
  margin: 0 auto;
  height: 100%;
  padding: 0 32px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.site-nav .brand { display: flex; align-items: center; gap: 12px; flex-shrink: 0; }
.site-nav .brand img { height: 34px; width: auto; display: block; }

/* The link strip. public/js/site-nav.js hides whichever links would not fit, so on a wide screen
   this holds everything and on a narrow one it holds the first few, with the rest reachable from
   the launcher beside it. With the script blocked it keeps its old behaviour: every link present,
   scrolling sideways inside itself. */
.site-nav .links {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: 10px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.site-nav .links::-webkit-scrollbar { display: none; }
/* Set by the script on a link it has measured as not fitting. A class rather than the hidden
   attribute, because these are flex children with their own display and the attribute would lose. */
.site-nav .links a.is-tucked { display: none; }
.site-nav .links a {
  font-size: 13px;
  font-weight: 700;
  color: var(--fg-secondary);
  padding: 8px 13px;
  border-radius: 9px;
  white-space: nowrap;
  transition: color 0.16s var(--ease), background 0.16s var(--ease);
}
.site-nav .links a:hover { color: var(--fg-primary); background: var(--bg-muted); }
/* The current page is marked by a rule under the label rather than a filled shape, so an active
   link and a hovered one never read as the same thing. */
.site-nav .links a.active {
  color: var(--ocean-700);
  background: var(--teal-100);
  box-shadow: inset 0 -2px 0 var(--mt-ocean);
}

/* ---------- The launcher ---------- */

.nav-launcher { position: relative; flex-shrink: 0; }
.nav-launcher-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 8px 12px;
  border-radius: 9px;
  border: 1px solid var(--border-default);
  background: var(--mt-white);
  font-family: 'Montserrat', sans-serif;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--fg-secondary);
  cursor: pointer;
  white-space: nowrap;
  list-style: none;
  transition: border-color 0.16s var(--ease), color 0.16s var(--ease);
}
/* Both are needed to drop the native disclosure triangle across browsers. */
.nav-launcher-btn::-webkit-details-marker { display: none; }
.nav-launcher-btn::marker { content: ''; }
.nav-launcher-btn svg { width: 15px; height: 15px; flex: none; }
.nav-launcher-btn:hover { border-color: var(--mt-teal); color: var(--fg-primary); }
.nav-launcher[open] .nav-launcher-btn {
  border-color: var(--mt-ocean);
  color: var(--ocean-700);
  background: var(--teal-100);
}
.nav-launcher-count {
  display: inline-block;
  min-width: 18px;
  padding: 1px 5px;
  border-radius: 6px;
  background: var(--mt-ocean);
  color: var(--mt-white);
  font-size: 11px;
  line-height: 1.5;
  text-align: center;
}
.nav-launcher-count[hidden] { display: none; }

.nav-panel {
  position: absolute;
  top: calc(100% + 14px);
  right: 0;
  width: min(880px, calc(100vw - 32px));
  background: var(--mt-white);
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  box-shadow: var(--shadow-lg);
  padding: 20px 22px;
  max-height: min(78vh, 700px);
  overflow-y: auto;
  z-index: 120;
}
@media (prefers-reduced-motion: no-preference) {
  .nav-launcher[open] .nav-panel { animation: nav-panel-in 0.16s var(--ease) both; }
}
@keyframes nav-panel-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* Editorial treatment, matching the launchpad (public/css/home-editorial.css): no card, no rail,
 * a tool's accent surviving only on its icon, hairlines instead of borders. This used to be a grid
 * of bordered cards, which was the same "rounded box plus accent" shape the launchpad itself moved
 * away from; a launcher reachable from every page and the launchpad looking like two different
 * products was the actual complaint, so this brings the panel in line rather than leaving it behind.
 *
 * Groups pack into columns rather than sitting in a fixed 3-wide grid of their own: a short group
 * (Settings, one tool) used to leave a hole under itself beside a tall one (Running today, four
 * tools). column-count lets a short group's column carry the next group underneath it instead,
 * same fix as .he-sections on the launchpad. break-inside: avoid keeps one group from being split
 * across two columns. */
.nav-panel-inner {
  column-count: 3;
  column-gap: 28px;
}
@media (max-width: 1000px) { .nav-panel-inner { column-count: 2; column-gap: 22px; } }
@media (max-width: 760px)  { .nav-panel-inner { column-count: 1; } }

.nav-panel-group {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  page-break-inside: avoid;
  margin-bottom: 18px;
}
.nav-panel-label {
  font-family: 'Montserrat', sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
  padding-bottom: 7px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--border-subtle);
}

.nav-panel-grid { display: block; }

.nav-panel-card {
  position: relative;
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 9px 4px;
  margin-inline: -4px;
  border-radius: 3px;
  text-decoration: none;
  background: none;
  transition: background-color 0.12s var(--ease);
}
/* The hairline between rows, not around them. */
.nav-panel-card + .nav-panel-card { border-top: 1px solid var(--border-subtle); }
.nav-panel-card:hover { background: var(--bg-soft); }
/* The one place a tool's accent still appears, same rule the launchpad's icon carries. Declared
   here rather than at :root: a custom property referencing another resolves where it is declared,
   and --card-accent (set inline per card in nav.ejs) is empty at the root. */
.nav-panel-icon {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  --nav-accent-ink: color-mix(in srgb, var(--card-accent, #5AB3C9) var(--nav-ink-keep), var(--nav-ink-toward));
  color: var(--nav-accent-ink);
  flex: none;
}
.nav-panel-icon svg { width: 17px; height: 17px; }
.nav-panel-text { min-width: 0; }
.nav-panel-text b {
  display: block;
  font-family: 'Montserrat', sans-serif;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--fg-primary);
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Wraps to two lines rather than truncating on one, same reasoning and the same two-class weight
   as .he-text .he-blurb on the launchpad: a bare element selector here would be easy to outrank
   later by a class added for some other reason, and this is the exact trap that bit the launchpad's
   own blurb and the classic home card's before it. */
.nav-panel-text .nav-panel-text-blurb {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  font-size: 12px;
  line-height: 1.42;
  color: var(--fg-tertiary);
  margin-top: 2px;
}
.nav-panel-card.is-current {
  --nav-accent-ink: color-mix(in srgb, var(--card-accent, #5AB3C9) var(--nav-ink-keep), var(--nav-ink-toward));
}
.nav-panel-card.is-current .nav-panel-text b { color: var(--nav-accent-ink); }
.nav-panel-here {
  --nav-accent-ink: color-mix(in srgb, var(--card-accent, #5AB3C9) var(--nav-ink-keep), var(--nav-ink-toward));
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--nav-accent-ink);
  white-space: nowrap;
}

/* ---------- Admin and the user's own controls ---------- */

.nav-user {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
/* Admin lives here rather than in the tool strip, so it is never the link that falls off the end.
   It is not a tool, it is where the system is configured. */
.nav-admin {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border-radius: 9px;
  border: 1px solid var(--border-default);
  font-size: 12.5px;
  font-weight: 700;
  font-family: 'Montserrat', sans-serif;
  color: var(--fg-secondary);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.16s var(--ease), border-color 0.16s var(--ease), background 0.16s var(--ease);
}
.nav-admin svg { width: 15px; height: 15px; flex: none; }
.nav-admin:hover { color: var(--fg-primary); border-color: var(--mt-teal); background: var(--bg-soft); }
.nav-admin.active { color: var(--ocean-700); border-color: var(--mt-ocean); background: var(--teal-100); }

.nav-user-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--fg-secondary);
  white-space: nowrap;
}
.nav-signout-form { display: inline; }
.nav-signout {
  font-size: 12px;
  font-weight: 700;
  color: var(--fg-tertiary);
  background: none;
  border: 1.5px solid var(--border-default);
  border-radius: 9px;
  padding: 6px 14px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  font-family: 'Montserrat', sans-serif;
}
.nav-signout:hover { color: var(--state-error); border-color: var(--state-error); }

/* Narrow screens. The 68px height is deliberately kept at every width, other pages anchor sticky
   toolbars to it. What gives way instead, in order, is the strip (the script tucks more links away),
   then the Admin label, then the user's name. */
/* The panel hangs off the launcher, which sits further left the narrower the bar gets, so below
   this width an 880px panel starts running off the left edge of the screen. Anchored to the
   viewport instead of to the button from here down, still right aligned, still capped at its full
   width. Caught by measuring it at 390px, where it was 154px off screen. */
@media (max-width: 1200px) {
  /* Dropping the launcher out of the positioning flow hands the panel to .site-nav, which is
     sticky and therefore positioned, and which spans the layout viewport exactly. position: fixed
     was tried first and resolved against the visual viewport, so a classic scrollbar pushed the
     panel three pixels off the left edge. This has no such ambiguity. */
  .nav-launcher { position: static; }
  /* One width and one inset, rather than both insets plus an auto margin. That combination is
     over-constrained, and the browser settled it by giving margin-left a negative value the width
     of the scrollbar, which put the panel back off the left edge. */
  /* Pinned to both edges of .inner rather than given a percentage width. .site-nav carries a
     backdrop-filter, which makes it the containing block but resolves a percentage against the
     visual viewport, so calc(100% - 24px) came out a scrollbar too wide and hung off the left.
     .inner is a plain box with known padding, so two insets and no width is unambiguous. */
  .nav-panel {
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    width: auto;
  }
}

@media (max-width: 1000px) {
  .site-nav .inner { padding: 0 16px; gap: 6px; }
  .site-nav .links { margin-left: 0; }
  .site-nav .links a { padding: 8px 11px; }
  .nav-launcher-label { display: none; }
  .nav-launcher-btn { padding: 8px 10px; }
}
@media (max-width: 760px) {
  .nav-panel { width: calc(100vw - 24px); }
  .nav-panel-grid { grid-template-columns: minmax(0, 1fr); }
  .nav-admin span { display: none; }
  .nav-admin { padding: 7px 9px; }
}
@media (max-width: 640px) {
  .site-nav .brand img { height: 28px; }
  .nav-user { gap: 8px; }
  .nav-user-name { display: none; } /* the name is the first thing worth dropping for space */
}

/* Below this width the strip overflows for every role even after the script has tucked links away,
   so the fade always means "there is more here, swipe". Kept off wider screens on purpose: CSS
   cannot tell whether a scroller actually overflows, and a fade over links that already fit would
   just look like a rendering fault. */
@media (max-width: 560px) {
  .site-nav .links {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
  }
}

/* ---------- Shared footer ---------- */
.site-footer {
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-soft);
  margin-top: 48px;
}
.site-footer .inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 28px 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.site-footer .links { display: flex; gap: 18px; flex-wrap: wrap; }
.site-footer .links a {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--fg-secondary);
}
.site-footer .links a:hover { color: var(--ocean-700); }
.site-footer .tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-tertiary);
}

/* ---------- Layout ---------- */
.container { max-width: 1200px; margin: 0 auto; padding: 0 32px; }
@media (max-width: 640px) { .container { padding: 0 20px; } }

/* ---------- Buttons, pills always ---------- */
.btn {
  font-family: 'Montserrat', sans-serif;
  font-weight: 700;
  font-size: 13px;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border-radius: var(--pill);
  padding: 11px 22px;
  transition: transform 0.16s var(--ease), box-shadow 0.16s var(--ease), background 0.16s var(--ease);
  text-decoration: none;
}
/* display:inline-flex above beats the user agent's [hidden] rule, so a .btn toggled with the
   hidden attribute would otherwise stay visible. */
.btn[hidden] { display: none; }
/* Reworked September 2026, same pass as the public form's own buttons (see the long note on
 * .jn-btn in join.css for the reasoning). Two rules keep this safe to change app-wide, since
 * every tool in this app uses .btn:
 *
 * **The radius is untouched.** These stay capsules. The owner's no-pill preference applies to
 * specific pages, and CLAUDE.md's own note says to change `.btn` globally or not at all rather
 * than special-casing a page, so this pass changes the *finish* and nothing about the shape.
 *
 * **The highlight is a translucent overlay, never a second hex.** `background-image` carries a
 * white-to-dark wash over whatever `background-color` the token resolves to, so this is correct
 * in dark mode for free rather than needing its own dark block, and the hover colour still
 * animates underneath it. */
.btn:active { transform: translateY(1px) scale(0.99); }

.btn-primary {
  background-color: var(--mt-teal);
  /* Three stops, same falloff reasoning as .jn-btn in join.css. */
  background-image: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.2) 0%,
    rgba(255, 255, 255, 0.04) 42%,
    rgba(0, 0, 0, 0.07) 100%
  );
  color: var(--fg-inverse);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    0 1px 2px rgba(12, 47, 62, 0.2),
    var(--shadow-ocean);
}
.btn-primary:hover {
  background-color: var(--teal-600);
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.32),
    0 2px 5px rgba(12, 47, 62, 0.22),
    var(--shadow-ocean);
}
.btn-primary:active {
  box-shadow:
    inset 0 2px 5px rgba(0, 0, 0, 0.24),
    0 1px 1px rgba(12, 47, 62, 0.14);
}

.btn-secondary {
  background-color: var(--mt-white);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(44, 62, 80, 0.03));
  color: var(--mt-navy);
  border: 2px solid var(--border-default);
  box-shadow: 0 1px 2px rgba(44, 62, 80, 0.08);
}
.btn-secondary:hover {
  border-color: var(--mt-teal);
  color: var(--teal-700);
  transform: translateY(-1px);
  box-shadow: 0 3px 10px rgba(44, 62, 80, 0.12);
}
.btn-secondary:active { box-shadow: inset 0 2px 4px rgba(44, 62, 80, 0.12); }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn:hover, .btn:active { transform: none; }
}

/* ---------- Cards ---------- */
.card {
  background: var(--mt-white);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 24px;
}

/* ---------- Cleaner avatar, photo or initials fallback ---------- */
.cleaner-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background-size: cover;
  background-position: center;
  background-color: var(--teal-100);
  box-shadow: 0 0 0 2px var(--mt-white), 0 0 0 3px var(--border-subtle);
  font-family: 'Montserrat', sans-serif;
  font-size: 13px;
  font-weight: 800;
  color: var(--teal-700);
}
.cleaner-avatar.sm { width: 28px; height: 28px; font-size: 11px; }

/* ---------- Dark mode: the handful of things the token remap above cannot reach ----------
 *
 * Everything else in this file already reads through --mt-white, --mt-navy, --bg-*, --fg-*, and
 * --border-*, so the remap at the top of this file is the entire dark theme for it. These three
 * are the only literals in this stylesheet that were never a token to begin with: a translucent
 * white the nav blurs the page through, and a color-mix() target that names "white" outright. */
/* Gated the same as the rest: the nav's own text, borders and hover states all still read
   through --fg-secondary/--border-default/etc, which only flip to dark on an adopted page. A
   dark nav background here on an unadopted page would sit under light-mode text, unreadable, the
   same class of bug the header comment above describes. */
/* The nav-panel-icon rule that used to live here (a tinted background behind the icon) went with
   the card grid it was covering for. The panel is editorial now, see the block starting at
   .nav-panel-inner above: the icon carries the accent as bare ink via --nav-ink-keep/-toward,
   computed right there rather than needing a dark override of its own here. */
@media (prefers-color-scheme: dark) {
  :root.dark-ready:not([data-theme="light"]) .site-nav { background: rgba(15, 24, 33, 0.86); border-bottom-color: var(--border-subtle); }
}
:root.dark-ready[data-theme="dark"] .site-nav { background: rgba(15, 24, 33, 0.86); border-bottom-color: var(--border-subtle); }

/* ---------- The brand logo swap ----------
 *
 * See the comment on the markup in partials/nav.ejs for why the primary asset cannot stay on a
 * dark nav: half of it is navy and measures 1.6:1 there.
 *
 * **Gated on .dark-ready exactly like .site-nav above, and that is load bearing.** The nav renders
 * on every page in the app, and on an unadopted one it stays light even when the OS prefers dark,
 * because the rule above is what darkens it. Swapping the logo on bare `prefers-color-scheme`
 * would put the white asset on that still-light nav, which is a white logo on a white bar: not
 * merely low contrast, gone. The two conditions have to move together or the logo can be wrong on
 * roughly seventy pages at once. */
/* Every selector here is scoped through `.site-nav .brand img` rather than the shorter
   `.brand-logo`, and that is not style preference. The sizing rule further up this file is
   `.site-nav .brand img { display: block; }`, specificity (0,2,1); a bare `.brand-logo.is-dark`
   hide is (0,2,0) and **loses to it**, so the dark asset stayed visible and every light page
   rendered both logos side by side. Caught only by checking an unadopted page. Match or beat that
   selector, or add `display` to a rule that does not also set the size. */
.site-nav .brand img.is-dark { display: none; }

@media (prefers-color-scheme: dark) {
  :root.dark-ready:not([data-theme="light"]) .site-nav .brand img.is-light { display: none; }
  :root.dark-ready:not([data-theme="light"]) .site-nav .brand img.is-dark { display: block; }
}
:root.dark-ready[data-theme="dark"] .site-nav .brand img.is-light { display: none; }
:root.dark-ready[data-theme="dark"] .site-nav .brand img.is-dark { display: block; }

/* ---------- The theme toggle ----------
 *
 * Rendered on every page from partials/nav.ejs, wired by public/js/site-theme.js. Icon swap
 * follows the attribute directly rather than JS toggling a class, so the button's own appearance
 * can never disagree with what the page actually did. */
.theme-toggle {
  width: 34px;
  height: 34px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 9px;
  border: 1px solid var(--border-default);
  background: var(--mt-white);
  color: var(--fg-secondary);
  cursor: pointer;
  transition: border-color 0.16s var(--ease), color 0.16s var(--ease);
}
.theme-toggle:hover { border-color: var(--mt-teal); color: var(--fg-primary); }
.theme-toggle svg { width: 16px; height: 16px; }
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
