/* do.not.omit.me — hand-rolled styles, no framework.
   Design rule: the grid is the only colored thing on the page; all chrome is
   monochrome. Type is Avenir Next (Apple platforms) with graceful fallback. */

:root {
  color-scheme: light;
  --bg: #fafafa;
  --surface: #ffffff;
  --ink: #191919;
  --on-ink: #ffffff;  /* text on --ink-colored surfaces (buttons) */
  --ink-hover: #3a3a3a;
  --muted: #6e6e6e;
  --line: #dcdcdc;
  --off: #f0f0ee;
  --disabled: #c6c8cd;
  --faded: #b5b5b5;   /* grayed-out participants on hover */
  /* The only color on the page lives in grid cells: */
  --paint-yes: #2f6f4f;
  --paint-maybe: #e0a63c;
  --heat-1: #e8f2ec;
  --heat-2: #d2e6da;
  --heat-3: #bcdac9;
  --heat-4: #a5ceb7;
  --heat-5: #8bbfa2;
  --heat-6: #6fae8b;
  --heat-7: #4f9770;
  --chosen: #16a34a; /* bright green ring on the finalized cell */
  --radius: 10px;
  font-size: 16px;
}

/* Dark palette (issue 037). Two triggers, one look: a signed-in user's
   explicit choice (data-theme stamped server-side, so the first paint is
   already dark — no flash) or an OS dark preference with no explicit
   choice. KEEP THE TWO TOKEN BLOCKS IDENTICAL. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #1d1d20;
  --surface: #26262a;
  --ink: #d8d8d8;
  --on-ink: #1d1d20;
  --ink-hover: #bfbfbf;
  --muted: #96969b;
  --line: #3d3d42;
  --off: #313136;
  --disabled: #4f4f55;
  --faded: #606066;
  --paint-yes: #4c8a66;
  --paint-maybe: #b98c3a;
  --heat-1: #2b352f;
  --heat-2: #2f3d35;
  --heat-3: #34473c;
  --heat-4: #3a5244;
  --heat-5: #405e4c;
  --heat-6: #466b55;
  --heat-7: #47795c;
  --chosen: #22c55e; /* bright green ring on the finalized cell */
}
:root[data-theme="dark"] .banner-light { display: none; }
:root[data-theme="dark"] .banner-dark { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #1d1d20;
    --surface: #26262a;
    --ink: #d8d8d8;
    --on-ink: #1d1d20;
    --ink-hover: #bfbfbf;
    --muted: #96969b;
    --line: #3d3d42;
    --off: #313136;
    --disabled: #4f4f55;
    --faded: #606066;
    --paint-yes: #4c8a66;
    --paint-maybe: #b98c3a;
    --heat-1: #2b352f;
    --heat-2: #2f3d35;
    --heat-3: #34473c;
    --heat-4: #3a5244;
    --heat-5: #405e4c;
    --heat-6: #466b55;
    --heat-7: #47795c;
    --chosen: #22c55e; /* bright green ring on the finalized cell */
  }
  :root:not([data-theme="light"]) .banner-light { display: none; }
  :root:not([data-theme="light"]) .banner-dark { display: block; }
  :root:not([data-theme="light"]) .finalized-banner { background: var(--heat-4); }
}

/* The header banner ships in light and dark variants; CSS above picks one.
   Scoped under .brand so this outranks the generic .brand img display rule. */
.brand .banner-dark { display: none; }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Avenir Next", Avenir, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.5;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1;
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
  padding: 1.5rem 1rem 3rem;
}

/* Header / footer */
.site-header {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1rem 1.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

.brand img {
  height: 1.9rem;
  width: auto;
  max-width: 100%;
  display: block;
}

.tagline { color: var(--muted); font-size: 0.9rem; }

.site-nav { margin-left: auto; display: flex; gap: 1rem; align-items: baseline; }
.site-nav a { color: var(--ink); font-size: 0.9rem; }

/* Auth & dashboard */
.auth-card { max-width: 30rem; }
.auth-links { margin-bottom: 0; }
.auth-links a { color: var(--ink); }

.poll-list { list-style: none; margin: 0; padding: 0; }
.poll-list li {
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  flex-wrap: wrap;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--line);
}
.poll-list li:last-child { border-bottom: none; }
.poll-list a { color: var(--ink); overflow-wrap: anywhere; }
.poll-dates { margin-left: auto; color: var(--muted); font-size: 0.9rem; }
/* "Create new poll" reuses a poll's participants (issue 114): a small
   secondary action at the end of a created-poll row. */
.poll-clone { font-size: 0.9rem; white-space: nowrap; }

/* Completion count chip on dashboard rows: the invitee ratio ("3/6") for
   invited polls, a plain count otherwise (issues 034/078). */
.poll-count {
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font-size: 0.8rem;
  padding: 0 0.55rem;
  white-space: nowrap;
}
/* Uninvited completers ride outside the pill ("+ 2", issue 078); pull it
   toward the pill it annotates against the row's flex gap. */
.poll-extra {
  font-size: 0.8rem;
  margin-left: -0.35rem;
  white-space: nowrap;
}
.poll-list li.past .poll-count { color: var(--muted); }
/* A poll whose last offered date has passed reads as done — mute the whole
   row, including the title link and its date. */
.poll-list li.past,
.poll-list li.past a,
.poll-list li.past .poll-dates { color: var(--muted); }

.site-footer {
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.85rem;
  text-align: center;
}

.site-footer a { color: var(--ink); }

/* First-visit privacy note (issue 094, replacing the issue-061 banner): a
   quiet line in the footer's normal flow — no bar, shadow, or dismiss
   control. Rendered with the hidden attribute and revealed by app.js only on
   a visitor's first page view; footer text styles do the rest. */
.privacy-note { margin: 0.35rem 0 0; }

/* Hero */
.hero { text-align: center; margin: 2.5rem auto 2rem; max-width: 40rem; }
.hero h1 { font-size: 2rem; margin: 0 0 0.5rem; }
.hero em { font-style: normal; text-decoration: underline; text-underline-offset: 0.2em; }
.hero p { color: var(--muted); margin: 0; }

/* Three-step "how it works" list under the hero heading (issue 091). */
.hero-steps {
  display: grid;
  gap: 0.6rem;
  margin: 1.25rem auto 0;
  padding-left: 1.5rem;
  max-width: 24rem;
  text-align: left;
}
.hero-steps li::marker { color: var(--muted); font-weight: 600; }
.hero-steps .step-desc { display: block; color: var(--muted); }

/* Cards & forms */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
  margin: 0 auto 1.5rem;
}

.field { margin-bottom: 1rem; }

.field label {
  display: block;
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 0.3rem;
}

.field .hint { color: var(--muted); font-weight: 400; }

/* Honeypot decoy: kept in the layout but off-screen so real users never see or
   fill it, while bots that fill every field give themselves away (issue 072).
   Positioned off-screen rather than display:none so field-filling bots that
   skip hidden inputs still trip it. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

input[type="text"], input[type="email"], input[type="password"], input[type="date"],
select, textarea {
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
  background: var(--surface);
  color: var(--ink);
}

input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 1px;
}

.row { display: flex; gap: 1rem; flex-wrap: wrap; }
.row > .field { flex: 1 1 10rem; margin-bottom: 0; }

/* The organizer identity row sits between the paint grid and the submit
   button (issue 093), so it carries its own field-sized gap below. */
.identity-row { margin-bottom: 1rem; }

/* Grouped daily time window (issue 091): one heading over a From/to pair of
   selects, each keeping its own small inline label for accessibility. */
.field-timewindow { flex: 2 1 16rem; }
.field .field-label {
  display: block;
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 0.3rem;
}
.time-range { display: flex; align-items: center; gap: 0.5rem; }
.time-range label {
  display: inline;
  font-weight: 400;
  font-size: inherit;
  margin: 0;
  color: var(--muted);
}
.time-range select { width: auto; flex: 1 1 auto; min-width: 0; }

button, .btn {
  display: inline-block;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--ink);
  border-radius: 6px;
  background: var(--ink);
  color: var(--on-ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

button:hover, .btn:hover { background: var(--ink-hover); }

button:disabled, .btn:disabled {
  background: var(--off);
  border-color: var(--line);
  color: var(--muted);
  cursor: not-allowed;
}

/* Card heading with an action (e.g. the dashboard's create-poll button). */
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.card-head h2 { margin: 0; }

.hero .cta { margin-top: 1.25rem; }

/* Prominent dashboard notice (e.g. the resume-emails control). */
.notice { border-color: var(--ink); }
.notice p { margin: 0 0 0.75rem; }

.btn-secondary {
  background: var(--surface);
  color: var(--ink);
}

.btn-secondary:hover { background: var(--off); }

.errors {
  background: var(--surface);
  border: 2px solid var(--ink);
  border-radius: 6px;
  font-weight: 600;
  padding: 0.6rem 0.9rem;
  margin-bottom: 1rem;
}

.errors ul { margin: 0; padding-left: 1.2rem; }

.notice {
  background: var(--off);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 0.6rem 0.9rem;
  margin-bottom: 1rem;
}

/* Draws the eye to the account fields when an anonymous visitor hits their
   per-browser poll limit and can create an account to make more. */
.field-highlight input {
  border-color: var(--paint-maybe);
  box-shadow: 0 0 0 2px var(--paint-maybe);
}

.account-nudge p { margin: 0 0 0.6rem; }
.account-nudge p:last-child { margin-bottom: 0; }

.anon-identity { margin: 0 0 1rem; }
.anon-identity .inline-form { margin-left: 0.35rem; }

.anon-poll-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.anon-poll-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.4rem 0;
  border-top: 1px solid var(--line);
}

.anon-poll-list .anon-poll-title {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.anon-poll-list form { margin: 0; }

.muted { color: var(--muted); }
.form-actions { margin-top: 1.25rem; }

/* Plan price beside a tier heading on /pro. */
.price {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--muted);
}

/* Create form */
.create-form { max-width: 60rem; }

.check-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 0.9rem;
}
.check-label input[type="checkbox"] { width: auto; margin: 0; }

/* Admin: results-visibility + notify lines in the share card */
.results-visibility, .notify-setting { margin-top: 0.75rem; font-size: 0.9rem; }
.notify-setting { margin-top: 0.25rem; }

/* Disabled pro-only affordances keep their explanatory tooltip. */
.check-disabled, .linklike:disabled { color: var(--muted); cursor: not-allowed; }
.linklike:disabled { text-decoration: none; background: none; border: none; }

/* Date picker */
/* Last-resort containment: at target phone widths a month fits outright;
   if an extreme font/width combination makes one wider, the picker scrolls
   inside its own box instead of widening the page. */
.datepicker { overflow-x: auto; }

.dp-nav { display: flex; justify-content: space-between; margin-bottom: 0.5rem; }

.dp-nav button {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line);
  padding: 0.25rem 0.7rem;
  font-weight: 500;
}

.dp-nav button:hover { background: var(--off); }
.dp-nav button:disabled { opacity: 0.4; cursor: default; background: var(--surface); }

.dp-months { display: flex; gap: 1.25rem; flex-wrap: wrap; }

.month { border-collapse: collapse; }

.month caption { font-weight: 600; padding-bottom: 0.35rem; }

.month th {
  font-size: 0.7rem;
  color: var(--muted);
  font-weight: 500;
  padding: 0.15rem;
}

.month td { padding: 1px; }

.month button {
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--ink);
  font-weight: 400;
}

.month button:hover:not(:disabled) { border-color: var(--ink); background: none; }

.month button:disabled { color: var(--disabled); cursor: default; }

.month button.selected {
  background: var(--ink);
  color: var(--on-ink);
  font-weight: 600;
}

/* Finger-sized picker targets (issue 106): 2.1rem day buttons are well
   under the ~44px guideline, and a mis-tapped meeting date is worse than a
   mis-tapped grid cell. A wide month scrolls inside .datepicker's own box
   (its existing overflow-x) rather than widening the page. */
@media (pointer: coarse) {
  .month button { width: 2.75rem; height: 2.75rem; }
  .dp-nav button { padding: 0.55rem 1rem; }
}

/* Link boxes (admin page) */
.linkbox { display: flex; gap: 0.5rem; }
.linkbox input { flex: 1; min-width: 0; color: var(--muted); }

/* Poll page */
.event-head { margin: 1rem 0 1.5rem; }
.event-head h1 { margin: 0 0 0.25rem; overflow-wrap: anywhere; }
.event-head p { margin: 0.15rem 0; }
.join-card { max-width: 46rem; }

.tz-note { margin-top: 0.5rem; font-size: 0.9rem; }
/* min(): the zone list's long names never push past a narrow viewport. */
.tz-note select { width: auto; max-width: min(16rem, 100%); padding: 0.2rem 0.4rem; font-size: 0.85rem; }

/* Availability grid */
.grid-head { display: flex; justify-content: space-between; align-items: baseline; flex-wrap: wrap; gap: 0.5rem; }
.grid-head h2 { margin: 0; }

.inline-form { display: inline; }

.linklike {
  background: none;
  border: none;
  color: var(--ink);
  padding: 0;
  font: inherit;
  font-size: 0.85rem;
  text-decoration: underline;
  cursor: pointer;
}

.linklike:hover { background: none; color: var(--muted); }

.paint-controls { display: flex; align-items: center; gap: 1rem; margin: 0.75rem 0 0.25rem; flex-wrap: wrap; }

/* Sticky paint controls (issue 102): a long grid scrolls the mode toggle and
   save status out of reach — and a failed save out of sight — so the bar pins
   to the viewport top and the grid's date headers pin just below it. The
   header offset is the bar's live height (a CSS variable measured by app.js:
   the bar wraps at narrow widths and scales with font size, so no constant
   is safe). app.js stamps .pc-sticky, so a script-less page — where grids
   are inert anyway — keeps plain in-flow controls. Margins become padding
   while pinned: an opaque box, no see-through gaps against the headers. */
.paint-controls.pc-sticky {
  position: sticky;
  top: 0;
  z-index: 3; /* over the date headers' z-index 2 */
  background: var(--surface);
  margin: 0;
  padding: 0.75rem 0 0.35rem;
}

/* Wraps so the grid card never gets a rigid min-width (huge fonts / tiny
   screens stack the two mode buttons instead of overflowing). */
.mode-toggle { display: inline-flex; flex-wrap: wrap; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }

.mode-btn {
  border: none;
  border-radius: 0;
  background: var(--surface);
  color: var(--muted);
  font-weight: 600;
  padding: 0.4rem 0.9rem;
}

.mode-btn:hover { background: var(--off); }
.mode-btn.active { background: var(--ink); color: var(--on-ink); }

#save-status { font-size: 0.85rem; }

/* The grid (issue 039): every date is its own single-column table
   (.day-col) and the .slot-grid flex container wraps them into stacked
   blocks. How many columns share a block is pure geometry — container
   width ÷ column width — and the column width is in rem, so the count
   follows the viewer's font size as well as their screen: one column on
   a tiny screen or huge font, many on a wide desktop. No breakpoints,
   no JS, no horizontal scrolling.

   The grid is never capped or inner-scrolled (issue 060): it renders at
   full height and the page scrolls, so the whole grid is reachable by
   normal page scroll. The date headers stay sticky, so they now pin to
   the viewport top while scrolling a long grid. */
.slot-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  row-gap: 1.25rem;      /* wrapped rows read as separate blocks */
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none; /* the paint long-press (issue 101) must not
                                  pop iOS's copy/share callout */
}

/* Adjacent columns overlap borders by 1px so a block reads as one table. */
.day-col {
  table-layout: fixed;
  width: 5.5rem;
  border-collapse: separate;
  border-spacing: 0;
  margin-right: -1px;
}

.day-col th { border: 1px solid var(--line); }
.day-col td { border: 1px solid var(--line); border-top: none; }

.day-col thead th {
  position: sticky;
  /* Sit below the pinned paint controls when the card has them (issue 102);
     grids without controls — the heatmap, finalized grids — keep top: 0. */
  top: var(--paint-controls-h, 0px);
  z-index: 2;
  background: var(--surface);
  font-size: 0.75rem;
  padding: 0.35rem 0.25rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Grid cells — the only colored pixels on the page. */
td.slot, td.heat {
  height: 1.4rem;
  background: var(--off);
  font-size: 0.7rem;
  text-align: center;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  padding: 0 0.3rem;
}

/* pan-y, not none (issue 101): a long grid fills most of a phone screen, so
   a vertical swipe over cells must stay a native page scroll. app.js engages
   painting by tap or long-press and blocks the pan only while painting. */
td.slot { cursor: pointer; touch-action: pan-y pinch-zoom; }

td.slot.lvl-2 { background: var(--paint-yes); color: #fff; }
td.slot.lvl-1 { background: var(--paint-maybe); color: #fff; }

td.slot.off, td.heat.off {
  background: var(--surface);
  cursor: default;
  touch-action: auto;
}

/* Finger-sized cells (issue 101): 1.4rem is about half the recommended touch
   target. Coarse pointers get taller rows on the paint grids and the heatmap
   alike, so the response and results grids stay visually aligned. */
@media (pointer: coarse) {
  td.slot, td.heat { height: 1.6rem; }
}

/* Wrapping happens between .legend-item pairs, never inside one (issue
   105): item spacing rides the flex gap instead of per-swatch margins,
   which broke a swatch away from its own label at narrow widths. */
.legend { font-size: 0.85rem; display: flex; align-items: center; gap: 0.4rem 0.75rem; flex-wrap: wrap; }
.legend-item { white-space: nowrap; }

.swatch {
  display: inline-block;
  width: 0.9rem;
  height: 0.9rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--off);
}
.swatch.lvl-2 { background: var(--paint-yes); }
.swatch.lvl-1 { background: var(--paint-maybe); }
.swatch.off { background: var(--surface); }

/* Heatmap */
.heatmap-card { margin-top: 1.5rem; }

/* The .swatch variants feed the intensity legend (issue 103). */
td.heat-1, .swatch.heat-1 { background: var(--heat-1); }
td.heat-2, .swatch.heat-2 { background: var(--heat-2); }
td.heat-3, .swatch.heat-3 { background: var(--heat-3); }
td.heat-4, .swatch.heat-4 { background: var(--heat-4); }
td.heat-5, .swatch.heat-5 { background: var(--heat-5); }
td.heat-6, .swatch.heat-6 { background: var(--heat-6); }
td.heat-7, .swatch.heat-7 { background: var(--heat-7); color: #fff; }
td.heat-8, .swatch.heat-8 { background: var(--paint-yes); color: #fff; }

/* Intensity legend (issue 103): a tight fewer→more ramp under the heatmap. */
.heat-scale .swatch { margin-left: 2px; }

/* Tap-a-slot roster popover (issue 103): rides .slot-pop for the box,
   shadow, and viewport clamping; only the name lines are its own. */
.heat-pop .hp-names {
  margin: 0 0 0.2rem;
  font-size: 0.85rem;
  overflow-wrap: anywhere; /* long rosters wrap inside the pop */
}
.heat-pop .hp-maybe { color: var(--muted); }

/* Heat cells are tappable on the poll page (the popover above); the admin
   page's cursor comes from .can-finalize instead. */
.heat-tappable td.heat[data-ts] { cursor: pointer; }

/* Share URLs and tokens wrap rather than widening the page. */
.empty-state { padding: 1rem 0; overflow-wrap: anywhere; }

.best-times, .split-times { margin: 0.75rem 0 1rem; }
.best-times h3, .split-times h3 { margin: 0 0 0.35rem; font-size: 1rem; }
.best-times ol, .split-times ol { margin: 0; padding-left: 1.4rem; }
.best-times li { margin-bottom: 0.15rem; }
.split-times li { margin-bottom: 0.5rem; }
.split-roster { font-size: 0.85rem; }

.everyone {
  border: 1px solid var(--ink);
  color: var(--ink);
  font-size: 0.75rem;
  font-weight: 700;
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
  margin-left: 0.35rem;
}

/* Friendly error pages (404/500/429) */
.error-card { max-width: 34rem; text-align: center; margin-top: 3rem; }
.error-home { margin-top: 1.25rem; }

/* Finalized poll */
.finalized-banner { border-width: 2px; border-color: var(--ink); background: var(--heat-1); }
.finalized-banner h2 { margin: 0 0 0.25rem; }
.finalized-banner a { color: var(--ink); }
/* Light mode's --heat-1 already reads clearly green; in dark mode it's too
   subtle, so deepen the tint there (issue 071). */
:root[data-theme="dark"] .finalized-banner { background: var(--heat-4); }

/* Two-session split rosters (issue 119): the suggested attendees and calendar
   link for each meeting, side by side on wide screens, stacked when narrow. */
.session-rosters { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 0.6rem; }
.session-roster { flex: 1 1 14rem; border-top: 1px solid var(--line); padding-top: 0.5rem; }
.session-roster h3 { margin: 0 0 0.3rem; font-size: 0.95rem; }
.session-roster p { margin: 0 0 0.3rem; }

/* Closed poll — every offered time passed with nothing finalized. Neutral,
   muted styling so it reads as "over" rather than a positive result. */
.closed-banner { border-width: 2px; border-color: var(--muted); }
.closed-banner h2 { margin: 0 0 0.25rem; color: var(--muted); }

#paint-grid[data-finalized] td.slot { cursor: default; touch-action: auto; }

/* The chosen cell keeps its heat color; a bold label and a bright green ring
   mark it as the finalized time. */
td.heat.chosen { outline: 3px solid var(--chosen); outline-offset: -3px; font-weight: 700; }

/* Admin page */
.share-actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.organizer-link { margin-bottom: 0; }

.organizer-availability { margin-bottom: 1.5rem; }
#organizer-grid .grid-card { margin-top: 1rem; margin-bottom: 0; }
#organizer-grid .form-actions { margin-top: 0.75rem; }

.finalize-card, .danger-zone { margin-top: 1.5rem; }

/* Click-to-finalize pop-up, anchored to the clicked heatmap cell. */
.can-finalize td.heat[data-ts] { cursor: pointer; }

/* Shared cell-anchored popover box (issue 103): the admin finalize pop and
   the poll page's roster pop differ only in contents. */
.slot-pop {
  position: absolute;
  z-index: 20;
  /* min() keeps the pop inside even the smallest viewport (app.js clamps
     its left edge; this clamps its width). */
  min-width: min(14rem, calc(100vw - 1rem));
  max-width: calc(100vw - 1rem);
  background: var(--surface);
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
  padding: 0.75rem 0.9rem;
}

.slot-pop p { margin: 0 0 0.3rem; }
.finalize-pop form { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
.finalize-pop button { padding: 0.35rem 0.8rem; font-size: 0.9rem; }

.finalize-form select { max-width: 32rem; }

/* Poll page layout: content column + participant panel on the right. */
.poll-layout { display: flex; gap: 1.5rem; align-items: flex-start; }
.poll-main { flex: 1; min-width: 0; }

.panel-card {
  flex: 0 0 14rem;
  position: sticky;
  top: 1rem;
  margin: 0;
  padding: 1.1rem 1.25rem;
}

.panel-card h2 { margin: 0 0 0.5rem; font-size: 1.05rem; }

.participant-list {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
}

.participant-list li {
  position: relative; /* anchors the require popup (issue 117) */
  padding: 0.15rem 0;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* Required-for-both, issue 117 (refining 081's badge/inline toggle):
   a compact "Ⓑ" marks the required rows and the name is a click target
   opening the toggle popup. The name button inherits the row's color and
   weight so the slot-hover restyling (p-yes/p-maybe/p-no) still applies. */
.p-name {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.p-name:hover, .p-name:focus-visible { text-decoration: underline; }
.req-both { font-weight: 700; }

.require-pop {
  position: absolute;
  left: 0;
  top: 100%;
  z-index: 20;
  width: 14rem;
  max-width: 80vw;
  background: var(--surface);
  border: 1px solid var(--ink);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
  padding: 0.6rem 0.75rem;
  font-size: 0.85rem;
}

/* Role badges (issue 099): small bordered pills, visually separated from
   the name they follow — the bare muted word used to read as part of it. */
.role-badge {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 0.45rem;
  vertical-align: 0.1em;
}

/* Hover states — monochrome by design: */
/* Admin panel: invitations live with the people (invite form + Invited list). */
.panel-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
}
.panel-head h2 { margin: 0 0 0.5rem; }

.btn-small { padding: 0.3rem 0.7rem; font-size: 0.85rem; }

#invite-form { margin: 0.25rem 0 0.75rem; }
#invite-form .field { margin-bottom: 0.5rem; }
#invite-form textarea { font-size: 0.85rem; }

/* Invite autocomplete dropdown (issue 050) */
.invite-entry { position: relative; }
.invite-suggest {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 20;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  max-height: 12rem;
  overflow-y: auto;
}
.invite-suggestion {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 0.45rem 0.7rem;
  font: inherit;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
}
.invite-suggestion:hover,
.invite-suggestion:focus { background: var(--off); }
.invite-suggestion .sugg-name { font-weight: 600; }

.invited-head {
  margin: 0.75rem 0 0.25rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* The heading is a toggle (issue 117): it inherits the uppercase muted look
   and expands the list to include already-responded invitees. */
.invited-toggle {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.inv-more { text-transform: none; letter-spacing: 0; font-weight: 400; }

.invited-list { list-style: none; margin: 0 0 0.75rem; padding: 0; }

/* Responded invitees stay hidden until the organizer expands the list. */
.invited-list:not(.expanded) .inv-responded { display: none; }

.invited-item {
  position: relative;
  padding: 0.15rem 0;
  cursor: default;
  overflow-wrap: anywhere; /* long invitee emails stay inside the panel */
}

.invited-item .inv-status { font-size: 0.75rem; }

/* Hover (or focus, for keyboard/touch) an invitee for details + remind. */
.invite-pop {
  display: none;
  position: absolute;
  left: 0;
  top: 100%;
  z-index: 20;
  width: 15rem;
  max-width: 80vw;
  background: var(--surface);
  border: 1px solid var(--ink);
  border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
  padding: 0.6rem 0.75rem;
  font-size: 0.85rem;
}

.invited-item:hover .invite-pop,
.invited-item:focus-within .invite-pop { display: block; }

.invite-pop p { margin: 0 0 0.25rem; }

/* Disabled remind for a suppressed invitee (issue 047): the wrapping span
   carries the explanatory title so it's hover/focus-reachable. */
.remind-disabled { cursor: not-allowed; }

.remind-all { margin-top: 0.25rem; }

/* Manual match controls (issue 079) inside the invite popover: a compact
   participant picker on pending rows, sized to the 15rem pop. */
.match-form {
  display: flex;
  gap: 0.35rem;
  align-items: center;
  margin-top: 0.35rem;
}
.match-form select {
  flex: 1;
  min-width: 0;
  font-size: 0.8rem;
}

.pro-pitch { font-size: 0.85rem; margin-bottom: 0; }

.participant-list li.p-yes { font-weight: 700; }
.participant-list li.p-maybe { font-weight: 400; color: var(--ink); }
.participant-list li.p-no { color: var(--faded); }
.participant-list li.p-no .role-badge { color: var(--faded); }

.p-no-sample { color: var(--faded); }

/* Participant strip (issue 076): hidden beside the desktop panel; at narrow
   widths it shows a one-line roster with the group results (issue 096 moved
   it below the response area, so it no longer pins over the grids). It
   scrolls sideways rather than wrapping. */
.participant-strip { display: none; }

/* Shared breakpoints (issues 039–041): 900px stacks the poll layout's
   side panel; 600px tightens chrome spacing for phones. The availability
   grid uses neither — it wraps intrinsically (see .slot-grid). */
@media (max-width: 900px) {
  .poll-layout { flex-direction: column; }
  /* Explicit widths: the layout's align-items:flex-start would otherwise
     size stacked children to min-content, letting a wide grid card push
     past the viewport instead of wrapping its columns. */
  .poll-main { width: 100%; }
  .panel-card { position: static; flex: none; width: 100%; }

  .participant-strip {
    display: block;
    padding: 0.3rem 0.25rem;
    margin-bottom: 0.75rem;
    overflow-x: auto;
    white-space: nowrap;
    font-size: 0.85rem;
    border-bottom: 1px solid var(--line);
  }
  .strip-count { font-weight: 700; }

  /* Clipped-edge fade (issue 104): a hard cut mid-name reads as a bug, so
     while the roster overflows, its right edge fades out. An alpha mask —
     not a colored overlay — so it blends into either theme's background and
     can't intercept taps. app.js stamps the class only while there is
     actually more to scroll, and clears it at the scroll end. */
  .participant-strip.strip-clipped {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
  }
}

@media (max-width: 600px) {
  main { padding: 1rem 0.75rem 2.5rem; }
  .card { padding: 1.1rem 0.9rem; }
  .site-header { padding: 0.75rem 1rem; }
  .hero { margin: 1.5rem auto; }
  .hero h1 { font-size: 1.6rem; }
  /* Admin controls get finger-sized targets (issue 041). */
  .btn-small { padding: 0.45rem 0.85rem; }
  .participant-list li, .invited-item { padding: 0.3rem 0; }
}
