:root {
  /* Desktop sticky-header height; the mobile media query overrides it. Keeps the
     rota bar pinned flush beneath the appbar without a magic number drifting. */
  --appbar-h: 59px;
  --bg: #f4f6f9;
  --panel: #ffffff;
  --border: #dfe4ea;
  --text: #1f2933;
  /* Darkened from #7b8794 (~3.6:1 on white — below AA) to ~5:1 so secondary
     text (cell counts, company names on shift pills, rail labels, hints) stays
     legible while still reading as muted. Dark-mode --muted is fine as-is. */
  --muted: #66717e;
  /* Brand green — the lime of the "a" in the Nearby logo (#93be26), darkened
     just enough that white button text clears WCAG AA (~5:1). --accent-rgb is
     the same colour as an r,g,b triple for rgba() washes; --accent-soft is the
     pale tint used behind active/hover surfaces. */
  --accent: #5d7d17;
  --accent-dark: #496112;
  --accent-rgb: 93, 125, 23;
  --accent-soft: #eef4e2;
  --danger: #dc2626;
  --cover: #ea580c; /* cover-needed accent — orange, distinct from holiday red */
  --shift: #e0edff;
  --shift-border: #93c0ff;
  --today: #fff7e6;
  /* Mobile day-card header band — a solid-enough blue so each day clearly
     stands out from the page rather than blending into it. */
  --day-head-bg: #b6cef6;
  --day-head-fg: #16388c;
  --day-head-line: #6f9ce8;
  /* A stronger outline than the default border so each day card is well defined. */
  --day-card-border: #b8c2cf;
  --tl-line: #eef1f5; /* faint hour gridlines in the timeline view */
  --tl-now: #ef4444; /* current-time marker on the hour view */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: var(--bg);
}

.hidden { display: none !important; }
.muted { color: var(--muted); }
.center { text-align: center; }

button {
  font: inherit;
  cursor: pointer;
  border: none;
  border-radius: 6px;
  padding: 8px 14px;
  background: var(--accent);
  color: #fff;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease, color .15s ease;
}
button:hover { background: var(--accent-dark); }
button.secondary { background: #e5e9f0; color: var(--text); }
button.secondary:hover { background: #d5dbe6; }
button.danger { background: var(--danger); }

/* Consistent keyboard-focus ring across every interactive control. Uses the
   two-tone ring (white gap + accent) so it reads on any background, light or
   dark. Mouse clicks don't trigger :focus-visible, so it stays out of the way. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
button:focus-visible {
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--accent);
  outline: none;
}
a, input, select, textarea, .ce-input { transition: border-color .15s ease, box-shadow .15s ease, background .15s ease; }

input, textarea {
  font: inherit;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  width: 100%;
  background: #fff;
  color: var(--text);
}
textarea { resize: vertical; }

/* Password reveal toggle (injected by app.js around every password input). */
.pw-field { position: relative; display: block; }
.pw-field > input { padding-right: 42px; }
.pw-field .pw-reveal {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin: 0;
  padding: 0;
  background: transparent;
  color: var(--muted);
  border-radius: 6px;
}
.pw-field .pw-reveal:hover { background: transparent; color: var(--text); }
.pw-reveal svg { width: 20px; height: 20px; display: block; pointer-events: none; }
.pw-reveal .pw-eye-off { display: none; }
.pw-reveal[aria-pressed="true"] .pw-eye { display: none; }
.pw-reveal[aria-pressed="true"] .pw-eye-off { display: block; }

/* --- Login --- */
.login-wrap {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 20px;
}
.login-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 32px;
  width: 100%;
  max-width: 360px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.06);
}
.login-logo {
  width: 160px;
  height: auto;
  display: block;
  margin: 0 auto 16px;
  filter: drop-shadow(0 2px 3px rgba(15, 23, 42, 0.22));
}
.login-card h1 { margin: 0 0 4px; }
.login-card label {
  display: block;
  margin: 16px 0 0;
  font-weight: 600;
  font-size: 14px;
}
.login-card input { margin-top: 6px; font-weight: 400; }
.login-card button { width: 100%; margin-top: 20px; }
.error { color: var(--danger); font-size: 14px; }

/* --- Top bar --- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.brand { font-weight: 700; font-size: 18px; display: flex; align-items: center; gap: 8px; }
.brand-logo { height: 22px; width: auto; display: block; filter: drop-shadow(0 1px 2px rgba(15, 23, 42, 0.28)); }
.week-nav { display: flex; align-items: center; gap: 10px; }
.week-nav button { padding: 6px 12px; }
#week-label { font-weight: 600; min-width: 190px; text-align: center; }
.user-box { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.filters-group {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 12px 6px 10px;
  border: 2px solid var(--accent);
  border-radius: 10px;
  background: var(--accent-soft);
  flex-wrap: wrap;
}
.filters-heading {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-dark);
}
.filter {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.filters-group select {
  border-color: var(--accent);
  font-weight: 600;
}
.clear-filters {
  padding: 6px 12px;
  font-size: 13px;
  font-weight: 700;
  background: #fff;
  color: var(--accent-dark);
  border: 1px solid var(--accent);
}
.clear-filters:hover { background: var(--accent); color: #fff; }
.clear-filters:disabled {
  opacity: 0.45;
  cursor: default;
  background: #fff;
  color: var(--accent-dark);
}
select {
  font: inherit;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  color: var(--text);
}

main { padding: 20px; }
.toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
.add-staff {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
}
.add-staff input { width: 220px; font-weight: 400; }

/* --- Rota sections (Dayshift / Backshift / Nightshift) --- */
.rota-section { margin-bottom: 26px; }
.section-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.section-head h2 {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
}
.section-add { font-weight: 400; color: var(--text); }
.section-add input { width: 200px; }
.section-empty { margin: 6px 2px 0; font-size: 13px; }

/* Per-section accent colour, to tell the three sections apart at a glance. */
#block-day { --sec: #d97706; }   /* amber  — daytime */
#block-back { --sec: #0d9488; }  /* teal   — back/evening */
#block-night { --sec: #4338ca; } /* indigo — nights */
.rota-section .section-head h2 {
  border-left: 5px solid var(--sec);
  padding-left: 10px;
  color: var(--sec);
}
.rota-section .rota-grid { border-top: 3px solid var(--sec); }
#block-day .rota-grid thead th { background: #fdf3e6; }
#block-back .rota-grid thead th { background: #e7f6f4; }
#block-night .rota-grid thead th { background: #e7e8f7; }
/* Keep the "today" column highlight winning over the section tint. */
.rota-section .rota-grid thead th.today { background: var(--today); }

/* Section checkboxes in the edit dialog. */
.sections-field {
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-top: 14px;
  padding: 8px 12px 12px;
}
.sections-field legend { font-size: 13px; font-weight: 600; padding: 0 6px; }
.sections-field .chk {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: 14px;
  font-weight: 400;
}
.sections-field .chk input { width: auto; }

/* --- Grid --- */
/* Fixed layout with explicit column widths so all three section tables line up
   column-for-column no matter what each one contains. */
.grid-scroll { overflow-x: auto; }
.rota-grid {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
  min-width: 1000px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.rota-grid th, .rota-grid td {
  border: 1px solid var(--border);
  padding: 8px;
  vertical-align: top;
}
/* Stronger vertical dividers so each day column is clearly separated. */
.rota-grid th + th, .rota-grid td + td {
  border-left: 2px solid #aab4c4;
}
.rota-grid thead th {
  background: #f0f3f8;
  font-size: 13px;
  text-align: center;
  position: sticky;
  top: 0;
}
.rota-grid thead th.today { background: var(--today); }

/* Special days (e.g. Christmas) shown atop a day's column / card. A tinted,
   bordered pill in the special day's own colour so it's clear at a glance. */
.special-days {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 5px;
}
.special-day {
  display: block;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.25;
  white-space: normal;
  word-break: break-word;
}
/* On the mobile day cards the badge sits just under the date heading. */
.day-card-head + .special-days { margin: -2px 12px 6px; }
.staff-name {
  width: 150px;
  font-weight: 600;
  word-break: break-word;
  background: #fafbfc;
}
.staff-name.editable-name { cursor: pointer; }
.staff-name.editable-name:hover { background: var(--accent-soft); text-decoration: underline; }
.col-total, .cell-total { width: 74px; }
.col-total { text-align: right; }
.cell-total {
  text-align: right;
  font-weight: 600;
  white-space: nowrap;
  background: #fafbfc;
  color: var(--text);
}
.day-cell { height: 70px; }
.day-cell.editable { cursor: pointer; }
/* Per-day "staff on shift" count row at the bottom of each section. */
.section-footer td {
  background: #eef1f6;
  font-weight: 600;
  vertical-align: middle;
}
.section-footer .footer-label { font-size: 13px; color: var(--muted); }
.section-footer .footer-count { text-align: center; }
.section-footer .footer-count.zero { color: var(--muted); font-weight: 400; }
.day-cell.editable:hover { background: #f7faff; }
.day-cell.today { background: var(--today); }

/* --- Matrix layout: day columns × section bands --- */
/* Real gaps between columns so each day is a clearly separated lane. */
/* Calendar panel — frame the weekly grid and the month grid as one bounded
   object in a soft slate tint, so the white cells read as raised tiles against
   the near-white page. Content colours (shift chips, section tints) untouched. */
#sections,
.month-grid {
  background: #e7ebf1;
  border: 1px solid #d5dbe4;
  border-radius: 14px;
  box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.03);
}
#sections { padding: 4px 14px 10px; }
.month-grid { padding: 10px; }
@media (max-width: 640px) {
  #sections { padding: 6px 8px 8px; }
  .month-grid { padding: 8px; }
}

.rota-grid.matrix {
  border-collapse: separate;
  border-spacing: 8px 16px;
  border: none;
  border-radius: 0;
  overflow: visible;
  /* Low floor so all 7 days fit on a normal laptop; the grid only scrolls
     horizontally on genuinely narrow windows rather than always clipping Sun. */
  min-width: 780px;
  /* Transparent so the panel's grey shows through the border-spacing gaps
     between cells; otherwise the table's own white fills the gutters. */
  background: transparent;
}
.rota-grid.matrix th,
.rota-grid.matrix td {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding-left: 14px;
  padding-right: 14px;
}
/* Gap now separates the days, so drop the heavy vertical divider here. */
.rota-grid.matrix th + th,
.rota-grid.matrix td + td { border-left: 1px solid var(--border); }
/* Header cells (day columns + the section-title column) are white tiles like
   the data cells; the bold dates and the section-colour accents carry the
   hierarchy, so no muddy in-between grey against the panel. */
.rota-grid.matrix thead th { background: #fff; }
/* Wider left column for the section titles. */
.matrix thead .section-col { background: #fff; width: 112px; }
.section-row .section-col {
  width: 112px;
  font-size: 15px;
  font-weight: 700;
  vertical-align: middle;
  background: #fff;
  color: var(--sec, var(--text));
  border-left: 5px solid var(--sec, var(--border));
}
.section-row.sec-day { --sec: #d97706; }
.section-row.sec-back { --sec: #0d9488; }
.section-row.sec-night { --sec: #4338ca; }

/* Today's column stands out: an amber ring on every cell down the column
   (header + each section cell), echoing the month view's today treatment. The
   body-cell fill stays light so shift chips keep their contrast. */
.rota-grid.matrix th.today,
.rota-grid.matrix td.today {
  border-color: #eab308;
  box-shadow: 0 0 0 2px #fde68a;
}
.rota-grid.matrix thead th.today {
  background: #fef3c7;
  color: #92400e;
  font-weight: 700;
}
/* Day cells carry their own white fill (the table is transparent so its gaps
   show the grey panel). Without this the panel grey bleeds into the cells. */
.matrix-cell { vertical-align: top; background: #fff; }
.matrix-cell.today { background: var(--today); }

/* Trailing "off" band — who's on approved holiday each day. */
.section-row.off-row { --sec: #dc2626; }
.off-cell { padding-top: 6px; padding-bottom: 6px; }
/* Holiday "off" chips are always red, regardless of the person's company. */
.off-chip {
  display: inline-block;
  margin: 0 4px 4px 0;
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 600;
  color: #b91c1c;
  background: rgba(220, 38, 38, 0.12);
  border-radius: 999px;
}
.off-chip.clickable { cursor: pointer; }
.off-chip.clickable:hover { background: rgba(220, 38, 38, 0.25); }
.off-chip.clickable:focus-visible { outline: 2px solid #dc2626; outline-offset: 1px; }

/* Holiday-details dialog (from an "Off" chip) */
.hol-info-card { margin-top: 8px; }
.hol-info-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 18px;
  align-items: baseline;
  margin: 0;
  font-size: 14px;
}
.hol-info-grid dt {
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.hol-info-grid dd { margin: 0; }
.hol-info-cancel { margin-top: 16px; }
/* Mobile "off" line inside a day card. */
.m-off {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  padding: 10px 14px;
  border-top: 1px solid var(--border);
}
.m-off-label { font-weight: 700; font-size: 13px; margin-right: 4px; }

.entry {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  border-radius: 6px;
  padding: 4px 8px;
  margin-bottom: 4px;
  font-size: 13px;
  line-height: 1.3;
}
.entry.clickable { cursor: pointer; }
.entry.clickable:hover { filter: brightness(0.97); }
.entry.cover .entry-name { color: var(--cover); font-weight: 800; }
/* Prompt on an open cover slot ("Tap to cover" for staff, offer count for admins). */
.entry-cover-hint {
  flex-basis: 100%;
  font-size: 12px;
  font-weight: 700;
  color: var(--cover);
  opacity: 0.9;
}
/* The "✓ You've put your name forward" confirmation shown to a staff volunteer. */
.entry-cover-hint.offered { color: #16a34a; opacity: 1; }
/* A cover slot for a company this staff member isn't on — muted, tap to explain. */
.entry-cover-hint.locked { color: var(--muted); font-weight: 600; }
/* A cover slot with volunteers waiting — nudge it to stand out for managers. */
.entry.cover.has-offers { box-shadow: inset 0 0 0 1px var(--cover); }

/* Volunteer picker in the shift dialog. */
.shift-offers {
  margin: 4px 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: color-mix(in srgb, var(--cover) 8%, transparent);
}
.shift-offers-head {
  font-size: 13px;
  font-weight: 700;
  color: var(--cover);
  margin-bottom: 8px;
}
/* Shown while a cover slot is still waiting for its first volunteer. */
.shift-offers-empty {
  font-size: 13px;
  font-style: italic;
  color: var(--muted);
}
/* Scrolls once a popular slot gathers many volunteers, so Save stays reachable. */
.shift-offers-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 200px;
  overflow-y: auto;
}
.offer-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
  color: var(--text);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}
.offer-row:hover { border-color: var(--cover); }
.offer-radio {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--muted);
}
.offer-row.selected { border-color: var(--cover); }
.offer-row.selected .offer-radio {
  border-color: var(--cover);
  background: radial-gradient(circle, var(--cover) 0 45%, transparent 55%);
}

/* "Cover needed" toggle in the shift dialog */
.cover-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-weight: 600;
}
.cover-toggle input { width: auto; }
.entry-name { font-weight: 700; flex-basis: 100%; }
.entry-time { flex-basis: 100%; font-variant-numeric: tabular-nums; white-space: nowrap; }
.entry-co, .entry-note { flex-basis: 100%; font-size: 12px; color: var(--muted); }

/* Desktop week-grid shifts: mirror the mobile card styling within the narrow
   day columns — a prominent name with the time beneath it coloured to its shift
   type (--sec inherits from the section row), and the venue/note muted. The
   two-column name|venue split can't fit at this width, so they stay stacked. */
.matrix-cell .entry .entry-name { font-size: 13.5px; }
.matrix-cell .entry .entry-time { font-weight: 700; color: var(--sec, var(--text)); }

/* Draft/publish bar — shown to managers when the visible week has staged,
   unpublished changes. Warm amber so it reads as "work in progress". */
.draft-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin: 12px 0 0;
  padding: 10px 14px;
  background: #fff7ed;
  border: 1px solid #f6c88b;
  border-radius: 10px;
}
.draft-bar-info { display: flex; align-items: flex-start; gap: 8px; font-weight: 600; color: #92400e; }
.draft-bar-lines { display: flex; flex-direction: column; gap: 2px; }
.draft-bar-hint { font-weight: 500; font-size: 12px; color: #b45309; }
.draft-dot { width: 9px; height: 9px; border-radius: 50%; background: #d97706; flex: 0 0 auto; margin-top: 5px; }
.draft-bar-actions { display: flex; gap: 8px; }

/* A staged (unpublished) shift on the manager's grid: dashed outline + a small
   tag. New shifts read amber; edits read blue. Staff never receive drafts. */
.entry.draft { outline: 1px dashed #d97706; outline-offset: -1px; }
.entry.draft-edited { outline-color: #2563eb; }
.entry-draft-tag {
  flex-basis: 100%;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #b45309;
}
.entry.draft-edited .entry-draft-tag { color: #1d4ed8; }

.cell-count { font-size: 11px; color: var(--muted); margin-top: 2px; }
.cell-add {
  margin-top: 4px;
  padding: 2px 8px;
  font-size: 12px;
  background: transparent;
  color: var(--accent);
  border: 1px dashed var(--border);
  border-radius: 6px;
}
.cell-add:hover { background: var(--accent-soft); border-color: var(--accent); }

/* --- Weekly hours summary --- */
.hours-summary {
  margin-top: 22px;
  background: #e7ebf1;
  border: 1px solid #d5dbe4;
  border-radius: 10px;
  padding: 14px 18px;
  max-width: 420px;
}
.hours-summary h3 { margin: 0 0 10px; font-size: 15px; }
.hours-list { display: grid; grid-template-columns: 1fr auto; gap: 5px 18px; }
.hours-row { display: contents; }
.hours-val { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

/* --- Mobile: stacked day-by-day cards --- */
.day-card {
  background: var(--panel);
  border: 1.5px solid var(--day-card-border);
  border-radius: 10px;
  margin-bottom: 12px;
  overflow: hidden;
}
.day-card.today { box-shadow: 0 0 0 2px #eab308; }
.day-card-head {
  background: var(--day-head-bg);
  color: var(--day-head-fg);
  border-bottom: 2px solid var(--day-head-line);
  padding: 10px 14px;
  font-weight: 700;
  text-align: center;
}
.day-card.today .day-card-head {
  background: var(--today);
  color: #b45309; /* amber-brown, readable on the warm "today" band */
  border-bottom-color: #eab308;
}
.m-section {
  padding: 10px 14px;
  margin-top: 12px;
  border-top: 1px solid var(--border);
  /* Shift-type accent lines running down the full height of the section, both sides. */
  border-left: 7px solid var(--sec, var(--border));
  border-right: 7px solid var(--sec, var(--border));
}
.m-section:first-of-type { margin-top: 0; }
.m-section.sec-day { --sec: #d97706; }
.m-section.sec-back { --sec: #0d9488; }
.m-section.sec-night { --sec: #4338ca; }
.m-section-head {
  font-weight: 700;
  font-size: 13px;
  color: var(--sec, var(--text));
  margin-bottom: 8px;
}
.m-empty { font-size: 13px; padding: 2px 0; }
.m-none { padding: 12px 14px; }

/* Phone day-card entries: a big, full-height staff name on the left, with all
   the detail — time, venue, note — stacked immediately to its right and packed
   to the left (rather than the old four full-width rows that wasted the
   horizontal space). Card is allowed to grow a little to give the name room. */
.day-card .entry {
  font-size: 14px;
  padding: 10px 12px;
  align-items: center; /* centre the name/time block against the venue/note block */
  column-gap: 14px;
  row-gap: 3px;
}
.day-card .entry .entry-main {
  flex: 1 1 auto; /* name + time on the left, growing into the free space */
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.day-card .entry .entry-side {
  flex: 0 0 auto;
  min-width: 0;
  margin-left: auto; /* push the venue + note to the right edge of the card */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  gap: 1px;
}
/* Big name with its time stacked directly beneath, on the left. Each keeps its
   own size — the name large, the time at its usual smaller size. */
.day-card .entry .entry-name,
.day-card .entry .entry-time {
  flex-basis: auto;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.day-card .entry .entry-name { font-size: 22px; line-height: 1.1; }
.day-card .entry .entry-time { font-size: 15px; font-weight: 700; color: var(--sec, var(--text)); }
/* Venue and comment share the smaller size on the right. */
.day-card .entry .entry-co,
.day-card .entry .entry-note { flex-basis: auto; font-size: 14px; }

@media (max-width: 640px) {
  main { padding: 12px; }
  .topbar {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: 12px;
  }
  .week-nav { justify-content: space-between; }
  #week-label { min-width: 0; flex: 1; }
  .user-box { justify-content: flex-start; }
  .toolbar { margin-bottom: 12px; }
}

.shift-chip {
  display: block;
  background: var(--shift);
  border: 1px solid var(--shift-border);
  border-radius: 6px;
  padding: 4px 6px;
  margin-bottom: 4px;
  font-size: 13px;
  line-height: 1.3;
}
.shift-chip .time { font-weight: 600; }
.shift-chip .company { font-size: 12px; font-weight: 600; }
.shift-chip .note { color: var(--muted); font-size: 12px; }

/* --- Colour swatches --- */
.swatches { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.swatch {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  padding: 0;
  border: 2px solid transparent;
  box-shadow: 0 0 0 1px var(--border);
}
.swatch:hover { transform: scale(1.08); }
.swatch.selected {
  border-color: #fff;
  box-shadow: 0 0 0 2px var(--text);
}

/* --- Admin screen --- */
.admin-main { max-width: 820px; margin: 0 auto; }
/* The time clock has a wide, many-column table — give its page much more room
   than the other admin sections so every column fits without cramping. */
#timeclock-view .admin-main { max-width: 1200px; }
/* The shift-feedback table has a free-text report column that wants room to
   breathe — give it the same wide page as the time clock. */
#feedback-view .admin-main { max-width: 1200px; }
.admin-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 18px 20px;
  margin-bottom: 20px;
}
/* Sub-tabs inside the admin screen (Users / Staff / Companies). */
.admin-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--border);
}
.admin-tab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-weight: 600;
  font-size: 15px;
  padding: 10px 16px;
  margin-bottom: -1px;
  border-radius: 8px 8px 0 0;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.admin-tab:hover { background: var(--bg); color: var(--text); }
.admin-tab.active {
  color: var(--accent-dark);
  border-bottom-color: var(--accent);
}
.admin-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.admin-head h2 { margin: 0; font-size: 18px; }
/* Cap long admin lists (Users, Staff) so a big list scrolls inside its card
   instead of pushing the page miles down. The header row stays pinned.
   Each list now sits on its own tab, so let it grow to fill the viewport
   (floored at 440px so short screens aren't cramped). */
.admin-scroll {
  max-height: max(440px, calc(100vh - 300px));
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.admin-scroll .admin-table th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}
.admin-table { width: 100%; border-collapse: collapse; }
.admin-table th, .admin-table td {
  text-align: left;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  vertical-align: middle;
}
.admin-table th { color: var(--muted); font-size: 12px; text-transform: uppercase; }
/* A small, plain-case note under a column header (e.g. explaining the ★). */
.th-hint {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}
.admin-table td.nowrap { white-space: nowrap; }
/* A real table cell (not flex) so it vertically centres in the row; buttons sit
   inline, right-aligned, spaced. */
.admin-actions { text-align: right; white-space: nowrap; }
.admin-actions button + button { margin-left: 8px; }
.company-dot {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 8px;
  vertical-align: middle;
}
button.small { padding: 5px 10px; font-size: 13px; }
.inline-add { display: flex; gap: 8px; align-items: center; }
.inline-add input { width: 200px; }
.admin-note { margin: 0 0 12px; font-size: 13px; }
.toolbar-export { display: flex; gap: 8px; flex-wrap: wrap; }

/* --- Dialog --- */
dialog {
  border: none;
  border-radius: 12px;
  padding: 24px;
  width: 90%;
  max-width: 380px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2);
  /* Theme-aware so dialogs follow dark mode instead of the browser's white. */
  background: var(--panel);
  color: var(--text);
  /* Safety net: any dialog too tall for the screen scrolls as a whole rather
     than overflowing off-screen (inner lists have their own caps on top). */
  max-height: 90vh;
  overflow-y: auto;
  /* Keep a scroll gesture inside the dialog from chaining to the page behind. */
  overscroll-behavior: contain;
}
/* While a modal is open, lock the page behind it so it can't scroll. */
html.dialog-open, html.dialog-open body { overflow: hidden; }
/* That `overflow: hidden` also cancels `position: sticky` on the bottom tab bar
   (a sticky element loses its stickiness when its scroll container can't scroll),
   so it drops from the viewport bottom to its in-flow position — mid-screen on a
   short view like Clock — and shows through the semi-transparent backdrop. The
   bar is unusable behind the modal anyway, so hide it while a dialog is open.
   Higher specificity than the mobile `#tabbar` rule, so it wins there too. */
html.dialog-open #tabbar { display: none; }
dialog::backdrop { background: rgba(0, 0, 0, 0.35); }
dialog h3 { margin: 0 0 4px; }
dialog label {
  display: block;
  margin-top: 14px;
  font-weight: 600;
  font-size: 14px;
}
/* The Person field wrapper is a <div> (not a <label>) so its quick-pick chip
   buttons don't hijack clicks via label→control forwarding. Match the caption
   and spacing to the surrounding dialog labels. */
.field-block { margin-top: 14px; }
.field-caption { display: block; font-weight: 600; font-size: 14px; }
dialog input { margin-top: 6px; font-weight: 400; }
dialog textarea { margin-top: 6px; font-weight: 400; }
dialog .row { display: flex; gap: 12px; }
dialog .row label { flex: 1; }
.dialog-actions { display: flex; align-items: center; gap: 10px; margin-top: 22px; }
.dialog-actions .spacer { flex: 1; }

/* --- Shift feedback (airport-style smiley) --- */
#feedback-dialog { max-width: 420px; }
.feedback-faces {
  display: flex;
  gap: 10px;
  margin: 18px 0 8px;
}
.feedback-face {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 12px 4px;
  background: var(--panel);
  color: var(--muted);
  border: 2px solid var(--border);
  border-radius: 12px;
  cursor: pointer;
  transition: transform 0.08s ease, border-color 0.12s ease, background 0.12s ease;
}
.feedback-face:hover { background: var(--accent-soft); border-color: var(--accent); }
.feedback-face.selected {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-dark);
  transform: translateY(-2px);
}
.feedback-emoji { font-size: 30px; line-height: 1; }
.feedback-label { font-size: 12px; font-weight: 600; }
/* Greyed out until a face is picked, so it's clear a rating is required. */
#feedback-send:disabled { opacity: 0.4; cursor: not-allowed; }
#feedback-send:disabled:hover { background: var(--accent); }

/* --- Shift feedback review (admin) --- */
.fb-summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin: 4px 0 14px;
  padding: 12px 14px;
  background: var(--accent-soft);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14px;
}
.fb-chips { display: flex; gap: 8px; margin-left: auto; }
.fb-chip {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 13px;
  font-weight: 600;
}
.fb-rating { white-space: nowrap; font-weight: 600; }
.fb-emoji { font-size: 18px; }
.fb-shift { white-space: nowrap; color: var(--muted); font-size: 13px; }
/* Free-text end-of-shift report: wrap long comments (and break unbroken runs
   like URLs) so the column can't blow the table out into a horizontal scroll. */
.fb-comment { white-space: normal; overflow-wrap: anywhere; }
/* Smiley shown next to a clock-out time on the Time clock rows. */
.tc-fb { font-size: 16px; cursor: help; border-radius: 4px; }
.tc-fb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.month-tip .fb-tip-comment { font-weight: 400; }
.month-tip .fb-tip-none { opacity: 0.6; font-style: italic; }

/* --- Personal details dialog --- */
/* Laid out wide (three columns) rather than tall, so the whole form fits with
   little or no scrolling. Collapses to two then one column on narrower screens. */
#profile-dialog { max-width: 900px; width: calc(100vw - 32px); }
.profile-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px 20px;
}
.profile-grid label { margin-top: 10px; }
/* Inputs/selects fill their cell; 16px keeps iOS from auto-zooming on focus.
   An explicit height + matching padding makes text inputs, selects and the date
   field all the same height (native controls otherwise render at their own
   metrics). box-sizing: border-box (global) keeps the border inside that height. */
.profile-grid input,
.profile-grid select {
  width: 100%;
  height: 42px;
  margin-top: 6px;
  padding: 8px 10px;
  font-weight: 400;
  font-size: 16px;
  line-height: 1.25;
}
/* Full-width fields (address lines, emergency contact name) span the whole row. */
.profile-grid .profile-full { grid-column: 1 / -1; }
/* Read-only (locked) mode: disabled fields read as plain values, not greyed
   form controls, so a view looks like a record rather than a dimmed form. */
.profile-grid input:disabled,
.profile-grid select:disabled {
  background: var(--panel-2, rgba(148, 163, 184, 0.1));
  color: var(--text, inherit);
  opacity: 1;
  -webkit-text-fill-color: var(--text, inherit); /* Safari keeps disabled text full-colour */
  cursor: default;
}
@media (max-width: 760px) {
  .profile-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .profile-grid { grid-template-columns: 1fr; }
  .profile-grid .profile-full { grid-column: auto; }
}

/* The Staff directory is its own top-level section — give it room for the
   extra columns (full name, mobile, email) without cramping. */
#staff-view .admin-main { max-width: 1200px; }
.staff-table th,
.staff-table td { vertical-align: middle; }
/* Keep long emails from stretching the table; wrap instead. */
.staff-table .staff-email-cell { word-break: break-word; }

/* Popup listing a person's attached companies (Companies button). */
#companies-dialog { max-width: 420px; }
.companies-popup { min-width: 240px; }
.companies-popup h3 { margin: 0 0 12px; }
#companies-dialog-list { margin-bottom: 4px; }

/* Search box above the admin staff directory. */
.staff-search-wrap { margin: 0 0 12px; }
.staff-search-wrap input {
  width: 100%;
  max-width: 320px;
  font-size: 16px;
}

/* Friendly welcome banner shown when a staff member is auto-prompted to fill
   in their details on first sign-in. */
.welcome-banner {
  margin: 0 0 4px;
  padding: 11px 13px;
  border-radius: 8px;
  background: var(--accent-soft, rgba(37, 99, 235, 0.1));
  border: 1px solid var(--accent-border, rgba(37, 99, 235, 0.35));
  font-size: 13.5px;
  line-height: 1.45;
}

/* A muted heads-up banner inside a dialog (e.g. "encryption key not set"). */
.info-banner {
  margin: 10px 0 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--warn-bg, rgba(217, 119, 6, 0.12));
  border: 1px solid var(--warn-border, rgba(217, 119, 6, 0.4));
  font-size: 13px;
}

/* --- Custom person suggestions (replaces native datalist) --- */
.input-suggest { position: relative; display: block; }
.suggest {
  position: absolute;
  left: 0; right: 0; top: 100%;
  z-index: 20;
  margin-top: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.14);
  max-height: 200px;
  overflow-y: auto;
}
.suggest.hidden { display: none; }
.suggest-item { padding: 9px 12px; cursor: pointer; font-size: 14px; }
.suggest-item:hover,
.suggest-item.active { background: var(--accent-soft); }

/* One-tap staff picker in the shift dialog (touch-friendly alternative to the
   desktop drag rail). Wraps; scrolls vertically if the list is long. */
.quickpick {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
  max-height: 140px;
  overflow-y: auto;
  /* Keep chips clear of the scrollbar, and reserve its gutter so the grid
     doesn't reflow when the list becomes scrollable. */
  padding: 2px 6px 2px 2px;
  scrollbar-gutter: stable;
  /* Fade the last row instead of slicing a chip in half, so a long list reads
     as "scroll for more" rather than clipped. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
}
.qp-chip {
  padding: 7px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.qp-chip:hover { border-color: var(--accent); }
.qp-chip.active {
  color: var(--accent-dark);
  background: var(--accent-soft);
  border-color: var(--accent);
  font-weight: 700;
}

/* type="search" is used to stop Safari's Contacts autofill; strip its native
   search chrome so these fields still look like normal inputs. */
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; display: none; }

/* Contenteditable text field (the Person input) — styled to match inputs, with
   an emulated placeholder. Used because Safari won't Contacts-autofill a div. */
.ce-input {
  display: block;
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  color: var(--text);
  font: inherit;
  font-weight: 400;
  line-height: 1.3;
  white-space: nowrap;
  overflow-x: auto;
  cursor: text;
}
.ce-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ce-input:empty::before { content: attr(data-placeholder); color: var(--muted); }
dialog .ce-input { margin-top: 6px; }

/* Login footer links: Create account / Forgot password */
.login-toggle {
  display: flex;
  justify-content: space-between;
  margin-top: 16px;
  font-size: 13px;
}
.login-toggle a { color: var(--accent); cursor: pointer; text-decoration: none; }
.login-toggle a:hover { text-decoration: underline; }

/* Informational (non-error) message, e.g. "awaiting approval" */
.info {
  color: var(--accent);
  font-size: 14px;
  background: rgba(var(--accent-rgb), 0.08);
  border-radius: 8px;
  padding: 8px 10px;
  margin-top: 12px;
}

/* Account status pills in the admin users table */
.status-pill {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
}
.status-active { background: #dcfce7; color: #166534; }
.status-pending { background: #fef9c3; color: #854d0e; }
.status-rejected { background: #fee2e2; color: #991b1b; }

/* Link-to-staff picker in the pending-approvals row */
.link-staff { max-width: 220px; font-size: 13px; padding: 4px 6px; }

/* Holidays: balance chips */
.balance-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }
.balance-chip {
  display: flex;
  flex-direction: column;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  min-width: 92px;
}
.bc-label { font-size: 12px; color: var(--muted); }
.bc-val { font-size: 18px; font-weight: 700; }

/* Holidays: request form */
/* Holidays filter bar (staff member + date range) */
.holiday-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  padding: 12px 14px;
  margin-bottom: 18px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.holiday-filter label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.holiday-filter input,
.holiday-filter select {
  min-width: 150px;
  height: 38px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text);
}
.holiday-filter button { align-self: flex-end; height: 38px; }
/* A note under the Time clock filter (e.g. an invalid range warning). Full-width
   so it sits on its own line, keeping the From/To/Staff fields aligned above. */
.tc-range-note {
  flex-basis: 100%;
  margin-top: -6px;
  font-size: 12px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--danger);
}
.tc-range-note:empty { display: none; }
@media (max-width: 640px) {
  .holiday-filter input,
  .holiday-filter select { min-width: 0; flex: 1; }
  /* iOS Safari ignores padding on native date inputs and top-aligns the value,
     so they read as unpadded. Drop the native appearance (as we do for search
     inputs) so they honour the box model like a normal text field. */
  .holiday-filter input[type="date"],
  .holiday-request input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
  }
  .holiday-filter input[type="date"]::-webkit-date-and-time-value,
  .holiday-request input[type="date"]::-webkit-date-and-time-value {
    text-align: left;
    margin: 0;
  }
}

/* Inline allowance editor in the totals table */
.allowance-input {
  width: 78px;
  padding: 4px 6px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
}
.allowance-input:focus { border-color: var(--accent); outline: none; }

.holiday-request {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
  margin-bottom: 8px;
}
.holiday-request label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
}
.holiday-request input { font-weight: 400; }
/* Native <select> and <input type=date> render at different heights across
   browsers (notably Safari), so pin every control to one explicit height —
   with box-sizing: border-box (global) they all line up regardless. */
.holiday-request input,
.holiday-request select {
  height: 38px;
  padding: 8px 10px;
}
/* Number inputs default to a very wide intrinsic size; keep Hours compact so
   the row reads evenly and Note takes the remaining space. */
.holiday-request input[type="number"] { width: 90px; }
/* Let the Staff select fill its column instead of shrinking to intrinsic width. */
.holiday-request select { width: 100%; min-width: 130px; }
.holiday-request .grow { flex: 1; min-width: 160px; }

/* Mobile: stack the holiday forms so every field is full-width. This also gives
   the date inputs a real width — with -webkit-appearance:none (needed for their
   padding) iOS collapses an *empty* date input to nothing, which squashed the
   From/To fields. Placed after the base rules so these widths win. */
@media (max-width: 640px) {
  .holiday-request { flex-direction: column; align-items: stretch; }
  .holiday-request label,
  .holiday-request .grow { width: 100%; min-width: 0; }
  .holiday-request input,
  .holiday-request select,
  .holiday-request input[type="number"] { width: 100%; }
  .holiday-request button[type="submit"] { width: 100%; }

  .holiday-filter label { flex: 1 1 40%; min-width: 0; }
  .holiday-filter input { width: 100%; }
}

/* "My holidays" list: the admin table on desktop, stacked cards on phones
   (five columns don't fit a 375px screen). renderMyHolidays fills both. */
.mh-cards { display: none; }
@media (max-width: 640px) {
  .mh-table { display: none; }
  .mh-cards { display: block; margin-top: 6px; }
}
.mh-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 10px;
}
.mh-card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.mh-card-dates { font-weight: 700; font-size: 15px; }
.mh-card-meta { color: var(--muted); font-size: 13px; margin-top: 6px; }
.mh-card-actions { margin-top: 12px; display: flex; justify-content: flex-end; }

/* Quiet Cancel for your own pending request — a ghost outline that reddens on
   hover, rather than a loud danger button. Used in both the table and cards. */
.cancel-req {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.cancel-req:hover {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger);
}

/* Count of accounts awaiting approval, next to the section heading */
.pending-badge {
  display: inline-block;
  min-width: 20px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
}

/* ===================================================================== */
/* Monthly overview                                                       */
/* ===================================================================== */
.month-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}

/* Weekday header cells */
.month-dow {
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  padding: 4px 0 2px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* A single day */
.month-cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  min-height: 92px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-align: left;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  transition: box-shadow 0.12s, border-color 0.12s, transform 0.02s;
}
.month-cell:hover {
  border-color: var(--accent);
  box-shadow: 0 2px 8px rgba(var(--accent-rgb), 0.12);
}
.month-cell:active { transform: translateY(1px); }
.month-cell.other-month { background: #fafbfc; }
.month-cell.other-month .month-daynum { color: var(--muted); }
.month-cell.today { border-color: #eab308; box-shadow: 0 0 0 2px #fde68a; }
.month-cell.today .month-daynum { color: #b45309; }

.month-cell-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.month-daynum { font-weight: 700; font-size: 14px; }
.month-cover { color: var(--cover); font-size: 11px; line-height: 1; }

/* Per-section count chips */
.month-badges { display: flex; flex-wrap: wrap; gap: 4px; }
.month-badge {
  --sec: var(--accent);
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  text-align: center;
  background: var(--sec);
}
.month-badge.sec-day { --sec: #d97706; }
.month-badge.sec-back { --sec: #0d9488; }
.month-badge.sec-night { --sec: #4338ca; }

/* Per-person names inside a cell — shown only on wide screens (see the
   min-width media query below, which also hides the count badges). */
.month-names { display: none; flex-direction: column; gap: 2px; }
.mn-line {
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: 11.5px;
  line-height: 1.3;
  cursor: help;
}
.mn-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--sec, var(--accent));
  flex: none;
  position: relative;
  top: 1px;
}
.mn-people { color: var(--text); }
.mn-line.sec-day { --sec: #d97706; }
.mn-line.sec-back { --sec: #0d9488; }
.mn-line.sec-night { --sec: #4338ca; }
.mn-line.sec-cover { --sec: var(--cover); }
.mn-line.sec-cover .mn-people { color: var(--cover); font-weight: 600; }

.month-cell-foot {
  margin-top: auto;
  font-size: 11px;
  color: var(--muted);
}

/* Legend under the grid */
.month-legend,
.week-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
  font-size: 13px;
}
/* The weekly colour key reads as a caption under the grid. */
.week-legend { color: var(--muted); }
.week-legend .lg-title { font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; font-size: 12px; }
.ml-item { display: inline-flex; align-items: center; gap: 6px; }
.ml-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.ml-hint { margin-left: auto; }

/* Phones: tighter grid, hide the "on shift" footnote to save space */
@media (max-width: 720px) {
  .month-grid { gap: 3px; }
  .month-cell { min-height: 64px; padding: 4px; border-radius: 6px; gap: 3px; }
  .month-daynum { font-size: 13px; }
  .month-badge { min-width: 18px; padding: 0 5px; font-size: 11px; }
  .month-cell-foot { display: none; }
  .month-dow { font-size: 10px; }
  .ml-hint { display: none; }
}

/* Wide screens: cells are roomy enough to spell people out, so swap the
   compact count badges for the names list. */
@media (min-width: 1100px) {
  .month-cell { min-height: 108px; }
  .month-badges { display: none; }
  .month-names { display: flex; }
}

/* Month hover tooltip: who's on shift for a section pill / cover dot */
.month-tip {
  position: fixed;
  z-index: 1000;
  max-width: 240px;
  padding: 8px 10px;
  background: #1f2933;
  color: #fff;
  border-radius: 8px;
  font-size: 12px;
  line-height: 1.4;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.28);
  pointer-events: none; /* never let the tip swallow hover/click */
}
.month-tip.hidden { display: none; }
.month-tip .tip-head {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  opacity: 0.75;
  margin-bottom: 5px;
}
.month-tip .tip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 7px; padding: 1px 0; }
.month-tip .tip-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.month-tip .tip-name { font-weight: 600; }
.month-tip .tip-time {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  opacity: 0.85;
}
.month-tip .tip-co { flex-basis: 100%; margin-left: 15px; opacity: 0.7; }

/* The section pills invite a hover on desktop */
.month-badge { cursor: help; }

/* ===================================================================== */
/* Rota templates builder                                                 */
/* ===================================================================== */
.templates-main { padding: 20px; max-width: 1200px; margin: 0 auto; }

/* Templates landing: list of template cards */
.tpl-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
.tpl-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: box-shadow 0.12s, border-color 0.12s, transform 0.02s;
}
.tpl-card:hover { border-color: var(--accent); box-shadow: 0 2px 8px rgba(var(--accent-rgb), 0.12); }
.tpl-card:active { transform: translateY(1px); }
.tpl-card-name { font-weight: 700; font-size: 15px; color: var(--text); }
.tpl-card-meta { font-size: 13px; color: var(--muted); }

/* Templates builder header (back / title / rename / delete) */
.tpl-builder-head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.tpl-builder-head h2 { margin: 0; font-size: 20px; }
.tpl-builder-head .spacer { flex: 1; }
.tpl-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 16px;
}
.tpl-controls label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
}
.tpl-controls select { min-width: 180px; }
.tpl-hint { margin: 0 0 12px; font-size: 13px; }

/* Template builder: staff rail + grid, side by side */
.tpl-work { display: flex; align-items: flex-start; gap: 16px; }
.tpl-work .tpl-grid { flex: 1; min-width: 0; }
#tpl-rail {
  flex: 0 0 150px;
  align-self: stretch;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px;
  max-height: 70vh;
  overflow-y: auto;
}
@media (max-width: 719px) {
  #tpl-rail { display: none; } /* drag is desktop-only */
}

.tpl-grid {
  display: grid;
  grid-template-columns: 92px repeat(7, minmax(0, 1fr));
  gap: 6px;
  align-items: stretch;
}
.tpl-corner { }
.tpl-dow {
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 4px 0;
}
.tpl-seclabel {
  display: flex;
  align-items: center;
  font-weight: 700;
  font-size: 13px;
  padding: 6px 8px;
  border-left: 4px solid var(--border);
  background: #fafbfc;
  border-radius: 6px;
}
.tpl-seclabel.sec-day { color: #d97706; border-left-color: #d97706; }
.tpl-seclabel.sec-back { color: #0d9488; border-left-color: #0d9488; }
.tpl-seclabel.sec-night { color: #4338ca; border-left-color: #4338ca; }

.tpl-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 74px;
  padding: 5px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.tpl-entry {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  padding: 3px 7px;
  border: none;
  border-left: 4px solid var(--accent);
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.25;
}
.tpl-entry .te-name { font-weight: 600; }
.tpl-entry .te-time { color: var(--muted); font-variant-numeric: tabular-nums; }
.tpl-add {
  margin-top: auto;
  align-self: stretch;
  padding: 2px;
  border: 1px dashed var(--border);
  border-radius: 4px;
  background: transparent;
  color: var(--muted);
  font-size: 14px;
  line-height: 1;
}
.tpl-add:hover { border-color: var(--accent); color: var(--accent); }

@media (max-width: 720px) {
  .tpl-grid { grid-template-columns: 60px repeat(7, minmax(0, 1fr)); gap: 3px; }
  .tpl-seclabel { font-size: 11px; padding: 4px; }
  .tpl-cell { min-height: 60px; padding: 3px; }
  .tpl-entry { font-size: 11px; }
}

/* ===================================================================== */
/* App shell & navigation (top app bar, account menu, bottom tab bar)     */
/* ===================================================================== */
#appbar {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}
.primary-nav { display: flex; gap: 4px; flex: 1; }
.nav-tab {
  background: transparent;
  border: none;
  color: var(--muted);
  font-weight: 600;
  padding: 8px 14px;
  border-radius: 8px;
}
.nav-tab:hover { background: var(--bg); color: var(--text); }
.nav-tab.active { background: var(--accent-soft); color: var(--accent-dark); }

/* Generic dropdown/popover plumbing (account, actions, filter) */
.menu-wrap { position: relative; }
.dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 60;
  min-width: 190px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);
  padding: 6px;
}
.dropdown-right { left: auto; right: 0; }
.dropdown-head {
  padding: 8px 10px;
  font-size: 12px;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.dropdown-item {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: 9px 10px;
  border-radius: 6px;
  color: var(--text);
  font-weight: 500;
}
.dropdown-item:hover { background: var(--bg); }

/* Account menu: always pinned to the right of the app bar (on mobile the nav
   tabs are hidden, so without this the avatar would sit next to the brand). */
#account-menu { margin-left: auto; }
.account-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 12px 4px 4px;
  color: var(--text);
  font-weight: 600;
}
.account-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 13px;
  font-weight: 700;
}
.account-name { font-size: 14px; }
.caret { font-size: 10px; color: var(--muted); }

/* Rota sub-header: week nav + filter/actions */
.rota-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  /* Pin the week nav / view switch / filters / actions beneath the appbar so
     they stay reachable while a tall rota scrolls. `- 1px` tucks the top edge
     under the appbar (z-index 50 > 40) to avoid a hairline seam. The mobile
     media query overrides top/z-index for its two-bar stack. */
  position: sticky;
  top: calc(var(--appbar-h) - 1px);
  z-index: 40;
}
.rota-bar-actions { display: flex; align-items: center; gap: 8px; }

/* Prominent inline pickers (desktop) — the staff and company filters both sit
   in the rota bar as big heading-style dropdowns, so who/what you're viewing is
   obvious at a glance. Each is a styled native <select> that displays the
   current choice AND changes it on click. */
/* Keep the pickers next to the date nav; push the action buttons to the edge. */
.rota-bar .rota-bar-actions { margin-left: auto; }
.rota-heading {
  display: inline-flex;
  align-items: center;
  margin-left: 32px;
  min-width: 0;
}
.company-heading {
  display: inline-flex;
  align-items: center;
  margin-left: 24px;
  min-width: 0;
}
.company-heading select,
.rota-heading select {
  height: auto;
  max-width: 340px;
  padding: 4px 30px 4px 8px;
  font-size: 24px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--accent);
  border: none;
  border-radius: 8px;
  background-color: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  /* Caret in the brand green (--accent), matching the company picker's caret —
     it was previously a stray blue. A native <select> can't read a CSS var
     inside url(), so the colour is inlined and the dark theme overrides it. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%235d7d17' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
[data-theme="dark"] .rota-heading select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23659229' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
.company-heading select:hover,
.rota-heading select:hover { background-color: var(--accent-soft); }
.company-heading select:focus-visible,
.rota-heading select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[data-theme="dark"] .company-heading select:hover,
[data-theme="dark"] .rota-heading select:hover { background-color: rgba(var(--accent-rgb), 0.16); }

/* Multi-select company filter: a heading-style button that opens a checkbox
   panel, so several companies can be shown at once. Styled to match the old
   single-select heading. */
.company-filter-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 340px;
  padding: 4px 8px;
  font-size: 24px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--accent);
  border: none;
  border-radius: 8px;
  background: transparent;
  cursor: pointer;
}
.company-filter-btn .company-filter-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.company-filter-btn .caret { font-size: 14px; color: var(--accent); }
.company-filter-btn:hover { background-color: var(--accent-soft); }
[data-theme="dark"] .company-filter-btn:hover { background-color: rgba(var(--accent-rgb), 0.16); }
.company-filter-panel {
  min-width: 230px;
  max-height: 360px;
  overflow-y: auto;
  padding: 6px;
}
/* Select all / Clear all row at the top of the company checkbox list. */
.company-filter-tools {
  display: flex;
  justify-content: flex-end;
  padding: 2px 8px 6px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.company-filter-tools .link-btn { font-weight: 600; font-size: 13px; }
/* A checkbox row — used in the desktop panel and the mobile filter popover. */
.company-check {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}
.company-check:hover { background: var(--bg); }
.company-check input { width: 16px; height: 16px; flex: 0 0 auto; cursor: pointer; }
.company-filter-checks {
  display: flex;
  flex-direction: column;
  max-height: 240px;
  overflow-y: auto;
  margin-top: 4px;
}
.rail-hint { font-size: 11px; }
#filter-btn.has-filters {
  border-color: var(--accent);
  color: var(--accent-dark);
  font-weight: 700;
  background: var(--accent-soft);
}

/* Filter popover */
.popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  width: 240px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.popover .filter { flex-direction: column; align-items: stretch; gap: 4px; font-weight: 600; }
.popover .filter select { width: 100%; }
/* Bottom row of the filter popover: Clear filters (when active) + a Done button
   that gives an explicit way to dismiss it after picking companies. */
.popover-actions { display: flex; gap: 8px; }
.popover-actions .clear-filters { flex: 1; }
.filter-done { flex: 1; padding: 6px 12px; font-size: 13px; font-weight: 700; }

/* Active-filter chips under the rota bar */
/* Active-filter chips live inside the sticky rota bar as their own full-width
   row, so they stay pinned with the header instead of scrolling off. */
.filter-chips { display: flex; flex-wrap: wrap; gap: 8px; flex-basis: 100%; width: 100%; }
.filter-chips:not(.hidden) { margin-top: 2px; }

/* First-run nudge (empty install) */
.first-run {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin: 12px 20px 0;
  padding: 12px 16px;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: 10px;
  font-size: 14px;
}
.link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}
.link-btn.danger { background: none; color: var(--danger, #dc2626); }
.link-btn:disabled { opacity: 0.5; cursor: default; }
.fb-actions, .fb-actions-col { text-align: right; white-space: nowrap; }
.filter-chip {
  background: var(--accent-soft);
  color: var(--accent-dark);
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 13px;
  font-weight: 600;
}
.filter-chip:hover { background: var(--accent); color: #fff; }

/* Section heading for Admin / Holidays / Templates panels */
.section-title { font-size: 22px; margin: 4px 0 18px; }

/* Bottom tab bar — mobile only */
#tabbar { display: none; }

/* ===================================================================== */
/* Drag-and-drop scheduling (desktop)                                     */
/* ===================================================================== */
.rota-layout { display: flex; align-items: flex-start; gap: 16px; }
.rota-layout #sections { flex: 1; min-width: 0; }

#staff-rail {
  flex: 0 0 180px;
  align-self: stretch;
  background: #e7ebf1;
  border: 1px solid #d5dbe4;
  border-radius: 10px;
  padding: 10px;
  max-height: 70vh;
  overflow-y: auto;
}
.rail-head {
  margin-bottom: 10px;
}
.rail-search {
  width: 100%;
  margin-bottom: 10px;
  padding: 6px 10px;
  font-size: 13px;
}
.rail-title {
  display: block;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 6px;
}
.rail-filter {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.rail-filter select {
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text);
  width: 100%;
}
.rail-empty { font-size: 12px; margin: 4px 2px; }

/* Company checkboxes in the staff dialog */
/* Companies column in the admin staff list — small tags, primary starred.
   The flex lives on an inner wrapper so the <td> stays a real table cell
   (a flex <td> breaks the row's bottom border). Tags wrap when there are many. */
.staff-companies-wrap { display: flex; flex-wrap: wrap; gap: 4px 6px; }
.staff-company-tag {
  display: inline-flex;
  align-items: center;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--muted);
}
.staff-company-tag.is-primary {
  color: var(--accent);
  border-color: var(--accent);
  font-weight: 600;
}

/* Scrolls once there are many companies, so the dialog stays a sane height. */
.company-checks {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 220px;
  overflow-y: auto;
  padding-right: 4px;
}
/* Special-days manager: an add row, then a scrollable list of existing days. */
.sd-add-row {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.sd-add-row label { margin: 0; }
.sd-label-field { flex: 1 1 160px; }
.sd-color-field input { width: 44px; height: 34px; padding: 2px; }
.sd-add-row #sd-add { align-self: flex-end; }
.sd-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 240px;
  overflow-y: auto;
}
.sd-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 2px;
  border-top: 1px solid var(--border);
}
.sd-dot { width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto; }
.sd-row-text { flex: 1; }
.sd-row-label { font-weight: 600; }
.danger-link { color: var(--danger, #dc2626); text-decoration: underline; }

/* Header row above the export company checklist: label + select-all/clear. */
.export-co-tools {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
  font-weight: 600;
}
.export-co-tools .spacer { flex: 1; }
.export-co-tools .link-btn { font-weight: 600; font-size: 13px; }
/* Each export company: tick, its colour dot, then the name. */
.export-co-row { display: flex; align-items: center; gap: 6px; }
.export-co-row input { width: auto; flex: 0 0 auto; }
.export-co-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
}
/* One company per row: its tick on the left, the "primary" star on the right. */
.company-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0;
}
.company-row .chk { margin-right: 0; }
.primary-pick {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}
.primary-pick input { width: auto; }
/* A chosen primary reads in the accent colour; disabled stars fade back. */
.primary-pick:has(input:checked) { color: var(--accent); font-weight: 600; }
.primary-pick:has(input:disabled) { opacity: 0.45; }

.rail-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 10px;
  margin-bottom: 6px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: grab;
}
.rail-chip:hover { border-color: var(--accent); }
.rail-chip:active { cursor: grabbing; }
.rail-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-tally {
  flex: none;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  background: rgba(var(--accent-rgb), 0.1);
  padding: 1px 7px;
  border-radius: 999px;
}
.rail-tally.muted { color: var(--muted); background: transparent; font-weight: 600; }

/* Draggable shift chips + drop feedback */
.entry[draggable='true'] { cursor: grab; }
.entry.dragging { opacity: 0.4; }
.matrix-cell.drag-over {
  outline: 2px dashed var(--accent);
  outline-offset: -3px;
  background: rgba(var(--accent-rgb), 0.06);
}

/* Move/Duplicate choice popover at the drop point */
#drop-menu { position: fixed; z-index: 90; min-width: 150px; }

/* Elements that only make sense on desktop (drag features) */
@media (max-width: 719px) { .desktop-only { display: none !important; } }
.mobile-only { display: none; }
@media (max-width: 719px) { .mobile-only { display: inline-block; } }

@media (max-width: 640px) {
  :root { --appbar-h: 53px; } /* mobile appbar height; see #appbar rule below */
  :root { --tabbar-h: 54px; } /* approx bottom tab-bar height; used to park the draft bar just above it */

  /* iOS Safari zooms the whole page in whenever a text field with font-size < 16px
     is focused, and that zoom STICKS after you move on. Focusing the 14px login
     fields left the app rendered too large for the screen right after sign-in —
     header pushed out of view, content clipped at the left edge. Keep every
     focusable form control at >= 16px on phones to stop the auto-zoom. We do NOT
     add maximum-scale=1 to the viewport, which would also kill pinch-to-zoom. */
  input, select, textarea, .ce-input { font-size: 16px; }

  /* Guarantee the page never loads sideways-scrolled on a phone. Use `clip`,
     not `hidden`: `hidden` turns html/body into a scroll container, which
     silently kills `position: sticky` on the appbar (it scrolls away and gets
     clipped at the top). `clip` blocks sideways scroll without that side effect. */
  html, body { overflow-x: clip; }
  /* Make the page a full-height flex column so the content region always fills
     the viewport and pushes the sticky #tabbar to the bottom. Without this, a
     short view (e.g. the Clock tab) doesn't make the page tall enough to scroll,
     and `position: sticky; bottom: 0` simply rests at the end of the flow —
     leaving the bar stranded halfway up the screen. `flex: 1 0 auto` lets the
     content grow past the viewport when it's long (page scrolls, sticky pins the
     bar); dvh tracks the iOS toolbar, with vh as the fallback. */
  body { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }
  #shell-content { flex: 1 0 auto; }
  /* Deliberately NO overflow here. The appbar (a body child) pins to the root
     viewport, but the rota-bar lives inside #shell-content — and WebKit treats
     any non-`visible` overflow ancestor as a sticky element's containing block.
     With a clip here, the rota-bar pinned to #shell-content's box while the
     appbar pinned to the viewport, and iOS momentum/rubber-band scrolling nudged
     the two boxes out of sync — the header visibly bounced apart. The html/body
     clip above already blocks sideways page scroll, so this was redundant; drop
     it so both bars share the root as their sticky context and move as one. */
  #shell-content { max-width: 100%; }

  /* The appbar is `position: sticky`, NOT `fixed`. iOS renders a fixed header
     on a separate scrolling layer that it rasterises as a texture — which came
     out blurry on device (the logo/title/avatar softened) no matter what layer
     hints we tried. Sticky keeps the header in the normal document layer, so
     text stays crisp, exactly like the sticky .rota-bar below it.
     It also extends up into the iOS safe area (notch / Dynamic Island) with the
     solid header background filling it, so the status bar sits on the header.
     Height grows by the inset (box-sizing: border-box); zero on non-notch
     devices, so nothing shifts. */
  #appbar {
    padding: 8px 12px; gap: 8px;
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
    height: calc(var(--appbar-h) + env(safe-area-inset-top, 0px));
    position: sticky; top: 0;
  }
  /* A `filter` spins up a compositing layer even on a sticky element, which can
     re-introduce the iOS softness — the logo's drop-shadow is negligible at
     22px, so drop it on mobile (kept on desktop). */
  .brand-logo { filter: none; }
  /* Sticky = in normal flow, so it takes its own space — no scroll-content
     padding needed (unlike the old out-of-flow fixed bar). */
  .primary-nav { display: none; } /* replaced by the bottom tab bar */
  .account-name { display: none; } /* avatar-only to save room */
  .account-btn { padding: 4px; border: none; }

  /* Stack the rota bar into rows that always fit: week nav wraps, then the
     Filter/Actions row below it. Keep it pinned under the appbar so the week
     controls stay on screen — only the rota list scrolls. --appbar-h is a fixed
     constant matching the pinned appbar height (no JS runs during scroll — see
     the appbar note above), so the bar sits flush beneath it.
     The upward solid box-shadow is a gap-filler: two separately-stacked sticky
     bars can momentarily part during iOS momentum scrolling, and without this a
     sliver of the (now colourful) rota list bleeds through the seam. The shadow
     paints the header background into that gap. It sits at z-index 40 — above
     the scrolling list, below the appbar (z-index 50) — so it covers bleed-
     through while staying hidden behind the appbar the rest of the time. */
  .rota-bar {
    flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 12px;
    position: sticky; top: calc(var(--appbar-h, 52px) + env(safe-area-inset-top, 0px)); z-index: 40;
    box-shadow: 0 -24px 0 0 var(--panel);
  }
  .rota-bar .week-nav { width: 100%; flex-wrap: wrap; justify-content: center; gap: 6px; }
  #week-label { flex: 1 1 110px; min-width: 0; text-align: center; }
  .week-nav button { padding: 6px 10px; }
  .rota-bar-actions { width: 100%; }

  /* On mobile the Filter button sits mid-bar, so anchoring the popover to it
     overflowed one side. Pin it to the viewport instead: fixed, a small inset
     from both edges (never off-screen), with its top set just below the button
     by positionFilterPopover(). Scrolls if the content is tall. */
  .popover {
    position: fixed;
    left: 12px;
    right: 12px;
    width: auto;
    max-height: calc(100vh - 130px);
    overflow-y: auto;
  }

  /* Sticky, NOT fixed. `overflow-x: clip` on the root (above) breaks
     `position: fixed` on iOS Safari — the bar detaches on scroll and jumps
     into the middle of the screen. Sticky keeps it in the normal document
     layer and pinned to the viewport bottom, the same fix the appbar uses. As
     the last child in flow it takes its own space, so no scroll-content
     padding is needed to clear it. */
  #tabbar {
    display: flex;
    position: sticky;
    bottom: 0;
    z-index: 80;
    background: var(--panel);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  #tabbar .tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    background: transparent;
    border: none;
    padding: 8px 4px;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
  }
  #tabbar .tab-ico { font-size: 18px; line-height: 1; }
  #tabbar .tab.active { color: var(--accent); }

  /* Draft/publish bar: on desktop it sits above the rota, but on a tall mobile
     rota that pushes Publish off-screen — a manager who edits a shift then has
     to scroll back to the top to find it, and can miss it entirely. Float it
     instead, pinned just above the tab bar, so Publish/Discard stay in reach
     the moment there are unpublished changes. Fixed (not sticky) is the right
     tool here — same as .pwa-install-banner; the draft bar isn't last in flow,
     so it can't ride the tab bar's sticky trick. The `:not(.hidden)` guard means
     the fixed positioning only applies while the bar is actually shown. */
  .draft-bar:not(.hidden) {
    position: fixed;
    left: 8px;
    right: 8px;
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px) + 8px);
    z-index: 90; /* above the tab bar (80) */
    margin: 0;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.22);
  }
  /* The long reassurance line eats vertical space the floating bar can't spare,
     and it covers more of the rota underneath — the count + buttons carry the
     message on their own here. */
  .draft-bar .draft-bar-hint { display: none; }
  /* Clearance so the last of the rota can scroll out from under the floating
     bar. Roughly the bar's height plus its gap above the tab bar. */
  body.draft-active #shell-content { padding-bottom: 132px; }

  /* Day-jump strip: Mon–Sun pills pinned under the rota bar (top set by JS to
     the bars' combined height). Scrolls horizontally if 7 pills don't fit. */
  .day-strip {
    display: flex;
    gap: 6px;
    position: sticky;
    z-index: 39; /* below the rota bar (40), above the day cards */
    padding: 6px 10px;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    scrollbar-width: none;
  }
  .day-strip::-webkit-scrollbar { display: none; }
  .day-pill {
    flex: 1 0 auto;
    min-width: 40px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 5px 8px;
    background: var(--bg);
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: 9px;
    font-weight: 700;
    line-height: 1.1;
  }
  .day-pill .dp-dow { font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em; }
  .day-pill .dp-num { font-size: 15px; }
  /* Neutralise the generic `button:hover` (which paints --accent-dark). On touch,
     :hover STICKS to the tapped pill until another is tapped, so without this the
     day you tapped keeps a lime background and reads as a phantom second highlight
     alongside the real in-view pill. Emphasis must come only from .active/.today,
     which are defined after this so they still win when a pill is both. */
  .day-pill:hover { background: var(--bg); }
  /* "Today" is marked with a small dot (calendar-style), NOT a green highlight —
     otherwise, once you scroll away and another day becomes the active (in-view)
     pill, today's highlight reads as a second selection. The dot coexists calmly
     with the filled active pill; on the active pill it turns white to stay visible. */
  .day-pill.today::after {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--accent);
    margin-top: 1px;
  }
  /* The active pill = the day currently in view. The one filled state, so it's
     unambiguous which day you're looking at. */
  .day-pill.active {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
  }
  .day-pill.active.today::after { background: #fff; }
}

/* Never let the mobile-only strip show on desktop, even mid-resize before a
   re-render toggles .hidden. */
@media (min-width: 641px) {
  .day-strip { display: none !important; }
}

/* Mobile "More" sheet: a dimmed backdrop with a panel sliding up from the
   bottom, holding the admin overflow sections. Always carries .hidden until the
   More tab opens it (and that tab only exists on mobile), so desktop never sees
   it. .hidden (display:none !important) wins over the flex here. */
.more-sheet {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: flex-end;
  background: rgba(0, 0, 0, 0.35);
}
.more-sheet-panel {
  width: 100%;
  background: var(--panel);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  padding: 8px 8px calc(10px + env(safe-area-inset-bottom, 0));
  box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.18);
  animation: more-sheet-in 0.16s ease-out;
}
@keyframes more-sheet-in {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}
.more-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: transparent;
  color: var(--text);
  border: none;
  border-radius: 10px;
  padding: 14px 16px;
  font-size: 16px;
  font-weight: 600;
  text-align: left;
}
.more-item.active,
.more-item:hover { background: var(--accent-soft); color: var(--accent-dark); }
.more-ico { font-size: 20px; line-height: 1; }

/* Transient undo toasts, pinned bottom-centre above everything. */
.toast-container {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 16px;
  background: #1f2933;
  color: #fff;
  padding: 10px 12px 10px 16px;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.35);
  font-size: 14px;
  pointer-events: auto;
  animation: toast-in 0.15s ease-out;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
.toast-undo {
  background: transparent;
  color: #7cb0ff;
  font-weight: 700;
  padding: 4px 8px;
  width: auto;
}
.toast-undo:hover { background: rgba(255, 255, 255, 0.1); }
@media (max-width: 640px) {
  .toast-container { left: 12px; right: 12px; bottom: 84px; transform: none; }
  .toast { justify-content: space-between; }
}

/* ===========================================================================
   Dark mode — a subtle, soft dark theme (not pure black), applied when
   <html data-theme="dark">. Redefines the core tokens and overrides the few
   surfaces that use hardcoded light colours, so the "white tiles on grey"
   system inverts cleanly. Accent/company/holiday colours are kept.
   ======================================================================== */
:root[data-theme="dark"] {
  --bg: #14181e;
  --panel: #1e232b;
  --border: #313945;
  --text: #e5e8ee;
  --muted: #98a2af;
  --accent: #659229;
  --accent-dark: #93c94a;
  --accent-rgb: 101, 146, 41;
  --accent-soft: #263019;
  --shift: #1d2836;
  --shift-border: #35507a;
  --today: #2f2917;
  --day-head-bg: #223047;
  --day-head-fg: #9ec2ff;
  --day-head-line: #35507a;
  --day-card-border: #454f5e;
  --tl-line: #262d37; /* faint hour gridlines, dark */
  --tl-now: #f87171; /* current-time marker, dark */
}
[data-theme="dark"] body { background: var(--bg); }

/* The wordmark is black lettering (only the "a" is green), so it disappears on
   the dark card. Sit it on a light "chip" in dark mode — keeps the brand green
   without needing a separate white logo. content-box so the padding frames the
   glyph rather than shrinking it. */
[data-theme="dark"] .login-logo {
  box-sizing: content-box;
  background: #fff;
  padding: 12px 18px;
  border-radius: 12px;
  filter: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
[data-theme="dark"] .brand-logo {
  box-sizing: content-box;
  background: #fff;
  padding: 3px 6px;
  border-radius: 6px;
  filter: none;
}

/* Grid gutter + side panels sit just below the page tone. */
[data-theme="dark"] #sections,
[data-theme="dark"] .month-grid,
[data-theme="dark"] #staff-rail,
[data-theme="dark"] .hours-summary {
  background: #191d24;
  border-color: #2b323d;
}
/* White tiles → soft dark tiles. */
[data-theme="dark"] .matrix-cell,
[data-theme="dark"] .rota-grid.matrix thead th,
[data-theme="dark"] .matrix thead .section-col,
[data-theme="dark"] .section-row .section-col,
[data-theme="dark"] .rail-chip,
[data-theme="dark"] .month-cell {
  background: #242a33;
}
[data-theme="dark"] .month-cell.other-month { background: #1d222a; }
/* Today keeps a warm tint, but dialed right down — the light-mode amber ring
   (bright #eab308 border + #fde68a glow) is far too loud on a dark grid. */
[data-theme="dark"] .rota-grid.matrix thead th.today,
[data-theme="dark"] .matrix-cell.today,
[data-theme="dark"] .day-cell.today { background: #24211a; }
[data-theme="dark"] .rota-grid.matrix th.today,
[data-theme="dark"] .rota-grid.matrix td.today,
[data-theme="dark"] .month-cell.today {
  border-color: #6b5a2b;
  box-shadow: 0 0 0 2px rgba(234, 179, 8, 0.16);
}
[data-theme="dark"] .rota-grid.matrix thead th.today,
[data-theme="dark"] .month-cell.today .month-daynum { color: #d8ad4e; }

/* Form controls (incl. the contenteditable Person field). */
[data-theme="dark"] input,
[data-theme="dark"] textarea,
[data-theme="dark"] select,
[data-theme="dark"] .ce-input {
  background: #242a33;
  color: var(--text);
}

/* Secondary buttons + subtle accent-tinted states. */
[data-theme="dark"] button.secondary { background: #2b323d; color: var(--text); }
[data-theme="dark"] button.secondary:hover { background: #353d49; }
[data-theme="dark"] .nav-tab.active,
[data-theme="dark"] .filter-chip.active,
[data-theme="dark"] .suggest-item.active {
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--accent-dark);
}
[data-theme="dark"] .cell-add:hover,
[data-theme="dark"] .day-cell.editable:hover,
[data-theme="dark"] .staff-name.editable-name:hover { background: #2a313b; }

/* --- PWA install banner ------------------------------------------------ */
/* A slim, dismissible bar pinned to the bottom, prompting home-screen
   install. Theme-aware via the shared tokens; sits above app content and
   clears the iOS home indicator with safe-area padding. */
.pwa-install-banner {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
  z-index: 3000;
  width: min(560px, calc(100vw - 24px));
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.22);
}
.pwa-install-msg { flex: 1 1 auto; font-size: 0.9rem; line-height: 1.3; }
.pwa-install-msg strong { font-weight: 600; }
.pwa-install-actions { flex: 0 0 auto; display: flex; gap: 8px; }
.pwa-btn {
  font: inherit;
  font-size: 0.85rem;
  padding: 7px 12px;
  border-radius: 8px;
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
}
.pwa-btn-primary { background: #15803d; color: #fff; }
.pwa-btn-primary:hover { background: #166a35; }
.pwa-btn-ghost { background: transparent; color: var(--muted); border-color: var(--border); }
.pwa-btn-ghost:hover { color: var(--text); }
@media (max-width: 480px) {
  .pwa-install-banner { flex-wrap: wrap; }
  .pwa-install-actions { width: 100%; justify-content: flex-end; }
}

/* --- Time clock -------------------------------------------------------- */
.clock-main { max-width: 640px; margin: 0 auto; padding: 20px 16px 40px; }
.clock-card { text-align: center; }
.clock-status {
  font-size: 18px;
  font-weight: 600;
  margin: 8px 0 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.clock-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--muted);
  display: inline-block;
}
.clock-dot.on { background: #16a34a; box-shadow: 0 0 0 4px rgba(22, 163, 74, 0.18); }
.clock-btn {
  font-size: 20px;
  font-weight: 700;
  padding: 18px 24px;
  width: 100%;
  max-width: 320px;
  border-radius: 12px;
}
.clock-week-total { font-size: 14px; font-weight: 600; color: var(--muted); }

/* Admin time-clock card */
.timeclock-actions { display: flex; gap: 8px; flex-wrap: wrap; }
/* The time-clock table has more columns than the other admin tables, so give it
   room: never wrap a cell, add horizontal padding, and let the pane scroll
   sideways on narrow screens rather than squashing everything together. */
.tc-scroll { overflow-x: auto; }
.tc-table { min-width: 720px; }
.tc-table th, .tc-table td { padding: 10px 14px; white-space: nowrap; }
/* On phones the pane is both-axis scrollable (vertical max-height + this
   horizontal scroll), and a nested 2-D scroll box fights the page's own
   vertical scroll under a thumb. Let the page handle vertical and keep the
   table a clean horizontal-only swipe, with momentum. The half-cut last
   column is itself the "more to the right" cue. */
@media (max-width: 719px) {
  .tc-scroll {
    max-height: none;
    overflow-y: visible;
    -webkit-overflow-scrolling: touch;
  }
}
.tc-open { color: var(--cover); font-size: 12px; font-weight: 600; }
.tc-missed { color: var(--muted); font-style: italic; }

/* Loud badges for the two states managers must not miss. */
.tc-tag {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}
/* Shift running now with nobody clocked in — still actionable, so it pulses to
   prompt a nudge. Amber, since they're only just expected (not yet a miss). */
.tc-tag.await {
  color: #fff;
  background: var(--cover);
}
@media (prefers-reduced-motion: no-preference) {
  .tc-tag.await { animation: tc-pulse 1.6s ease-in-out infinite; }
}
@keyframes tc-pulse { 50% { opacity: 0.5; } }
/* A genuine miss: past shift, nobody clocked in. Loud red, but static — it's
   over, so no pulse (less motion noise on old rows). `lateout` is its clock-out
   twin: still on the clock after the shift ended (forgotten clock-out). */
.tc-tag.miss,
.tc-tag.lateout {
  color: #fff;
  background: var(--danger);
}
/* Clocked in with no clock-out yet — the open-shift counterpart of a missing
   clock-in, so it wears the same amber pill (static; it's a normal live state). */
.tc-tag.onclock {
  color: #fff;
  background: var(--cover);
}
/* Unscheduled work (clocked in, no shift): informational, not an error. */
.tc-tag.unsch {
  color: var(--cover);
  background: color-mix(in srgb, var(--cover) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--cover) 45%, transparent);
}
/* State chip echoed into the Staff cell — hidden on desktop (the Clock in /
   Actual columns carry it there), shown on phones where those columns scroll
   off-screen, so the row's state is clear without swiping sideways. */
.tc-mflag { display: none; }
@media (max-width: 719px) {
  .tc-mflag { display: block; width: max-content; margin-top: 4px; }
}

/* Tint the whole row so the state reads at a glance across all columns. A
   plain miss (past shift, nobody clocked in) keeps its red badge but no row
   tint — it's over, nothing to action, so a white row is quieter. A forgotten
   clock-out still needs fixing, so that one stays tinted. */
.tc-row-await td { background: color-mix(in srgb, var(--cover) 8%, transparent); }
.tc-row-lateout td { background: color-mix(in srgb, var(--danger) 7%, transparent); }
.tc-row-unsch td { background: color-mix(in srgb, var(--cover) 6%, transparent); }
.tc-progress { color: var(--accent); font-style: italic; }
/* Sub-line under a clock time: how far it fell from the scheduled start/end. */
.tc-timing { margin-top: 2px; font-size: 12px; color: var(--muted); }
.tc-timing.late { color: var(--danger); }
.tc-timing.early { color: #16a34a; }
.tc-var { font-weight: 600; }
.tc-var.under { color: var(--danger); }
.tc-var.over { color: #16a34a; }
.tc-var.muted { color: var(--muted); font-weight: 500; }

/* Time-clock summary strip: a row of stat tiles above the table. */
.tc-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 4px 0 14px;
}
.tc-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 104px;
  padding: 10px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.tc-stat-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.tc-stat-value { font-size: 18px; font-weight: 700; }
.tc-stat-value.under { color: var(--danger); }
.tc-stat-value.over { color: #16a34a; }
.tc-stat-value.miss { color: var(--danger); }
.tc-row-actions { text-align: right; white-space: nowrap; }
#tc-edit-dialog label { display: block; margin: 0 0 12px; }
#tc-edit-dialog input[type="datetime-local"],
#tc-edit-dialog input[type="text"],
#tc-edit-dialog select {
  width: 100%;
  margin-top: 4px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  color: var(--text);
  background: var(--panel);
}

/* --- Rota view switch (Hour / Month toggles) --------------------------- */
.view-switch { display: inline-flex; gap: 6px; }
.view-btn.active {
  background: var(--accent);
  color: #fff;
}
.view-btn.active:hover { background: var(--accent-dark); }

/* --- Hour-by-hour timeline view ---------------------------------------- */
#timeline-view { margin-top: 8px; }
.timeline-empty { padding: 32px 12px; text-align: center; }

/* "Pick one company" prompt for the hour view. */
.timeline-chooser {
  padding: 40px 16px;
  text-align: center;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}
.timeline-chooser-title { margin: 0 0 16px; color: var(--muted); }
.timeline-chooser-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
.timeline-chooser-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.timeline-chooser-btn:hover { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.06); }
.tc-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex: none;
}

/* Compact company switcher above the hour grid. */
.timeline-switch {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.timeline-switch-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.timeline-switch-btn:hover { border-color: var(--accent); }
.timeline-switch-btn.active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.timeline-switch-btn.active .tc-dot { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5); }

/* One self-contained scroll pane: the hours scroll vertically inside it (with a
   sticky day header) and the columns scroll horizontally on narrow screens, so
   there's a single, predictable scroll region rather than a nested one fighting
   the page. Its max-height is set in JS to fit the viewport. */
.timeline {
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}

/* Head and body share one column template so day columns line up. */
.tl-head,
.tl-body {
  display: grid;
  grid-template-columns: 56px repeat(7, minmax(120px, 1fr));
  /* Grey padding between white day columns, like the week/month views: the gap
     lets the grey backfill show through so each day reads as its own card. */
  column-gap: 6px;
  background: var(--bg);
  min-width: 720px;
}

.tl-head {
  position: sticky;
  top: 0;
  z-index: 3;
  border-bottom: 1px solid var(--border);
}
.tl-day-head {
  padding: 8px 6px;
  text-align: center;
  font-weight: 700;
  font-size: 14px;
  background: var(--panel);
  line-height: 1.25;
}
.tl-day-head .muted { font-weight: 500; font-size: 12px; }
.tl-day-head.today { background: var(--today); }

.tl-body { position: relative; }

/* Hour labels sit in the left gutter, aligned to each hour line. */
/* No right border here: the column-gap plus each day's own left border
   separates the gutter from day 1, matching every other day boundary. A border
   too would read as a double line across the gap. */
.tl-gutter { position: relative; }
.tl-hour-label {
  position: absolute;
  right: 6px;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.tl-col {
  position: relative;
  background: var(--panel);
}
.tl-col.today { background: var(--today); }

/* Current-time marker: a line across the grid with a dot at the column edge. */
.tl-now {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 2px solid var(--tl-now);
  z-index: 2;
  pointer-events: none;
}
.tl-now-dot {
  position: absolute;
  left: 52px; /* sits at the gutter/column boundary (56px gutter) */
  top: -5px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--tl-now);
}

/* A shift block: reuses the .entry look (company colour + tint), but positioned
   absolutely at its start/end and packed side by side when shifts overlap. */
.entry.tl-entry {
  position: absolute;
  margin: 0;
  overflow: hidden;
  border-radius: 5px;
  font-size: 12px;
  line-height: 1.2;
  padding: 3px 6px;
  display: block; /* label sits at the top; a foot label is added at the bottom */
}
/* The bottom label on a tall block — frames the shift at its end boundary. */
.tl-entry-foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 3px 6px;
}
.entry.tl-entry .entry-name {
  display: block;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis; /* keep the name on one line so the time always has room */
}
/* The base .entry-time is nowrap; in a narrow timeline block let it wrap after
   the en-dash ("09:00–" / "17:00") instead of being clipped. */
.entry.tl-entry .entry-time {
  display: block;
  font-size: 11px;
  color: var(--muted);
  white-space: normal;
  overflow-wrap: anywhere;
}
.entry.tl-entry .entry-co,
.entry.tl-entry .entry-note { display: none; } /* company shows via colour; keep blocks tidy */
/* Very short blocks: keep the name on one line and drop the time to save room. */
.entry.tl-entry-short { padding: 1px 6px; }
.entry.tl-entry-short .entry-name { font-size: 11px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; }
.entry.tl-entry-short .entry-time { display: none; }
