/* SELF HOSTED fonts (2026-08-13). These used to come from Google Fonts
   at page view time, which meant every page depended on an outside
   server: the browser smoke suite caught Firefox failing to fetch them
   (80 console errors in one run), and hospital content filters block
   exactly this kind of request. The four woff2 files in /static/fonts
   are the latin subsets Google served, all SIL Open Font License, free
   to self host. Inter and Source Serif 4 are VARIABLE fonts, one file
   covers the whole weight range. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/static/fonts/inter-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url('/static/fonts/source-serif-4-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
}

/* Coranth, the "Signal" palette: blue carries the interface, red is
   the second voice. --brand is the workhorse blue that white text sits
   on (buttons, selected chips, stat figures), --brand-bright is the
   lighter blue used where colour is decoration rather than something
   text has to sit on (hovers, focus rings, drag targets), and
   --brand-dark carries the sidebar.

   Red is deliberately rationed. It is a brand colour AND the danger
   colour, which only works because red never appears as ordinary
   furniture: it means destructive (--danger), or it means one specific
   facility. Start using red for decoration and the warning value of it
   is gone.

   The scheme is "Institutional Navy", chosen from five directions on
   marketability: deep navy with a crimson accent is the register of
   institutions people trust with money and lives, which is the right
   signal when the buyer is a hospital or a physician group. It is
   deliberately the least fashionable of the five options and the most
   durable.

   Note the split, which is the easiest thing here to break: --accent
   is the brand crimson and is only ever a FILL or a rule, while
   --danger is a deeper crimson because it has to survive as TEXT,
   both on white (the Remove button is a ghost button with red text)
   and on --danger-bg (hard badges, strength buttons, avoid markers).
   Blue splits the same way, --brand for fills and readable text,
   --brand-bright for decoration only. Substituting one for the other
   looks fine and quietly fails readability.

   Every pairing was computed, not eyeballed, and all 20 clear the
   4.5:1 that small text needs, with real headroom this time: the
   tightest are --amber on --amber-bg at 4.73 and white on
   --brand-bright at 5.17, and buttons sit at 10.36. Anything changed
   here has to be rechecked against whatever text sits on it. The mark
   in /static/coranth-mark.svg is deliberately NOT these values,
   see the comment inside that file. */
:root {
  --ink: #0F172A;
  --paper: #F8FAFC;
  --panel: #FFFFFF;
  --brand: #1E3A8A;
  --brand-bright: #2563EB;
  --brand-dark: #0F2255;
  --brand-soft: #A8C3F0;
  --brand-tint: #DBEAFE;
  --sidebar-ink: #E8EEFB;
  --sidebar-link: #B9C9E8;
  --accent: #BE123C;
  --amber: #996019;
  --amber-bg: #FCF3E6;
  --amber-line: #EFD9B4;
  --line: #DDE5F0;
  --muted: #55607A;
  --danger: #9F1239;
  --danger-bg: #FFE4E6;
  --danger-line: #F6C9D2;
  --highlight: #FBF0A8;
  --radius: 10px;
}

/* Hovering a provider's name in any calendar lights up that person's
   other shifts, so you can see the rest of their month without reading
   every cell. The box-shadow spreads the colour a little past the text
   so it still reads clearly on top of a facility's own coloured block.
   Wired up once in base.html, see the comment there. */
.shift-highlight {
  background: var(--highlight);
  border-radius: 3px;
  box-shadow: 0 0 0 2px var(--highlight);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: 'Inter', -apple-system, sans-serif;
  color: var(--ink);
  background: var(--paper);
  display: flex;
  min-height: 100vh;
}

h1, h2, h3 { font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; margin: 0 0 0.4em; }
h1 { font-size: 1.7rem; }
h2 { font-size: 1.25rem; }
code, .mono { font-family: 'IBM Plex Mono', monospace; }

/* ---- Sidebar ---- */
/* The sidebar is its own scroll region, pinned to the viewport, so a
   long calendar scrolls under it instead of dragging the whole nav out
   of reach. It also collapses to a narrow strip (the toggle stays), so
   a full month grid can have the whole window; the choice is remembered
   in localStorage and applied to <html> before first paint. */
nav.sidebar {
  width: 200px;
  flex-shrink: 0;
  background: var(--brand-dark);
  color: var(--sidebar-ink);
  padding: 24px 0;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  /* Without this the flex container stretches the sidebar to the full
     page height, which leaves sticky nothing to pin and no reason to
     scroll on its own. Safari is strictest about it. */
  align-self: flex-start;
  overflow-y: auto;
  transition: width 0.15s ease;
}
.nav-toggle {
  background: none;
  border: none;
  color: var(--sidebar-link);
  font-size: 1.1rem;
  line-height: 1;
  padding: 4px 8px;
  margin: 0 12px 10px auto;  /* right aligned above the logo */
  border-radius: 6px;
  flex-shrink: 0;
  cursor: pointer;  /* it reads as decoration otherwise */
}
.nav-toggle:hover { background: rgba(255,255,255,0.10); color: #fff; }

.nav-collapsed nav.sidebar {
  width: 46px;
  padding: 24px 0;
  overflow: hidden;
}
/* Everything except the toggle goes away, so the strip is just the way
   back. display:none rather than opacity so nothing stays clickable. */
.nav-collapsed nav.sidebar > *:not(.nav-toggle) { display: none; }
.nav-collapsed .nav-toggle { margin: 0 auto; }
/* The Coranth mark sits directly on the sidebar, no white panel: its
   two chevrons are lifted brighter than the brand values precisely so
   they hold up against the dark navy on their own. Mark and wordmark
   read as one lockup on a single line, which also survives the sidebar
   collapsing. */
nav.sidebar .brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 18px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.12);
  margin-bottom: 12px;
  text-decoration: none;
}
nav.sidebar .brand img {
  display: block;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
}
nav.sidebar .brand span {
  font-family: 'Source Serif 4', serif;
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  color: var(--sidebar-ink);
}
nav.sidebar .brand:hover span { color: #FFFFFF; }
nav.sidebar a {
  color: var(--sidebar-link);
  text-decoration: none;
  padding: 10px 20px;
  font-size: 0.92rem;
  border-left: 3px solid transparent;
}
nav.sidebar a:hover, nav.sidebar a.active {
  color: #fff;
  background: rgba(255,255,255,0.06);
  border-left-color: var(--accent);
}
.nav-badge {
  display: inline-block;
  min-width: 16px;
  padding: 0 5px;
  border-radius: 8px;
  background: var(--amber);
  color: #fff;
  font-size: 0.7rem;
  line-height: 16px;
  text-align: center;
}
.sidebar-footer {
  margin-top: auto;
  padding: 16px 20px 0;
  border-top: 1px solid rgba(255,255,255,0.12);
}
nav.sidebar .sidebar-footer a {
  display: block;
  padding: 0 0 10px;
  font-size: 0.85rem;
}
.sidebar-footer .role-badge {
  color: var(--sidebar-link);
  font-size: 0.78rem;
  margin-bottom: 8px;
}
.sidebar-footer button.ghost {
  color: var(--sidebar-link);
  border-color: rgba(255,255,255,0.25);
  width: 100%;
}
.sidebar-footer button.ghost:hover { background: rgba(255,255,255,0.06); }

main {
  flex: 1;
  min-width: 0;
  padding: 32px 40px;
  max-width: 1100px;
}
/* Collapsing the nav is only worth doing if the content actually claims
   the space, so the reading-width cap lifts with it. That's the whole
   point of the toggle: a full month grid gets the entire window. */
.nav-collapsed main { max-width: none; }

/* ---- Login page (no sidebar, standalone) ---- */
.login-wrap {
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.login-card { width: 100%; max-width: 360px; }

/* ---- Cards ---- */
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 20px;
}
.stat-grid { display: flex; gap: 16px; flex-wrap: wrap; }
.stat {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 20px;
  min-width: 140px;
}
.stat .value { font-family: 'Source Serif 4', serif; font-size: 1.9rem; font-weight: 600; color: var(--brand); }
.stat .label { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.stat.warn .value { color: var(--amber); }

/* ---- Table ---- */
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin-bottom: 20px; }
table:last-child { margin-bottom: 0; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-weight: 500; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }

/* Rules page: each provider gets their own separate <table> stacked one
   after another, not one shared table. Left to the browser's default
   content-based column sizing, each table's columns would end up a
   different width depending on that one provider's own longest
   description, so the Strength/Priority/action columns would drift
   left and right down the page instead of lining up. Fixed layout with
   the same percentages on every table keeps every column at the exact
   same x-position regardless of which provider's table it belongs to. */
.rules-table { table-layout: fixed; }
.rules-table th:nth-child(1), .rules-table td:nth-child(1) { width: 55%; }
.rules-table th:nth-child(2), .rules-table td:nth-child(2) { width: 20%; }
.rules-table th:nth-child(3), .rules-table td:nth-child(3) { width: 10%; }
.rules-table th:nth-child(4), .rules-table td:nth-child(4) { width: 15%; }
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 0.72rem;
  font-weight: 500;
}
.badge.hard { background: var(--danger-bg); color: var(--danger); }
.badge.soft { background: var(--brand-tint); color: var(--brand-dark); }
.badge.pending { background: var(--amber-bg); color: var(--amber); }
.badge.approved { background: var(--brand-tint); color: var(--brand-dark); }
.badge.denied { background: var(--danger-bg); color: var(--danger); }

.provider-thumb {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.provider-photo {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}

.strength-toggle { display: inline-flex; gap: 4px; }
.strength-btn {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--muted);
  padding: 4px 12px;
  font-size: 0.78rem;
  border-radius: 20px;
}
.strength-btn:hover { background: var(--paper); }
.strength-btn.hard.active { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.strength-btn.soft.active { background: var(--brand-tint); color: var(--brand-dark); border-color: var(--brand-dark); }

button, .btn {
  font-family: 'Inter', sans-serif;
  background: var(--brand);
  color: #fff;
  border: none;
  border-radius: 7px;
  padding: 9px 16px;
  font-size: 0.88rem;
  cursor: pointer;
}
.btn { display: inline-block; text-decoration: none; }
button:hover, .btn:hover { background: var(--brand-dark); }
button.ghost { background: transparent; color: var(--muted); border: 1px solid var(--line); }
button.danger { background: transparent; color: var(--danger); border: 1px solid var(--danger-line); padding: 4px 10px; font-size: 0.78rem; }

input, select, textarea {
  font-family: 'Inter', sans-serif;
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 8px 10px;
  font-size: 0.88rem;
  width: 100%;
}
label { font-size: 0.8rem; color: var(--muted); display: block; margin-bottom: 4px; }
.field { margin-bottom: 12px; }
.form-row { display: flex; gap: 12px; }
.form-row .field { flex: 1; }
.time-select-group { display: flex; gap: 4px; }
.time-select-group select { min-width: 0; }
/* Wide gap plus a dividing hairline between shift type / start time /
   end time, so the three read as clearly separate groups rather than
   one continuous row of dropdowns. Each field's own hour/minute/AM-PM
   trio still stays tight (.time-select-group's 4px gap above), only the
   space BETWEEN the three groups needed the emphasis. */
.template-time-row { gap: 40px; }
.template-time-row .field { flex: 0 0 auto; }
.template-time-row .field + .field {
  padding-left: 40px;
  border-left: 1px solid var(--line);
}

/* ---- Build Schedule's two-column area ----
   Below the calendar, the page splits: what the schedule currently looks
   like on the left (totals, rule breaks), what you can do about it on
   the right (save/clear, Generate, and the negotiation chat directly
   under the button that starts it). Everything in here runs a notch
   smaller than the rest of the app, since each column only has half the
   width to work with. Collapses to one column when there isn't room for
   two, the right column's controls come first there, they're the reason
   you're on this page. */
.schedule-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
  font-size: 0.88rem;
}
.schedule-split .card { padding: 16px 18px; }
.schedule-split h2 { font-size: 1.05rem; }
.schedule-split h3 { font-size: 0.9rem; }
.schedule-split table { font-size: 0.82rem; }
.schedule-split th, .schedule-split td { padding: 5px 8px; }
.schedule-split .hint { font-size: 0.78rem; }
.schedule-split button { font-size: 0.82rem; }
/* Wrapping row of related buttons, so Save/Clear/Clear all stay together
   and wrap as a group instead of overflowing a half-width column. */
.button-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
.button-row button, .button-row .btn { margin: 0; }
/* A long negotiation scrolls inside its own column rather than pushing
   the page down indefinitely. This is also what makes the existing
   `thread.scrollTop = thread.scrollHeight` in schedule.html actually do
   something, it was a no-op while the thread had no height limit. */
.schedule-split .chat-thread { max-height: 60vh; overflow-y: auto; }
.schedule-split .msg, .schedule-split .decision-card { max-width: 92%; font-size: 0.85rem; }

@media (max-width: 1100px) {
  .schedule-split { grid-template-columns: 1fr; }
  .schedule-split .schedule-actions { order: -1; }
}

/* ---- Chat ---- */
.chat-thread { display: flex; flex-direction: column; gap: 12px; margin-bottom: 16px; }
.msg {
  max-width: 75%;
  padding: 12px 16px;
  border-radius: 12px;
  font-size: 0.92rem;
  line-height: 1.45;
}
.msg.assistant { background: var(--panel); border: 1px solid var(--line); align-self: flex-start; }
.msg.user { background: var(--brand); color: #fff; align-self: flex-end; }

/* Signature element: the negotiation decision card */
.decision-card {
  align-self: flex-start;
  max-width: 85%;
  background: var(--amber-bg);
  border: 1px solid var(--amber-line);
  border-left: 4px solid var(--amber);
  border-radius: 10px;
  padding: 14px 18px;
  font-size: 0.92rem;
  /* Some messages (the numbered relaxation-options list) are several
     lines joined by \n, a plain div collapses that to one run-on line
     by default, this keeps each line on its own row without needing to
     switch the scheduler's plain-text messages over to HTML. */
  white-space: pre-line;
}
.decision-card .eyebrow {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--amber);
  font-weight: 600;
  margin-bottom: 6px;
}

.chat-input-row { display: flex; gap: 10px; }
.chat-input-row input { flex: 1; }

.thinking-indicator {
  display: flex;
  align-items: center;
  gap: 10px;
  align-self: flex-start;
  max-width: 75%;
  padding: 12px 16px;
  border-radius: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  font-size: 0.92rem;
  color: var(--muted);
}
.thinking-spinner {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  border: 2px solid var(--brand-soft);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: thinking-spin 0.8s linear infinite;
}
@keyframes thinking-spin {
  to { transform: rotate(360deg); }
}

/* ---- Calendar ---- */
.cal-weekday-header {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  margin-bottom: 6px;
}
.cal-weekday-header div {
  text-align: center;
  font-size: 0.7rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
}
.cal-day {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 6px 8px;
  min-height: 70px;
  font-size: 0.7rem;
}
.cal-day-blank { background: transparent; border-color: transparent; }
.cal-day .date-num { font-family: 'IBM Plex Mono', monospace; color: var(--muted); font-size: 0.7rem; }

.cal-nav { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.cal-nav strong { font-family: 'Source Serif 4', serif; }

.provider-cal-day, .viewer-cal-day {
  display: block;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}
.provider-cal-day:hover, .viewer-cal-day:hover { border-color: var(--brand-bright); }
.request-marker {
  margin-top: 4px;
  padding: 2px 5px;
  border-radius: 5px;
  font-size: 0.66rem;
  line-height: 1.3;
}
.request-marker.vacation { background: var(--amber-bg); color: var(--amber); }
.request-marker.want { background: var(--brand-tint); color: var(--brand-dark); }
.request-marker.avoid { background: var(--danger-bg); color: var(--danger); }

/* Viewer Requests page's days-off calendar: unlike .request-marker above
   (one provider's own calendar, at most one entry a day), this spans
   every provider, so each day stacks one small chip per person who's
   asked that date off, color-coded pending vs approved. Reuses
   .cal-legend-swatch's shape from the schedule calendar's facility key
   for the two-item legend above the grid. */
.request-chip {
  margin-top: 3px;
  padding: 2px 5px;
  border-radius: 5px;
  font-size: 0.66rem;
  line-height: 1.3;
}
.request-chip.pending { background: var(--amber-bg); color: var(--amber); }
.request-chip.approved { background: var(--brand-tint); color: var(--brand-dark); }
.cal-legend-swatch.req-pending { background: var(--amber); }
.cal-legend-swatch.req-approved { background: var(--brand-dark); }

/* ---- Master schedule (Build Schedule result): one calendar grid for
   the whole month, not one calendar per facility (three side-by-side
   month calendars meant every date was tiny and split across columns).
   Reuses .cal-weekday-header/.cal-grid/.cal-day from the calendar
   further up, .cal-day just grows to fit its content, a busier day
   makes a taller row, no separate layout needed. Each facility with a
   shift that day gets its own small color-coded block stacked inside
   the cell, sans font (inherited, this app's default), deliberately
   small to fit a full month with every facility visible at once. ---- */
.cal-site-block {
  border-radius: 4px;
  padding: 3px 4px;
  margin-bottom: 3px;
}
.cal-site-block:last-child { margin-bottom: 0; }
.cal-site-name { font-weight: 600; font-size: 0.6rem; line-height: 1.2; margin-bottom: 1px; }
.cal-shift-line { font-size: 0.62rem; line-height: 1.35; }

/* Color key above the grid: solid swatches (not the pale cell tints) so
   the facility-to-color mapping is obvious at a glance before scanning
   any actual dates. */
.cal-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 12px; font-size: 0.8rem; }
.cal-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.cal-legend-swatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }

/* A facility's calendar colour is stored on the facility itself and set
   on the Facilities page, so the swatch, the block tint, and the site
   name are all inline styles built from that one value rather than a
   fixed palette keyed to a facility's position in the list. The chip
   below previews the same pairing on the Facilities page. */
.color-chip {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 0.78rem;
  font-weight: 600;
}
.fac-color-input {
  width: 46px;
  height: 30px;
  padding: 2px;
  margin: 0;
  vertical-align: middle;
}

.hint { color: var(--muted); font-size: 0.85rem; }

/* ---- Home / greeting page ---- */
.greeting-hero { text-align: center; padding: 20px 0 12px; }
.greeting-hero h1 { font-size: 2.1rem; margin: 0; }

/* ---- Your next shift (a provider's Home) ---- */
/* Deliberately compact: one line per shift, the next one a little
   larger than the two behind it. The facility name is coloured inline
   from the facility's own record, the same colour it carries on every
   calendar, so a site is recognisable here without a legend. */
.next-shift-card { padding: 14px 18px; }
.next-shift-card h3 { margin: 0 0 6px; font-size: 0.95rem; color: var(--muted); }
.next-shift-line {
  font-size: 0.86rem;
  color: var(--muted);
  line-height: 1.7;
}
.next-shift-line.first {
  font-size: 1rem;
  color: var(--ink);
}
.next-shift-line .sep { margin: 0 6px; color: var(--line); }
.next-shift-card .hint { margin: 0; }

.month-picker { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.month-chip {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 8px 18px;
  font-size: 0.88rem;
}
.month-chip:hover { background: var(--brand-tint); border-color: var(--brand-bright); }
.month-chip.selected { background: var(--brand); color: #fff; border-color: var(--brand); }

/* Schedule group selector, same chip language as the month picker so
   the two selector rows read as one family. Hidden entirely while only
   one group exists (the partial renders nothing). */
.group-chips { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.group-chip {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 6px 16px;
  font-size: 0.85rem;
  text-decoration: none;
}
.group-chip:hover { background: var(--brand-tint); border-color: var(--brand-bright); }
.group-chip.selected { background: var(--brand); color: #fff; border-color: var(--brand); }

/* Any month beyond the four chips. The trigger is just another chip, the
   grid drops out of it as a popover so the row itself never grows. */
.month-more { position: relative; display: inline-flex; }
.month-pop {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 20;
  width: 240px;
  padding: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.12);
}
.month-pop[hidden] { display: none; }
.month-pop.flip-right { left: auto; right: 0; }
.month-pop-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
  font-size: 0.92rem;
  font-weight: 600;
}
.month-pop-nav {
  background: none;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 1px 11px;
  font-size: 1.05rem;
  line-height: 1.4;
}
.month-pop-nav:hover:not(:disabled) { border-color: var(--brand-bright); }
.month-pop-nav:disabled { opacity: 0.3; cursor: default; }
.month-pop-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.month-pop-cell {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 9px 0;
  font-size: 0.83rem;
}
.month-pop-cell:hover:not(:disabled) { border-color: var(--brand-bright); }
.month-pop-cell:disabled { opacity: 0.28; cursor: default; }
.month-pop-cell.selected { background: var(--brand); color: #fff; border-color: var(--brand); }
/* A published month in the grid: still clickable (the admin keeps the
   right to open and change one), just visibly done rather than pending. */
.month-pop-cell.published { position: relative; }
.month-pop-cell.published::after {
  content: '';
  position: absolute;
  top: 4px;
  right: 5px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand-bright);
}

/* ---- Rule review list (Build Schedule's pre-generate step) ---- */
.rule-review-list { list-style: none; padding: 0; margin: 0 0 16px; }
.rule-review-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}
.rule-review-list li:last-child { border-bottom: none; }

/* ---- Facilities page ---- */
.inline-form { display: flex; gap: 8px; align-items: center; }
.inline-form input { flex: 1; }
.fac-edit { display: none; }
tr.editing .fac-view { display: none; }
tr.editing .fac-edit { display: inline-block; }
.nickname-badge { font-size: 0.78rem; color: var(--muted); }
.template-form { margin-top: 16px; }
.day-checks { display: flex; gap: 12px; flex-wrap: wrap; }
.day-check { display: flex; align-items: center; gap: 4px; font-size: 0.8rem; color: var(--ink); }
.day-check input { width: auto; }

/* A standalone on/off checkbox with its text beside it. Same idea as
   .day-check above, just sized as a normal form label rather than one of
   a tight row of seven. Both exist because inputs are width: 100% by
   default here, which a checkbox should never be. */
.check-field { display: flex; align-items: center; gap: 8px; font-size: 0.88rem; color: var(--ink); }
/* A row of weekday checkboxes (never worked / disliked / preferred).
   The global label rule makes labels block-level, which crammed these
   into a jumble; this lays them out as wrapping pills instead. */
.weekday-picks { display: flex; flex-wrap: wrap; gap: 8px 18px; margin: 6px 0 4px; }
.weekday-picks label { display: inline-flex; align-items: center; gap: 6px;
                       margin: 0; font-size: 0.88rem; color: var(--ink); white-space: nowrap; }
.weekday-picks input { width: auto; margin: 0; }
.check-field input { width: auto; margin: 0; }

.review-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}
.review-row:last-of-type { border-bottom: none; }
.review-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* ---- Manual shift placement (Build Schedule's "place shifts manually"
   step) ---- */
.build-layout { display: flex; gap: 20px; align-items: flex-start; margin-bottom: 20px; }
/* The sidebar sticks alongside the calendar and scrolls on its own, so
   a long provider list can be worked through without moving the grid,
   and the grid can be scrolled without losing the list. Capped to the
   viewport height minus its own offset so the list, not the page, is
   what runs out of room. */
.provider-sidebar {
  width: 190px;
  flex-shrink: 0;
  position: sticky;
  top: 20px;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  padding-right: 6px;
}
.provider-sidebar h3 {
  font-family: 'Inter', sans-serif;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  margin: 14px 0 6px;
}
.provider-sidebar h3:first-of-type { margin-top: 0; }
.provider-chip {
  padding: 6px 10px;
  margin-bottom: 4px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: 0.82rem;
  cursor: grab;
}
.provider-chip:active { cursor: grabbing; }
/* Unavailable providers stay draggable, placing one just asks for
   confirmation the way it always has, but they read as the last resort
   they are. Their title attribute carries the actual reasons. */
.provider-chip.unavailable { opacity: 0.55; }
.build-grid-wrap { flex: 1; min-width: 0; }

.lock-slot {
  border: 1px dashed var(--line);
  border-radius: 4px;
  padding: 3px 4px;
  margin-bottom: 3px;
  font-size: 0.62rem;
  line-height: 1.35;
  cursor: pointer;
  min-height: 26px;
}
.lock-slot:last-child { margin-bottom: 0; }
/* Every slot's background is its facility's colour, set inline in
   schedule_build.html so the value comes from the facility record.
   State lives entirely in the border and text below, which is why
   neither .locked nor .generated sets a background any more. The drop
   target is the one place that has to override the facility shading,
   and an inline style only yields to !important. */
.lock-slot.drag-over {
  border-color: var(--brand-bright) !important;
  background: var(--brand-tint) !important;
}
.lock-slot.locked {
  border-style: solid;
  border-color: var(--brand-dark);
}
/* Not a real lock, just what the last generate happened to place here,
   amber to match this app's existing "provisional, not yet confirmed"
   color elsewhere (badge.pending). Dragging a new provider onto one of
   these replaces it, same as an ordinary drop. */
/* Kept deliberately quiet. Nearly every slot is in this state after a
   generate, so a heavy outline here is the whole grid shouting at once.
   The amber label text below carries the meaning; the border only has
   to hint at it. */
.lock-slot.generated {
  border-style: dashed;
  border-color: var(--amber-line);
  border-width: 1px;
}
.lock-slot.generated .slot-label { color: var(--amber); }
.slot-provider.generated-provider { color: var(--amber); font-weight: 600; }
.lock-slot.selected {
  border-style: solid;
  border-width: 3px;
  border-color: var(--brand);
  padding: 1px 2px;
}
.cal-day.day-selected {
  border-width: 3px;
  border-color: var(--brand-soft);
  padding: 4px 6px;
}
.lock-slot .slot-label { font-weight: 600; color: var(--muted); }
.lock-slot.locked .slot-label { color: var(--brand-dark); }
.lock-slot .slot-provider { display: flex; justify-content: space-between; align-items: center; margin-top: 1px; }
.lock-remove { cursor: pointer; color: var(--danger); font-weight: 700; padding: 0 3px; }
.review-actions button { font-size: 0.78rem; padding: 6px 10px; }

/* ---- Instructions page ----
   The one long-form reading page in this app, so it gets typography the
   rest of the UI doesn't need: a readable measure, real spacing between
   sections, and a few callout styles for the things worth stopping on.
   Every colour here is already in the palette above. */
.doc { max-width: 780px; }
.doc h2 {
  margin-top: 34px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--brand-soft);
}
.doc h2:first-of-type { margin-top: 0; }
.doc h3 { margin-top: 22px; font-size: 1rem; }
.doc p, .doc li { line-height: 1.6; }
.doc ul, .doc ol { padding-left: 22px; margin: 10px 0; }
.doc li { margin-bottom: 7px; }
.doc li > strong:first-child { color: var(--ink); }
.doc code {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85em;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 5px;
}

/* Step numbers for the walkthrough. */
.doc-steps { list-style: none; padding-left: 0; counter-reset: step; }
.doc-steps > li {
  counter-increment: step;
  position: relative;
  padding-left: 40px;
  margin-bottom: 16px;
}
.doc-steps > li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Callouts. Green for a useful habit, amber for a caution, grey for an
   aside. The left border does the signalling, the tint keeps it calm. */
.callout {
  border-left: 4px solid var(--line);
  background: var(--paper);
  border-radius: 0 8px 8px 0;
  padding: 12px 16px;
  margin: 14px 0;
  font-size: 0.9rem;
}
.callout strong:first-child { display: block; margin-bottom: 3px; }
.callout.tip { border-left-color: var(--brand); background: var(--brand-tint); }
.callout.tip strong:first-child { color: var(--brand-dark); }
.callout.warn { border-left-color: var(--amber); background: var(--amber-bg); }
.callout.warn strong:first-child { color: var(--amber); }

/* Inline pills for the hard/soft distinction, which comes up constantly. */
.pill {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-radius: 999px;
  padding: 2px 9px;
  vertical-align: 1px;
}
.pill.hard { background: var(--danger-bg); color: var(--danger); }
.pill.soft { background: var(--brand-tint); color: var(--brand-dark); }

/* Contents list at the top, so a long page is still navigable. */
.doc-toc { columns: 2; column-gap: 26px; margin: 0; padding-left: 18px; }
.doc-toc li { margin-bottom: 5px; }
@media (max-width: 700px) { .doc-toc { columns: 1; } }

/* Rule conflict warnings (hard versus hard, see rule_conflicts() in
   ed_scheduler.py). The flag is TEXT on white and on --danger-bg, so it
   uses --danger, never --accent, see the palette split in the header
   comment. A .conflict-row tint marks both sides of a conflict wherever
   rules are listed, so a conflict created despite the warning stays
   easy to find and correct. */
.conflict-flag { color: var(--danger); font-weight: 600; font-size: 13px; margin-top: 4px; }
tr.conflict-row > td { background: var(--danger-bg); }
li.conflict-row { background: var(--danger-bg); border-radius: 6px; }
.msg.conflict { color: var(--danger); font-weight: 600; border-color: var(--danger); }

/* Manual-only scheduling (Provider.manual_only): every non-profile card
   on the provider page goes inert while the flag is on, since those
   settings are ignored by the generator until it is turned off. Dimmed
   AND click-proof, not just faded, so a value can't be edited into a
   panel that will not be read. */
.card.manual-dimmed { opacity: 0.45; pointer-events: none; user-select: none; }

/* The Listed shifts panel's date picker: shifts grouped by facility,
   each group tinted and edged with the facility's own calendar colour
   (background at hex alpha 14, same low-opacity trick the calendars
   use), the abbreviation as the group heading, shift chips inline.
   Deliberately PLAIN inline-block flow with explicit margins, no flex
   and no gap: the first flex version rendered differently in Safari
   than Chromium (checkboxes crowding into their labels), and a
   checkbox row has nothing to gain from flex. The global label rule
   (block, muted, 0.8rem) is fully overridden here, the same lesson
   .weekday-picks already learned. */
.listed-fac-group { border-left: 4px solid; border-radius: 6px;
                    padding: 8px 12px; margin: 8px 0; }
.listed-fac-name { font-weight: 700; display: inline-block; min-width: 42px;
                   margin-right: 12px; font-size: 14px; }
.listed-chip { display: inline-block; white-space: nowrap; margin: 3px 18px 3px 0;
               font-size: 13.5px; color: var(--text); }
.listed-chip input { margin: 0 6px 0 0; vertical-align: -2px; }
.listed-any { display: inline-block; margin: 4px 0 6px; font-weight: 600; }

/* The pin action on a generated-but-unlocked placement slot: lock the
   provider in place with one click. Sits next to the existing clear
   (the reused .lock-remove), quiet until hovered like the rest of the
   grid's per-slot controls. */
.slot-pin { cursor: pointer; opacity: 0.45; margin-left: 4px; font-size: 12px; }
.slot-pin:hover { opacity: 1; }


/* Shift equity: a cell more than one shift above the group average is
   provisional-amber, the outlier signal; the average row is quiet. */
.equity-high { background: var(--amber-bg); color: var(--amber); font-weight: 600; }
.equity-avg td { color: var(--muted); font-style: italic; border-top: 2px solid var(--line); }

/* Assistant style selector (the small row under the plain language
   box). Quiet on purpose: a setting, not a feature. */
.comm-style-row {
  display: flex; align-items: center; gap: 8px;
  margin-top: 10px; flex-wrap: wrap;
}
.comm-style-row select {
  font-size: 0.82rem; padding: 3px 6px;
}

/* The Schedules hub (admin only): every nearby month with its state,
   one action row for the selected month. The dot carries the status,
   published brand green, draft amber, open grey outline. */
.hub-strip { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.hub-month {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px; border: 1px solid var(--line);
  border-radius: 999px; text-decoration: none;
  color: var(--ink); font-size: 0.88rem; background: var(--panel);
}
.hub-month:hover { border-color: var(--brand); }
.hub-month.selected { border: 2px solid var(--brand); font-weight: 600; }
.hub-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.hub-dot.published { background: var(--brand); }
.hub-dot.draft { background: var(--amber); }
.hub-dot.open { background: transparent; border: 1.5px solid var(--muted); }
.hub-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hub-actions .hint { margin: 0; }

/* ---- UX overhaul (the review, approved 2026-08-13) ---- */

/* Finding 1: grouped sidebar sections. */
.sidebar .nav-section {
  font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: #7E92C0; font-weight: 700; margin: 14px 0 2px; padding: 0 2px;
}
.sidebar.collapsed .nav-section { display: none; }

/* The one toast, top right, every page. */
#toastHost {
  position: fixed; top: 18px; right: 18px; z-index: 1000;
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toast {
  background: var(--brand-dark); color: #fff; padding: 9px 16px;
  border-radius: 8px; font-size: 0.88rem; box-shadow: 0 6px 18px rgba(15,23,42,.25);
  opacity: 0; transform: translateY(-6px); transition: all .25s ease;
  max-width: 340px;
}
.toast.show { opacity: 1; transform: none; }
.toast.error { background: var(--danger); }

/* The shared page tab component (findings 2, 3, 4, 6): one visual
   language for splitting a crowded page into jobs. Tabs are buttons
   toggling .tab-panel visibility client side; the active tab carries
   the brand underline. A .tab-count badge goes red when its panel
   holds problems (the rule breaks tab). */
.page-tabs {
  display: flex; gap: 2px; border-bottom: 2px solid var(--line);
  margin: 6px 0 16px; flex-wrap: wrap;
}
.page-tab {
  border: 0; background: none; cursor: pointer;
  padding: 8px 16px; font-size: 0.95rem; font-weight: 600;
  color: var(--muted); border-radius: 8px 8px 0 0; font-family: inherit;
}
.page-tab:hover { color: var(--brand); background: var(--brand-tint); }
.page-tab.active {
  color: var(--brand); background: var(--brand-tint);
  box-shadow: inset 0 -2px 0 var(--brand-bright);
}
.page-tab .tab-count {
  background: var(--line); color: var(--muted); border-radius: 999px;
  font-size: 0.72rem; padding: 1px 7px; margin-left: 6px; font-weight: 700;
}
.page-tab .tab-count.alert { background: var(--danger-bg); color: var(--danger); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Sticky table headers for the long listings. */
.sticky-head thead th {
  position: sticky; top: 0; background: var(--panel); z-index: 2;
  box-shadow: 0 1px 0 var(--line);
}

/* The status ribbon (finding 4): month, state, and the primary
   actions in one glance at the top of Build Schedule. */
.status-ribbon {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; background: var(--brand-tint);
  border-radius: var(--radius); padding: 12px 16px; margin-bottom: 16px;
}
.status-ribbon .left, .status-ribbon .right {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.status-ribbon h2 { margin: 0; }

/* Home's needs attention strip (finding 5). */
.attention-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px; margin-bottom: 20px;
}
.attention-card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 10px 14px; text-decoration: none;
  color: var(--ink); display: block;
}
.attention-card:hover { border-color: var(--brand-bright); }
.attention-card .big { font-weight: 700; font-size: 1.02rem; display: block; }
.attention-card .sub { color: var(--muted); font-size: 0.82rem; }
.attention-card.alert { border-color: var(--danger-line); }
.attention-card.alert .big { color: var(--danger); }
.attention-card.good .big { color: var(--brand); }

/* Provider page (finding 2): sticky identity header and a header row
   that never wraps into broken lines at narrow widths. */
.provider-head {
  position: sticky; top: 0; z-index: 20; background: var(--paper);
  padding: 10px 0; margin-bottom: 8px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.provider-head h1 { margin: 0; font-size: 1.5rem; }
.provider-head .nav-cluster {
  display: flex; align-items: center; gap: 8px; white-space: nowrap;
}
.mode-chip {
  display: inline-block; padding: 3px 11px; border-radius: 999px;
  font-size: 0.75rem; font-weight: 700; vertical-align: middle;
}
.mode-chip.auto { background: var(--brand-tint); color: var(--brand); }
.mode-chip.manual { background: var(--danger-bg); color: var(--danger); }
.mode-chip.listed { background: var(--amber-bg); color: var(--amber); }
.account-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--brand-bright); vertical-align: middle; margin-left: 6px;
}

/* Danger keeps its distance: destructive controls sit apart and read
   as ghost red until hovered. */
.button-row .danger-side { margin-left: auto; }

/* The status ribbon's More menu: a native details/summary dropdown for
   the secondary actions (save, clear, undo), so the ribbon itself stays
   two or three buttons wide. */
.ribbon-more { position: relative; display: inline-block; }
.ribbon-more summary { list-style: none; cursor: pointer; }
.ribbon-more summary::-webkit-details-marker { display: none; }
.ribbon-more .more-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  background: var(--card-bg, #fff); border: 1px solid var(--line, #d8dee9);
  border-radius: 8px; padding: 8px; min-width: 230px;
  box-shadow: 0 6px 18px rgba(16, 24, 48, 0.12);
  display: flex; flex-direction: column; gap: 6px;
}
.ribbon-more .more-menu button { width: 100%; text-align: left; }

/* The Rules this month tab's per-rule controls (Mike's ask, 2026-08-13):
   strength, priority, edit, delete, grouped to the right of each row and
   sized down so four controls fit without crowding the description. */
.rule-review-actions { display: flex; align-items: center; gap: 6px; flex: none; }
.rule-review-actions .review-priority {
  padding: 2px 4px; font-size: 0.78rem; border: 1px solid var(--line);
  border-radius: 6px; background: var(--panel);
}
.rule-review-actions .review-edit,
.rule-review-actions .review-delete {
  padding: 3px 9px; font-size: 0.78rem;
}

/* Collapsible provider sections in the Rules this month tab (Mike's ask,
   2026-08-13): a clickable name row that opens to reveal that person's
   rules, so the list stays short and searchable. A group with a conflict
   renders open, and its count badge goes red. */
.rule-group { border-bottom: 1px solid var(--line); }
.rule-group > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  gap: 10px; padding: 10px 4px; font-weight: 600; user-select: none;
}
.rule-group > summary::-webkit-details-marker { display: none; }
.rule-group > summary::before {
  content: "\25B8"; color: var(--muted); font-size: 0.8rem;
  transition: transform 0.15s ease;
}
.rule-group[open] > summary::before { transform: rotate(90deg); }
.rule-group > summary:hover { color: var(--brand); }
.rule-group .rule-group-count {
  margin-left: auto; background: var(--brand-tint); color: var(--brand);
  border-radius: 10px; padding: 1px 9px; font-size: 0.76rem; font-weight: 700;
}
.rule-group .rule-group-count.conflict { background: var(--danger-bg); color: var(--danger); }
.rule-group .rule-review-list { margin: 0 0 8px; padding-left: 22px; }

/* Rule category subheadings (Mike's ask, 2026-08-13): a provider's
   rules are grouped into labeled sections so a long list reads as a
   few intuitive groups instead of one jumble. Used in the provider
   page's All rules table (a full-width row) and the Build Schedule
   review list (a list item). */
.rule-cat-row td {
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted);
  background: var(--brand-tint); padding: 5px 8px;
}
.rule-review-list .rule-cat-label {
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted);
  padding: 8px 0 2px; border-bottom: none; display: block;
}

/* ---- PWA install hint ---- */
/* A single dismissible banner offering "add to home screen." Hidden
   until the .show class is set by the script in base.html, and never
   shown once the app is already installed. Navy to match the app
   chrome and the icon. */
.install-banner {
  position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 60;
  display: none; align-items: center; gap: 10px;
  background: var(--brand-dark); color: var(--sidebar-ink);
  border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 12px;
  padding: 10px 12px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  font-size: 0.9rem;
}
.install-banner.show { display: flex; }
.install-icon { width: 34px; height: 34px; border-radius: 8px; flex: none; }
.install-msg { flex: 1; line-height: 1.35; }
.install-btn {
  flex: none; font: inherit; font-weight: 600; cursor: pointer; color: #fff;
  background: var(--brand); border: none; border-radius: 8px; padding: 8px 14px;
}
.install-close {
  flex: none; background: none; border: none; color: var(--sidebar-link);
  font-size: 1.4rem; line-height: 1; cursor: pointer; padding: 0 4px;
}
@media (min-width: 720px) {
  .install-banner { left: auto; right: 16px; bottom: 16px; max-width: 380px; }
}

/* ---- Group batch volume preview (plain-language box) ---- */
.batch-preview { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 0.9rem; }
.batch-preview th, .batch-preview td { text-align: left; padding: 4px 10px; border-bottom: 1px solid var(--line); }
.batch-preview thead th { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.batch-preview td:nth-child(2), .batch-preview td:nth-child(3) { font-variant-numeric: tabular-nums; }

/* The assistant's proposal card is EDITABLE (Mike's ask, 2026-08-14): each
   suggested rule sits on one row with its own Strength and Priority
   controls and a Drop button, so a suggestion can be corrected before it
   is applied rather than accepted whole or retyped. */
.asst-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 4px 0;
}
.asst-row > span:first-child { flex: 1 1 260px; }
.asst-row select { width: auto; padding: 2px 6px; font-size: 0.9em; }
.asst-row button { padding: 2px 10px; font-size: 0.9em; }

/* The batch preview is editable (Mike's ask, 2026-08-14): correct any After
   value in place, or X a provider out of the change entirely. A dropped row
   stays visible and struck through rather than disappearing, so the batch
   you are about to apply always reads as the whole group minus what you
   removed, and the X flips to an undo arrow. */
.batch-preview input.batch-after {
  width: 68px;
  padding: 2px 6px;
  font-weight: 600;
}
.batch-preview button.batch-drop {
  padding: 0 8px;
  font-size: 1.05em;
  line-height: 1.4;
}
.batch-preview tr.batch-dropped td {
  opacity: 0.45;
  text-decoration: line-through;
}
.batch-preview tr.batch-dropped td:last-child {
  opacity: 1;
  text-decoration: none;
}

/* ---- Contact the developers ----
   The page is deliberately lopsided: one strong call to action for the
   conversation (which IS the developer's intake interview) and a quiet
   link for the written form, so the better path is the obvious one. */
.contact-start { display: flex; gap: 8px; align-items: center; }
.contact-start input[type="text"] { flex: 1; }

.contact-list { list-style: none; padding: 0; margin: 0; display: flex;
                flex-direction: column; gap: 14px; }
.contact-item { border: 1px solid var(--line); border-radius: 10px;
                padding: 12px 14px; background: var(--panel); }
/* A plan reads differently from a fault, so it looks different. */
.contact-item.is-plan { border-left: 3px solid var(--brand); }
.contact-head { display: flex; align-items: center; gap: 8px;
                justify-content: space-between; }
.contact-title { font-weight: 600; }
.contact-meta { font-size: 0.82rem; color: var(--muted); margin-top: 2px; }
.contact-body { margin-top: 8px; font-size: 0.92rem; line-height: 1.5;
                white-space: pre-wrap; }
/* The plain language note on a resolved item. This is the thing that
   makes checking back worth doing, so it gets the affirmative colour. */
.contact-note { margin-top: 8px; padding: 10px 12px; border-radius: 8px;
                background: var(--brand-tint); border: 1px solid var(--line);
                font-size: 0.92rem; line-height: 1.5; white-space: pre-wrap; }
.contact-plan { margin: 10px 0 0; font-size: 0.9rem; }
.contact-plan dt { font-weight: 600; margin-top: 6px; }
.contact-plan dd { margin: 2px 0 0; padding-left: 12px; color: var(--text);
                   white-space: pre-wrap; }

.proposal-card { margin: 12px 0; padding: 12px 14px; border-radius: 10px;
                 background: var(--amber-bg); border: 1px solid var(--amber-line); }
.proposal-actions { display: flex; gap: 8px; margin-top: 12px; }

/* The schedule hub's month picker: a dropdown rather than a wrapping row
   of chips, which crowded the page above the calendar you came to read. */
.hub-picker { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.hub-picker label { margin: 0; font-size: 0.85rem; color: var(--muted); }
.hub-picker select { max-width: 280px; }

/* One small select rather than two, since length and wording are not
   decisions anybody makes separately. */
.comm-style-select { max-width: 210px; font-size: 0.85rem; padding: 4px 8px; }

/* Home: a quiet greeting line rather than a hero, so the attention strip
   leads. And a verdict under a shortfall, since a number alone leaves
   the reading to the person. */
.greeting-line { font-size: 1.05rem; color: var(--muted); margin: 0 0 14px; }
.capacity-verdict { margin: 2px 0 0; line-height: 1.5; }
.capacity-verdict strong { color: var(--danger); }
