/* parade.sg — clean, utilitarian, professional dark theme.
   Palette carried over from the standard project styling:
   bg #0e1525, panels #1b2433 / #1e293b, borders #2d3548, accent #38bdf8. */

/* ===== Global Reset ===== */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --bg: #0e1525;
  --panel: #1b2433;
  --panel-2: #1e293b;
  --border: #2d3548;
  --accent: #38bdf8;
  --text: #e2e8f0;
  --muted: #94a3b8;
  --faint: #64748b;

  --present: #22c55e;
  --absent: #ef4444;
  --extra: #a855f7;
  --fallout: #f59e0b;
  --none: #9ca3af;
  --star: #4ade80;
  --appt-line: #4a5570;   /* outline of the CC / Dy CC / CSM boxes */
}

body {
  font-family: "Segoe UI", Arial, sans-serif;
  font-size: 14px;
  background-color: var(--bg);
  color: var(--text);
  overflow: hidden;
  height: 100vh;
  display: flex;
  flex-direction: column;
  touch-action: manipulation; /* disable browser double-tap zoom; reserve double-tap for moving personnel */
}

.mono { font-family: "Consolas", monospace; }

/* ===== Brand ===== */
.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 7px;
  background: var(--accent);
  color: #0e1525;
  font-weight: 900;
  font-size: 18px;
  flex-shrink: 0;
}
.brand-accent { color: var(--accent); }

/* ===== Topbar ===== */
.topbar {
  background-color: var(--panel-2);
  padding: 8px 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid var(--border);
  flex-shrink: 0;
  gap: 12px;
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* Topbar groups — kept as inline rows on desktop; the mobile query (≤820) reflows
   them into a 2×2 grid (brand / user on top, project name / project menu below). */
.topbar-brand { display: flex; align-items: center; gap: 10px; flex-shrink: 0; text-decoration: none; color: inherit; }
.topbar-projectmenu { display: flex; align-items: center; gap: 10px; }
.topbar-user { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

.topbar-left h2 {
  color: #f0f4ff;
  font-size: 20px;
  white-space: nowrap;
}

.topbar-divider {
  width: 1px;
  height: 24px;
  background: var(--border);
}

.project-name-input {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 4px;
  max-width: 280px;
  font-family: inherit;
}
.project-name-input:hover { border-color: var(--border); }
.project-name-input:focus { outline: none; border-color: var(--accent); background: var(--bg); }

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.user-chip {
  font-size: 12px;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--border);
  padding: 4px 10px;
  border-radius: 99px;
}

.save-status {
  font-size: 11px;
  color: var(--muted);
  min-width: 0;
}

/* ===== Buttons ===== */
.button-outline-blue {
  background: none;
  border: 1px solid var(--accent);
  color: var(--accent);
  padding: 5px 12px;
  cursor: pointer;
  border-radius: 4px;
  font-size: 13px;
  font-family: inherit;
}
.button-outline-blue:hover:not(:disabled) { background-color: var(--accent); color: #0e1525; }
/* A refused action has to LOOK refused: these carry `disabled` in places where the
   server would reject them anyway (a locked contingent's Rename / Copy / Delete). */
.button-outline-blue:disabled { opacity: 0.4; cursor: not-allowed; }

.button-solid-blue {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: #0e1525;
  padding: 7px 14px;
  cursor: pointer;
  border-radius: 4px;
  font-size: 13px;
  font-weight: 700;
  font-family: inherit;
}
.button-solid-blue:hover { filter: brightness(1.08); }
.button-solid-blue:disabled { opacity: 0.5; cursor: not-allowed; }

.button-outline-grey {
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 7px 14px;
  cursor: pointer;
  border-radius: 4px;
  font-size: 13px;
  font-family: inherit;
}
.button-outline-grey:hover:not(:disabled) { border-color: var(--muted); color: var(--text); }
.button-outline-grey:disabled { opacity: 0.4; cursor: not-allowed; }

.button-solid-red {
  background: var(--absent);
  border: 1px solid var(--absent);
  color: #fff;
  padding: 7px 14px;
  cursor: pointer;
  border-radius: 4px;
  font-size: 13px;
  font-weight: 700;
  font-family: inherit;
}
.button-solid-red:hover { filter: brightness(1.08); }

/* ===== Project Menu Dropdown ===== */
.menu-wrap { position: relative; }
.menu-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  z-index: 200;
  min-width: 190px;
  padding: 6px;
}
.menu-dropdown.open { display: block; }
.menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--text);
  padding: 8px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 13px;
  font-family: inherit;
}
.menu-item:hover { background: var(--bg); color: var(--accent); }
.menu-sep { height: 1px; background: var(--border); margin: 5px 4px; }

/* Export Grid popup + mobile Project popup — the same menu-items as tappable rows. */
.export-options { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.export-options .menu-item {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 11px 12px;
  font-size: 14px;
}
.export-options .menu-sep { margin: 2px 4px; }

/* ===== Toolbar ===== */
.toolbar {
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  padding: 8px 16px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
  flex-wrap: wrap;
}
.toolbar-divider { width: 1px; height: 22px; background: var(--border); }
.zoom-control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
}
.zoom-control input[type="range"] { width: 110px; accent-color: var(--accent); }
.toolbar-stats {
  margin-left: auto;
  display: flex;
  gap: 14px;
  font-size: 12px;
  color: var(--muted);
  flex-wrap: wrap;
}
.stat-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 5px;
  vertical-align: middle;
}
.toolbar-stats strong { color: var(--text); }

/* ===== Layout ===== */
.layout {
  flex: 1;
  display: flex;
  overflow: hidden;
  min-height: 0;
}

/* Left column: the scrolling workspace with the rehearsal-date tab bar pinned to
   its bottom, so the tab bar only spans the contingent area (not the sidebar). */
.workspace-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

/* Workspace (center/left) — scrolls in both directions for large grids */
.workspace {
  flex: 1;
  overflow: auto;
  padding: 24px;
  /* Transparent so the fixed .app-bg backdrop shows through behind the grid.
     The grid-line layer is kept (sized 0 0, i.e. off) for parity. */
  background:
    linear-gradient(var(--border) 1px, transparent 1px) 0 0 / 0 0,
    transparent;
}
/* Blurred NDP backdrop pinned to the viewport. z-index:-1 keeps it behind all
   content but above the body fill; the opaque topbar/toolbar/sidebar paint over
   it, so it only reads through the transparent workspace, behind the boxes. The
   inset + scale hide the soft blurred edges; the overlay keeps boxes readable. */
.app-bg {
  position: fixed;
  inset: -24px;
  z-index: -1;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  filter: blur(10px) brightness(0.9);
  transform: scale(1.06);
}
.app-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(14, 21, 37, 0.78), rgba(14, 21, 37, 0.88));
}
.grid-canvas {
  display: inline-grid;
  gap: 8px;
  padding: 4px;
  /* Appointment boxes (CC / Dy CC / CSM) are absolutely positioned against this box. */
  position: relative;
}

/* Grid slots */
.slot {
  border: 1.5px dashed var(--border);
  border-radius: 10px;
  background: #131c2e;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  position: relative;
  overflow: hidden;
  transition: border-color 0.12s, background 0.12s, box-shadow 0.12s;
}
.slot.filled { border-style: solid; background: var(--panel-2); cursor: grab; }
.slot.drag-over { border-color: var(--accent); background: #16314a; box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.4); }
/* Locked position: grey, forward-slash hatched tile. The occupant renders normally on
   top; the cell refuses every move until it is unlocked. */
.slot.filled.locked {
  cursor: default;
  border-color: #55607a;
  background-color: #363e52;
  background-image: repeating-linear-gradient(45deg,
    rgba(203, 213, 225, 0.20) 0 5px, rgba(203, 213, 225, 0) 5px 11px);
}
.slot.dragging { opacity: 0.35; }
.slot .slot-empty { font-size: 20px; color: var(--faint); user-select: none; }
.slot .slot-name {
  font-weight: 600;
  color: var(--text);
  text-align: center;
  line-height: 1.2;
  padding: 0 4px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  max-width: 100%;
}
.slot .slot-coord {
  position: absolute;
  bottom: 3px;
  left: 0; right: 0;
  text-align: center;
  font-size: 9px;
  color: var(--faint);
  font-family: monospace;
}
.slot .slot-status-dot {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}
/* Proficiency letter (F/S/C/★) in the top-left corner, mirroring the status dot opposite
   it. Colour is set inline from PROFICIENCY; hidden for None Set / locked (Absent,
   attendance unset). */
.slot .slot-prof-letter {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 9px;
  height: 9px;
  font-size: 11px;
  line-height: 9px;
  font-weight: 700;
  text-align: center;
  user-select: none;
  pointer-events: none;
}

/* ===== Avatar ===== */
.avatar {
  position: relative;
  border-radius: 50%;
  flex-shrink: 0;
}
.avatar img,
.avatar .avatar-initials {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
}
.avatar .avatar-initials {
  background: #334155;
  color: #e2e8f0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-family: "Consolas", monospace;
}
.avatar.s-present { border: 2.5px solid var(--present); }
.avatar.s-absent_mc { border: 2.5px solid var(--absent); }
.avatar.s-absent_loa { border: 2.5px solid var(--absent); }
.avatar.s-rso_rsi { border: 2.5px solid var(--fallout); }
.avatar.s-ld { border: 2.5px solid var(--fallout); }
.avatar.s-extra { border: 2.5px solid var(--extra); }
.avatar.s-fallout { border: 2.5px solid var(--fallout); }
.avatar.s-fallout_evening { border: 2.5px solid var(--fallout); }
.avatar.s-none { border: 2.5px solid var(--none); }
.avatar-badge {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid var(--panel);
}

/* ===== Sidebar / Standby Pool ===== */
.sidebar {
  width: 300px;
  min-width: 300px;
  background: var(--panel);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}
.sidebar-header {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}
.sidebar-title { font-weight: 700; font-size: 14px; color: #f0f4ff; }
.sidebar-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
/* Count + the A→Z / Z→A sort symbol for the standby list. */
.pool-sub-row { display: flex; align-items: center; gap: 6px; }

.pool-dropzone {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
  transition: background 0.12s;
}
.pool-dropzone.drag-over {
  background: #16314a;
  box-shadow: inset 0 0 0 2px var(--accent);
}
.pool-list { display: flex; flex-direction: column; gap: 8px; }

.pool-empty {
  text-align: center;
  color: var(--faint);
  font-size: 13px;
  padding: 40px 12px;
  border: 2px dashed var(--border);
  border-radius: 10px;
}
.pool-empty .big { font-size: 28px; margin-bottom: 6px; }

.pool-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  cursor: grab;
  user-select: none;
  transition: opacity 0.12s, border-color 0.12s;
}
.pool-card:hover { border-color: var(--accent); }
.pool-card.dragging { opacity: 0.4; }
.pool-card .pc-body { flex: 1; min-width: 0; }
.pool-card .pc-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.status-pill {
  display: inline-block;
  margin-top: 3px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 1px 7px;
  border-radius: 99px;
  /* A status is read as one label: the long ones ("Fall Out Until Evening") stay on a
     single line and ellipsise like the name above them rather than wrapping the pill. */
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}
.status-pill.s-present { background: rgba(34, 197, 94, 0.18); color: var(--present); }
.status-pill.s-absent_mc { background: rgba(239, 68, 68, 0.18); color: var(--absent); }
.status-pill.s-absent_loa { background: rgba(239, 68, 68, 0.18); color: var(--absent); }
.status-pill.s-rso_rsi { background: rgba(245, 158, 11, 0.18); color: var(--fallout); }
.status-pill.s-ld { background: rgba(245, 158, 11, 0.18); color: var(--fallout); }
.status-pill.s-extra { background: rgba(168, 85, 247, 0.18); color: var(--extra); }
.status-pill.s-fallout { background: rgba(245, 158, 11, 0.18); color: var(--fallout); }
.status-pill.s-fallout_evening { background: rgba(245, 158, 11, 0.18); color: var(--fallout); }
.status-pill.s-none { background: rgba(156, 163, 175, 0.18); color: var(--none); }

.pc-actions { display: flex; gap: 2px; }
.icon-btn {
  width: 26px;
  height: 26px;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  border-radius: 4px;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.icon-btn:hover { background: var(--bg); color: var(--text); }
.icon-btn.danger:hover { color: var(--absent); }
/* Greyed, not disabled: the button still takes the press so it can say why it refuses,
   which is the only way a touch device ever sees the tooltip. Same look as the greyed
   tab-menu entries (.edit-tab-actions button.disabled). */
.icon-btn.disabled { opacity: 0.4; cursor: not-allowed; }
.icon-btn.disabled:hover { background: transparent; color: var(--muted); }
/* display:flex above beats the UA stylesheet's [hidden] { display: none }. */
.icon-btn[hidden] { display: none; }

/* ===== Footer ===== */
.security-footer {
  background-color: #000;
  padding: 4px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: #fff;
  letter-spacing: 1.5px;
  font-weight: bold;
  flex-shrink: 0;
}

/* ===== Modals ===== */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(7, 11, 20, 0.7);
  backdrop-filter: blur(2px);
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.modal-overlay.open { display: flex; }
/* Secondary dialogs can open ON TOP of another modal (e.g. Grid config from the mobile
   Adjustments popup; Rename/New and the delete confirm from the Edit Tabs popup), so
   they outrank the base 1000 to avoid being hidden behind it. */
#configModal, #tabNameModal, #confirmModal, #autoSizeModal, #promptModal { z-index: 1100; }
/* Opens over the member modal (its status row carries a clock too). */
#statusDatesModal { z-index: 1100; }
/* Status duration popup: the two dates side by side with the dash between them. */
#statusDatesSub { margin: 0 0 14px; }
.sd-row { display: flex; align-items: flex-end; gap: 10px; margin-bottom: 10px; }
.sd-box { flex: 1 1 0; min-width: 0; }
/* color-scheme so the browser's own calendar indicator and picker render dark. */
.sd-box .field-input { margin-bottom: 0; color-scheme: dark; }
.sd-dash { flex: 0 0 auto; padding-bottom: 10px; color: var(--muted); font-size: 16px; }
.modal {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 420px;
  max-width: 100%;
  max-height: 86vh;
  overflow-y: auto;
  padding: 22px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}
.modal-wide { width: 520px; }
.modal-narrow { width: 340px; }
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 18px;
}
.modal-header h2 {
  font-size: 16px;
  font-weight: 700;
  color: #f0f4ff;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.modal-close {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 16px;
  cursor: pointer;
}
.modal-close:hover { color: var(--text); }
.modal-actions {
  display: flex;
  gap: 10px;
  margin-top: 20px;
}
.modal-actions button { flex: 1; }

/* Form fields */
.field-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 5px;
  letter-spacing: 0.03em;
}
.field-input {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 14px;
  outline: none;
  font-family: inherit;
  color: var(--text);
  background: var(--bg);
  margin-bottom: 16px;
}
.field-input:focus { border-color: var(--accent); }

/* Member modal avatar upload */
.avatar-upload {
  width: 84px;
  height: 84px;
  margin: 0 auto 18px;
  border-radius: 50%;
  border: 2.5px dashed var(--accent);
  background: var(--bg);
  cursor: pointer;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.avatar-upload img { width: 100%; height: 100%; object-fit: cover; }
.avatar-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: var(--muted);
  font-size: 11px;
  gap: 2px;
}
.avatar-placeholder { font-size: 22px; }
.avatar-placeholder span { font-size: 11px; }

/* A field heading with a control on its right (the Status row's duration clock). */
.field-label-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.label-clock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}
.label-clock:not(:disabled):hover { background: var(--bg); color: var(--text); }
.label-clock:disabled { opacity: 0.35; cursor: not-allowed; }
/* A duration is already set on the selected status: the clock reads active, not idle. */
.label-clock.has-dates { color: var(--accent); opacity: 1; }

/* Status toggle (6 statuses — wraps onto multiple rows so long labels fit) */
.status-toggle { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
.status-toggle button {
  flex: 1 1 30%;
  min-width: 92px;
  padding: 8px 6px;
  border-radius: 6px;
  background: var(--bg);
  color: var(--muted);
  font-weight: 600;
  font-size: 12px;
  line-height: 1.2;
  cursor: pointer;
  border: 1.5px solid transparent;
  font-family: inherit;
}
.status-toggle button[data-status="present"].active { background: rgba(34,197,94,0.18); color: var(--present); border-color: var(--present); }
.status-toggle button[data-status="absent_mc"].active { background: rgba(239,68,68,0.18); color: var(--absent); border-color: var(--absent); }
.status-toggle button[data-status="absent_loa"].active { background: rgba(239,68,68,0.18); color: var(--absent); border-color: var(--absent); }
.status-toggle button[data-status="rso_rsi"].active { background: rgba(245,158,11,0.18); color: var(--fallout); border-color: var(--fallout); }
.status-toggle button[data-status="ld"].active { background: rgba(245,158,11,0.18); color: var(--fallout); border-color: var(--fallout); }
.status-toggle button[data-status="extra"].active { background: rgba(168,85,247,0.18); color: var(--extra); border-color: var(--extra); }
.status-toggle button[data-status="fallout"].active { background: rgba(245,158,11,0.18); color: var(--fallout); border-color: var(--fallout); }
.status-toggle button[data-status="fallout_evening"].active { background: rgba(245,158,11,0.18); color: var(--fallout); border-color: var(--fallout); }
.status-toggle button[data-status="none"].active { background: rgba(156,163,175,0.18); color: var(--none); border-color: var(--none); }

/* Competency toggle (member modal) — colours mirror Absent / Fall Out / Present / None;
   Star is a brighter green. */
.status-toggle button[data-proficiency="fair"].active { background: rgba(239,68,68,0.18); color: var(--absent); border-color: var(--absent); }
.status-toggle button[data-proficiency="satisfactory"].active { background: rgba(245,158,11,0.18); color: var(--fallout); border-color: var(--fallout); }
.status-toggle button[data-proficiency="competent"].active { background: rgba(34,197,94,0.18); color: var(--present); border-color: var(--present); }
.status-toggle button[data-proficiency="star"].active { background: rgba(74,222,128,0.18); color: var(--star); border-color: var(--star); }
.status-toggle button[data-proficiency="none"].active { background: rgba(156,163,175,0.18); color: var(--none); border-color: var(--none); }
/* An Absent status locks the competency row out. */
.status-toggle button:disabled { opacity: 0.4; cursor: not-allowed; }

/* Personnel (roster) modal reuses the member modal but has no per-rehearsal status
   or competency. */
body.personnel-modal #statusFieldLabel,
body.personnel-modal #statusToggle,
body.personnel-modal #proficiencyFieldLabel,
body.personnel-modal #proficiencyToggle { display: none; }

/* Config modal */
.config-row { display: flex; gap: 16px; }
.config-row > div { flex: 1; }
.config-hint {
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  font-size: 13px;
  color: var(--muted);
}

/* Projects list modal */
.project-list { display: flex; flex-direction: column; gap: 8px; max-height: 60vh; overflow-y: auto; }
.project-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-2);
}
.project-row .pr-main { cursor: pointer; flex: 1; min-width: 0; }
.project-row:hover { border-color: var(--accent); }
.project-row .pr-name { font-weight: 700; font-size: 14px; color: #f0f4ff; }
.project-row .pr-meta { font-size: 12px; color: var(--muted); margin-top: 2px; }
.shared-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 1px 7px;
  border-radius: 99px;
  background: rgba(56,189,248,0.16);
  color: var(--accent);
  margin-left: 8px;
}
/* "Linked to <parade>" — green so a linked contingent reads differently at a glance
   from a shared one (blue). Wraps under a long name rather than stretching the row. */
.link-pill {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 1px 7px;
  border-radius: 99px;
  background: rgba(34,197,94,0.16);
  color: var(--present);
  border: 1px solid rgba(34,197,94,0.35);
  margin-left: 8px;
  vertical-align: middle;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The padlock rides inside the pill: a contingent is locked IN a parade, so the two facts
   belong to one badge rather than two competing for the end of a long name. */
.link-pill-lock { margin-left: 5px; font-size: 9px; vertical-align: baseline; }
.project-empty { color: var(--faint); font-size: 14px; text-align: center; padding: 30px 0; }

/* Share modal */
.share-input-row { display: flex; gap: 8px; }
.share-input-row .field-input { margin-bottom: 0; }
.share-list-label { font-size: 12px; color: var(--muted); margin: 18px 0 8px; font-weight: 600; letter-spacing: 0.03em; }
.share-list { display: flex; flex-direction: column; gap: 6px; }
.share-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 7px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  font-size: 13px;
}
.share-chip button { background: none; border: none; color: var(--absent); cursor: pointer; font-size: 14px; }
.share-empty { color: var(--faint); font-size: 13px; }

/* Access that came from the parade, not from this modal. Its controls stay in place but
   inert — the ✕ is what explains where the access is really from when pressed. */
.share-chip.via-parade { border-style: dashed; }
.share-chip .share-via { font-size: 11px; color: var(--present); margin: 0 auto 0 10px; }
.share-chip button.share-locked { color: var(--faint); cursor: help; }
.share-chip select:disabled { opacity: 0.45; cursor: not-allowed; }

.confirm-text { font-size: 14px; color: var(--muted); line-height: 1.5; }

/* ===== Toast ===== */
.toast {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--panel-2);
  border: 1px solid var(--accent);
  color: var(--text);
  padding: 10px 18px;
  border-radius: 8px;
  font-size: 13px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 3000;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.error { border-color: var(--absent); }

/* ===== Home page ===== */
/* The topbar and the boxes are non-positioned, so without a stacking context of their
   own the fixed .auth-bg (z-index 0) would paint over them. */
.topbar-home, .home-layout { position: relative; z-index: 1; }

.home-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  padding: 20px;
}
/* Equal-height boxes that stay on one screen: each list scrolls inside its own box
   rather than growing the page. */
.home-box {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-top: 3px solid var(--accent);
  border-radius: 12px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.45);
  overflow: hidden;
}
.home-box-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.home-box-header h2 { font-size: 15px; color: #f0f4ff; }
.home-box-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
}

/* Stacked boxes on mobile, in the order Parades / My Projects / Shared with Me. Each is
   capped so a long list can't push the next box off the bottom. */
@media (max-width: 820px), (hover: none) and (pointer: coarse) {
  .home-layout {
    grid-template-columns: 1fr;
    overflow-y: auto;
    padding: 14px;
    /* Clears the fixed Changelog / Contact links pinned to the bottom-right. */
    padding-bottom: 70px;
  }
  .home-box { max-height: 70vh; }
}

/* ===== Auth (login) page ===== */
/* Centred vertically, but only while the card still fits. A flex item centred in a
   scroll container -- by justify-content or, as .auth-stack used to, by margin:auto --
   spills equally past BOTH ends once it outgrows the container, and the overflow above
   the scrollport is unreachable: the admin user list scrolled to its bottom but never
   back to its top. `safe` falls back to start alignment in exactly that case. The plain
   `center` before it is the fallback for browsers that don't know the keyword. */
.auth-body {
  overflow: auto;
  align-items: center;
  justify-content: center;
  justify-content: safe center;
  padding: 24px 0;
  position: relative;
}
/* Blurred NDP backdrop. Sits behind the card/logo; the inset + scale keep the
   blurred edges from feathering into a hard border, and the gradient overlay
   keeps the foreground readable regardless of the photo's brightness. */
.auth-bg {
  position: fixed;
  inset: -24px;
  z-index: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  filter: blur(10px) brightness(0.9);
  transform: scale(1.06);
}
.auth-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(14, 21, 37, 0.74), rgba(14, 21, 37, 0.86));
}
/* Lift the card + logo above the backdrop layer. */
.auth-stack { z-index: 1; }
/* Stack is centred on the login card alone (same as the no-logo layout), so the
   card never shifts off centre. The logo box is taken out of flow and floated
   directly above the card. */
/* Horizontal centring only -- the vertical half is .auth-body's `safe center` above,
   which is what keeps a tall card scrollable to its top. */
.auth-stack {
  position: relative;
  margin: 0 auto;
  width: 380px;
  max-width: calc(100vw - 32px);
}
.auth-logo-box {
  position: absolute;
  bottom: 100%;
  left: 0;
  right: 0;
  margin-bottom: 16px;
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 20px;
  box-shadow: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Cap the logo at roughly half the login card's height so it stays a compact
   header rather than dominating the page. */
.auth-logo {
  display: block;
  max-width: 100%;
  max-height: 170px;
  height: auto;
  background: #fff;
  padding: 20px;
  border-radius: 12px;
}
.auth-card {
  margin: 0 auto;
  width: 380px;
  max-width: calc(100vw - 32px);
  background: var(--panel);
  border: 1px solid var(--border);
  border-top: 3px solid var(--accent);
  border-radius: 12px;
  padding: 32px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.55);
}
/* Inside the stack the card stretches to the shared width; reset its own sizing. */
.auth-stack .auth-card { margin: 0; width: 100%; max-width: 100%; }
.auth-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 24px;
  font-weight: 800;
}
.brand-text { color: #f0f4ff; }
.auth-sub { color: var(--muted); font-size: 13px; margin: 6px 0 22px; }
.auth-tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  margin-bottom: 22px;
}
.auth-tab {
  flex: 1;
  padding: 10px 0;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  font-family: inherit;
  margin-bottom: -1px;
}
.auth-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.auth-error { color: var(--absent); font-size: 13px; min-height: 18px; margin-bottom: 10px; }
.auth-card .button-solid-blue { width: 100%; padding: 10px; font-size: 14px; }

/* Changelog/Contact links pinned to the page's bottom-right corner in a small
   dark box — unobtrusive footer-style links, out of the auth card's flow. */
.auth-links {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 2;
  display: flex;
  gap: 14px;
  background: rgba(0, 0, 0, 0.6);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 14px;
}
.auth-link {
  color: var(--muted);
  font-size: 12px;
  text-decoration: underline;
}
.auth-link:hover { color: var(--text); }

/* Back link pinned to the page's bottom-left corner, mirroring .auth-links. */
.auth-back {
  position: fixed;
  bottom: 16px;
  left: 16px;
  z-index: 2;
  background: rgba(0, 0, 0, 0.6);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 14px;
}

/* DIS attribution sitting inside the login card, below the Sign In button. */
.dis-attribution {
  margin-top: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  font-size: 12px;
  text-align: left;
  color: var(--muted);
}
.dis-logo { height: 36px; width: auto; flex-shrink: 0; }

/* Keep the agency name intact; the only allowed wrap is after "Powered by". */
.dis-nowrap { white-space: nowrap; }

/* Wider stack variant for the changelog page. */
.auth-stack-wide { width: 640px; }

/* Scrollable changelog/contact body inside the card. */
.changelog-body {
  margin-top: 8px;
  max-height: 60vh;
  overflow-y: auto;
  color: var(--text);
  font-size: 13px;
  line-height: 1.6;
}
.contact-body {
  margin-top: 8px;
  color: var(--text);
  font-size: 13px;
  line-height: 1.6;
}

/* ===== Install (PWA landing) page ===== */
/* Scrollable instructions area inside the card; the platform tabs swap panels. */
.install-body {
  margin-top: 4px;
  max-height: 56vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.install-panel { display: none; color: var(--text); font-size: 13px; line-height: 1.6; }
.install-panel.active { display: block; }
.install-steps { margin: 6px 0 10px 20px; padding: 0; }
.install-steps li { margin: 7px 0; }
.install-note {
  color: var(--muted);
  font-size: 12.5px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  margin: 8px 0;
}
.install-footnote { margin-top: 14px; color: var(--faint); font-size: 12px; text-align: center; }
#installBtn { width: 100%; padding: 11px; font-size: 14px; margin-top: 14px; }

/* ===== Print / PDF export ===== */
@media print {
  body { overflow: visible; height: auto; background: #fff; color: #000; display: block; }
  .topbar, .toolbar, .sidebar, .security-footer, .modal-overlay, .toast, .status-menu { display: none !important; }
  .layout { display: block; height: auto; overflow: visible; }
  .workspace { overflow: visible; padding: 0; background: #fff; }
  .slot { border-color: #94a3b8 !important; background: #fff !important; color: #000; break-inside: avoid; }
  .slot .slot-name, .slot .slot-coord, .appt-label { color: #000 !important; }
  .front-box { color: #000 !important; border-color: #000 !important; background: #fff !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* ===== Front-of-contingent marker ===== */
/* The grid sits inside a padded stage so the front banner can be placed on any
   of its four sides without overlapping the cells. --appt-* is set from JS: extra
   room on the sides where appointment boxes (CC / Dy CC / CSM) stick out, so the
   banner keeps hugging the contingent itself. */
.grid-stage {
  position: relative;
  display: inline-block;
  padding:
    calc(46px + var(--appt-top, 0px))
    calc(46px + var(--appt-right, 0px))
    calc(46px + var(--appt-bottom, 0px))
    calc(46px + var(--appt-left, 0px));
}
.front-box {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: rgba(56, 189, 248, 0.16);
  border: 2px solid var(--accent);
  border-radius: 8px;
  color: var(--accent);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 6px 10px;
  user-select: none;
}
.front-box[hidden] { display: none; }
.front-box.pos-north { top: calc(6px + var(--appt-top, 0px));    left: calc(46px + var(--appt-left, 0px)); right: calc(46px + var(--appt-right, 0px)); height: 34px; }
.front-box.pos-south { bottom: calc(6px + var(--appt-bottom, 0px)); left: calc(46px + var(--appt-left, 0px)); right: calc(46px + var(--appt-right, 0px)); height: 34px; }
.front-box.pos-west  { left: calc(4px + var(--appt-left, 0px));  top: calc(46px + var(--appt-top, 0px)); bottom: calc(46px + var(--appt-bottom, 0px)); width: 34px; }
.front-box.pos-east  { right: calc(4px + var(--appt-right, 0px)); top: calc(46px + var(--appt-top, 0px)); bottom: calc(46px + var(--appt-bottom, 0px)); width: 34px; }
/* The side banners read downwards. This is a plain rotation rather than writing-mode
   because html2canvas (PNG / PDF export) renders transforms but ignores writing-mode —
   with writing-mode the exported banner came out horizontal, cramped and upside down. */
.front-box .front-text { display: block; white-space: nowrap; flex: 0 0 auto; }
.front-box.pos-west .front-text,
.front-box.pos-east .front-text { transform: rotate(90deg); }

/* ===== Appointment holders (CC / Dy CC / CSM) ===== */
/* Boxes sit outside the contingent, absolutely placed by appointmentCellsHtml(); they
   are ordinary .slot tiles otherwise, so drag/drop, status dots and the competency
   letter all behave exactly as on the grid. The appointment name replaces RnCn. */
.appt-slot {
  position: absolute;
  border-style: solid;
  border-color: var(--appt-line);
}
.appt-slot:not(.filled) { border-style: dashed; }
/* The name caption, directly under its box (the box itself clips its own overflow, so
   this is a sibling). Same colour, size and font as the RnCn coordinate on the grid. */
.appt-label {
  position: absolute;
  text-align: center;
  font-size: 9px;
  font-family: monospace;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 14px;
  color: var(--faint);
  user-select: none;
  pointer-events: none;
}

/* Toolbar appointment control */
.appt-control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
}
.appt-checks { display: flex; align-items: center; gap: 10px; }
.appt-checks label {
  display: flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  white-space: nowrap;
}
.appt-checks input[type="checkbox"] { accent-color: var(--accent); width: 14px; height: 14px; cursor: pointer; }
.appt-checks label:hover { color: var(--accent); }
.appt-checks input[type="checkbox"]:disabled { cursor: not-allowed; opacity: 0.4; }
.appt-checks input[type="checkbox"]:disabled + span { opacity: 0.4; }

/* Toolbar front control */
.front-control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
}
.front-dirs { display: flex; gap: 3px; }
.front-dirs button {
  width: 26px;
  height: 26px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 700;
  font-family: inherit;
}
.front-dirs button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.front-dirs button.active { background: var(--accent); color: #0e1525; border-color: var(--accent); }
.front-dirs button:disabled { opacity: 0.4; cursor: not-allowed; }

/* ===== Share roles + public link ===== */
.share-role-select {
  margin-bottom: 0;
  width: auto;
  flex-shrink: 0;
  padding: 9px 8px;
}
.share-role-select.sm { padding: 4px 6px; font-size: 12px; }
.share-chip .share-user { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.share-chip { gap: 8px; }
.share-hint { font-size: 12px; color: var(--faint); margin-bottom: 10px; line-height: 1.4; }
.share-link-row { display: flex; gap: 8px; margin-bottom: 10px; }
.share-link-row .field-input { margin-bottom: 0; font-size: 12px; }
.share-link-actions { display: flex; gap: 8px; }
.share-link-actions button { flex: 1; }

/* ===== Settings (auto-save) ===== */
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.settings-label { font-weight: 700; color: #f0f4ff; font-size: 14px; }
.settings-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* Account Settings stacks two self-contained sections in one popup, so each carries its
   own heading and is ruled off from the next. The default field/error/action margins add
   up to a modal's worth of gap under the last field, which is why they are tightened
   here: each button reads as belonging to the section above it. */
.settings-section + .settings-section {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}
.settings-head {
  font-size: 13px;
  font-weight: 700;
  color: #f0f4ff;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 12px;
}
.settings-head + .settings-sub { margin: -6px 0 12px; line-height: 1.45; }
.settings-section .field-input { margin-bottom: 12px; }
.settings-section .field-input:last-of-type { margin-bottom: 0; }
/* Empty until something goes wrong: no reserved band under the field. */
.settings-section .auth-error { min-height: 0; margin: 8px 0 0; }
.settings-section .auth-error:empty { display: none; }
.settings-section .modal-actions { margin-top: 12px; }
.switch { position: relative; display: inline-block; width: 44px; height: 24px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch-slider {
  position: absolute;
  inset: 0;
  cursor: pointer;
  background: var(--border);
  border-radius: 99px;
  transition: background 0.15s;
}
.switch-slider::before {
  content: "";
  position: absolute;
  height: 18px; width: 18px;
  left: 3px; bottom: 3px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.15s;
}
.switch input:checked + .switch-slider { background: var(--accent); }
.switch input:checked + .switch-slider::before { transform: translateX(20px); }

.button-outline-blue.sm { padding: 4px 10px; font-size: 12px; }
/* Same compact sizing for the filled button and plain fields, so a row of controls
   matches the dropdowns inside the attendance matrix rather than dwarfing them. */
.button-solid-blue.sm { padding: 4px 12px; font-size: 12px; }
.field-input.sm { padding: 4px 8px; font-size: 12px; }

/* ===== Rehearsal-date tab bar (Google-Sheets style) ===== */
.tabbar {
  display: flex;
  align-items: stretch;
  gap: 8px;
  flex-shrink: 0;
  background: var(--panel-2);
  border-top: 1px solid var(--border);
  padding: 0 10px;
  min-height: 40px;
  user-select: none;
}
.tabbar-label {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  padding: 4px 10px 4px 0;
  margin-right: 4px;
  border-right: 1px solid var(--border);
}
.tabbar-label-text {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--faint);
}
.tabbar-nav { display: flex; gap: 4px; }
.tab-scroll {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 18px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel);
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.tab-scroll:hover:not(:disabled) { background: var(--accent); color: #0e1525; }
.tab-scroll:disabled { opacity: 0.35; cursor: default; }
.tabbar-tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none; /* arrows/wheel handle scrolling; never show a scrollbar */
}
.tabbar-tabs::-webkit-scrollbar { height: 0; width: 0; }

/* Hide the scroll arrows on touch devices — users swipe to scroll instead. */
@media (hover: none) and (pointer: coarse) {
  .tabbar-nav { display: none; }
}
.tab {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  max-width: 220px;
  padding: 0 10px 0 14px;
  border: 1px solid transparent;
  border-bottom: none;
  border-right-color: var(--border); /* divider line between tabs */
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  align-self: flex-end;
  margin-bottom: -1px;
  padding-top: 6px;
  padding-bottom: 7px;
}
.tab:hover { background: var(--panel); color: var(--text); }
.tab.dragging { opacity: 0.45; }
.tab.selected {
  background: rgba(56, 189, 248, 0.16);
  color: var(--text);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.tab.active {
  background: var(--bg);
  color: var(--accent);
  border-color: var(--border);
  border-bottom: 1px solid var(--bg);
}
.tab .tab-name {
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 170px;
}
.tab .tab-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--faint);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  visibility: hidden;
}
.tab:hover .tab-close,
.tab.active .tab-close { visibility: visible; }
.tab .tab-close:hover { background: var(--absent); color: #fff; }
.tab-add {
  flex-shrink: 0;
  align-self: center;
  margin-left: 4px;
  width: 28px;
  height: 28px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--accent);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.tab-add:hover { background: var(--accent); color: #0e1525; }
/* View-only users can switch tabs but not create/delete them. */
body.view-only .tab-add,
body.view-only .tab .tab-close { display: none; }

/* Right-click context menu for tabs */
.tab-context-menu {
  position: fixed;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  min-width: 140px;
  padding: 5px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
}
.tab-context-menu[hidden] { display: none; }
.tab-context-menu button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.tab-context-menu button:hover { background: var(--bg); }
.tab-context-menu button.danger:hover { background: var(--absent); color: #fff; }

/* ===== Guest view page ===== */
.guest-badge {
  font-size: 12px;
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid var(--accent);
  padding: 5px 12px;
  border-radius: 99px;
}
.project-name-view { font-size: 14px; font-weight: 600; color: var(--text); }
.view-error {
  margin: 40px auto;
  max-width: 420px;
  text-align: center;
  color: var(--muted);
  font-size: 15px;
  border: 1px dashed var(--border);
  border-radius: 10px;
  padding: 40px 24px;
}

/* ===== Read-only (shared view-only) mode ===== */
/* The server refuses every one of these independently — this is only about not showing
   a viewer controls that can do nothing but fail. Import is here because both importers
   write into the OPEN project's roster, which a viewer does not own. Export, Visualise
   and Export Grid stay: they read, and a viewer is entitled to those. */
body.view-only #addMemberBtn,
body.view-only .pc-actions,
body.view-only .front-control,
body.view-only .appt-control,
body.view-only .menu-item[data-action="save"],
body.view-only .menu-item[data-action="share"],
body.view-only .menu-item[data-action="import"],
body.view-only .menu-item[data-action="importXlsx"],
body.view-only #autoSizeBtn,
body.view-only #gridConfigBtn,
body.view-only #editTabsBtn {
  display: none;
}
body.view-only .slot.filled { cursor: default; }

/* ===== Mobile control popups + Edit Tabs (components; shown via the mobile media query) ===== */
/* These are mobile-only affordances, hidden on desktop. */
.mobile-toolbar { display: none; }

/* Fixed visual order for the toolbar so it stays correct even after controls are
   relocated to the mobile popups and back (DOM order then no longer matters). */
.toolbar > #exportGridBtn { order: 0; }
.toolbar > #visualiseGridBtn { order: 1; }
.toolbar > #gridConfigBtn { order: 2; }
.toolbar > #autoSizeBtn { order: 3; }
.toolbar > .toolbar-divider:nth-of-type(1) { order: 4; }
.toolbar > .zoom-control { order: 5; }
.toolbar > .toolbar-divider:nth-of-type(2) { order: 6; }
.toolbar > #frontControl { order: 7; }
.toolbar > #apptControl { order: 8; }
.toolbar > #toolbarStats { order: 9; }

/* Relocated controls stack vertically inside the popups. */
.popup-controls { display: flex; flex-direction: column; gap: 16px; }
.popup-controls .zoom-control,
.popup-controls .front-control,
.popup-controls .appt-control { width: 100%; flex-wrap: wrap; }
/* Mobile: the three appointment checkboxes stay on one row under Leading Front. */
.popup-controls .appt-checks { flex: 1; justify-content: space-between; flex-wrap: nowrap; }
.popup-controls .zoom-control input[type="range"] { flex: 1; width: auto; }
.popup-controls .toolbar-stats { margin-left: 0; gap: 10px 16px; }
.popup-controls #gridConfigBtn,
.popup-controls #autoSizeBtn { align-self: flex-start; }

/* ===== Visualise Grid (3D) modal ===== */
.viz-modal { max-width: 940px; }
.viz-stage {
  position: relative;
  width: 100%;
  height: 62vh;
  min-height: 260px;
  background: #0e1525;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.viz-stage canvas { width: 100%; height: 100%; display: block; touch-action: none; }
.viz-nav {
  position: absolute;
  right: 10px;
  bottom: 10px;
  display: grid;
  grid-template-columns: repeat(2, 36px);
  gap: 6px;
}
.viz-nav button {
  width: 36px;
  height: 36px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(20, 27, 45, 0.85);
  color: var(--text);
  font-size: 16px;
  cursor: pointer;
}
.viz-nav button:hover { border-color: var(--accent); color: var(--accent); }
.viz-nav button[data-viz-nav="reset"] { grid-column: span 2; width: auto; }

/* Orbit / Move drag-mode toggle — visible on all devices (touch drags need it too). */
.viz-mode {
  position: absolute;
  left: 10px;
  top: 10px;
  height: 34px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: rgba(20, 27, 45, 0.85);
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
}
.viz-mode:hover { border-color: var(--accent); color: var(--accent); }
.viz-mode.pan { border-color: var(--accent); color: var(--accent); }

/* Edit Tabs list — mirrors the Load Project list, with a drag handle + per-row actions. */
.edit-tabs-list { display: flex; flex-direction: column; gap: 8px; max-height: 60vh; overflow-y: auto; }
.edit-tab-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-2);
}
.edit-tab-row.dragging { opacity: 0.5; border-color: var(--accent); }
.edit-tab-handle { cursor: grab; color: var(--muted); font-size: 18px; line-height: 1; padding: 0 2px; touch-action: none; user-select: none; }
.edit-tab-name { flex: 1; min-width: 0; font-weight: 600; color: #f0f4ff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.edit-tab-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }

/* ===== Mobile ===== */
/* Touch tablets (iPad etc.) stay in mobile mode in any orientation, even when their
   landscape width exceeds 820px. */
@media (max-width: 820px), (hover: none) and (pointer: coarse) {
  body { overflow: auto; height: auto; min-height: 100vh; }
  .layout { flex-direction: column; overflow: visible; }
  .sidebar { width: 100%; min-width: 0; border-left: none; border-top: 1px solid var(--border); }

  /* Two-row topbar: [P parade.sg]……[user Logout] on top, project name……[Project] /
     status below. display:contents flattens the two wrapper groups so their children
     place directly into this grid. Scoped to the editor topbar so the simpler guest
     (read-only) header is left untouched. */
  .topbar-editor {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "brand   user"
      "project menu";
    align-items: center;
    column-gap: 10px;
    row-gap: 6px;
    padding: 8px 12px;
  }
  .topbar-editor .topbar-left, .topbar-editor .topbar-right { display: contents; }
  /* Guest (read-only) topbar has no project controls — keep it a simple wrapping row. */
  .topbar:not(.topbar-editor) { flex-wrap: wrap; gap: 8px 10px; }
  /* Home carries Account Settings as well, which is three items too many for one line
     on a phone. Only there — the editor's topbar places .topbar-user into a grid area. */
  .topbar-home .topbar-user { flex-wrap: wrap; justify-content: flex-end; flex-shrink: 1; }
  .topbar-brand { grid-area: brand; }
  .topbar-user { grid-area: user; justify-self: end; }
  .topbar-divider { display: none; }
  .project-name-input {
    grid-area: project;
    width: 100%;
    max-width: none;
    min-width: 0;
    border-color: var(--border);
    text-overflow: ellipsis;
  }
  /* Project button on top, save status beneath it, both right-aligned. */
  .topbar-projectmenu {
    grid-area: menu;
    justify-self: end;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
  }
  .save-status { text-align: right; white-space: nowrap; }

  /* Standby pool stays a box at the bottom; long rosters scroll inside it (Task 3). */
  .pool-dropzone { max-height: 40vh; }

  /* The desktop toolbar is replaced by the Adjustments / Status popups. */
  .toolbar { display: none; }

  /* Pin the tab bar directly below the topbar and keep it there while the grid /
     attendance content (variable height) scrolls, so it never shifts position. */
  .workspace-col { overflow: visible; }
  .tabbar {
    order: -2;
    position: sticky;
    top: 0;
    z-index: 30;
    min-height: 46px;
    border-top: none;
    border-bottom: 1px solid var(--border);
  }
  .tabbar-label { display: none; }   /* Edit Tabs now lives in the mobile toolbar */

  /* Adjustments / Status row, pinned directly below the tab bar (grid tabs only). */
  .mobile-toolbar {
    order: -1;
    position: sticky;
    top: 46px;
    z-index: 29;
    display: flex;
    gap: 8px;
    padding: 8px 10px;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
  }
  body.on-overview .mobile-toolbar { display: none; }

  /* Deletion on mobile is via the Edit Tabs popup, not an on-tab ✕. */
  .tab .tab-close { display: none; }

  /* Attendance tab: bound the viewport so the matrix box fills it and scrolls
     internally (frozen header rows + horizontal columns), with no page-scroll fight. */
  body.on-overview { overflow: hidden; min-height: 0; height: 100vh; height: 100dvh; }
  body.on-overview .layout,
  body.on-overview .workspace-col { overflow: hidden; }
  body.on-overview .overview-panel { padding: 8px; }
}

/* Phone / portrait — stop the header & toolbar from squishing by wrapping
   each group onto its own row instead of crushing them with space-between. */
@media (max-width: 600px) {
  /* Topbar layout is handled by the two-row grid in the ≤820 block; only shrink the
     brand wordmark a touch more here. */
  .topbar-left h2 { font-size: 18px; }

  .toolbar { padding: 8px 12px; gap: 10px 12px; }
  .zoom-control { flex: 1 1 100%; }
  .zoom-control input[type="range"] { flex: 1; width: auto; }
  .front-control { flex: 1 1 100%; flex-wrap: wrap; }
  .appt-control { flex: 1 1 100%; flex-wrap: wrap; }
  .toolbar-stats { margin-left: 0; flex-basis: 100%; justify-content: space-between; }
  .toolbar-divider { display: none; }

  .workspace { padding: 14px; }

  /* iOS auto-zooms when focusing an input whose text is < 16px (e.g. opening
     the Add Person modal). Keeping inputs at 16px suppresses that zoom while
     leaving pinch-to-zoom on the grid intact. */
  .field-input,
  input[type="text"],
  input[type="number"],
  input[type="password"],
  select,
  textarea {
    font-size: 16px;
  }
}

/* Mobile tap-to-move: the person picked up by a double tap is highlighted until
   the next tap drops them on a tile or the standby pool. */
.slot.tap-picked,
.pool-card.tap-picked {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.35);
}

/* ===== Quick status menu (click a person on the grid) ===== */
.status-menu {
  position: fixed;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  min-width: 188px;
  padding: 5px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
}
.status-menu[hidden] { display: none; }
.status-menu button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.status-menu button:hover { background: var(--bg); }
.status-menu button.active { background: var(--bg); outline: 1px solid var(--accent); }
.status-menu button:disabled { opacity: 0.4; cursor: not-allowed; }
.status-menu button:disabled:hover { background: transparent; }
.status-menu .sm-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex-shrink: 0;
}
/* The action menu's Set Status carries the duration clock on the right of its own row,
   so the label button still fills the menu's width. */
.sm-row { display: flex; align-items: stretch; gap: 2px; }
.sm-row > button:first-child { flex: 1 1 auto; min-width: 0; }
.status-menu .sm-clock {
  flex: 0 0 auto;
  width: 30px;
  justify-content: center;
  padding: 0;
  color: var(--muted);
}
.status-menu .sm-clock:not(:disabled):hover { color: var(--text); }
.status-menu .sm-clock.has-dates { color: var(--accent); }

/* ===== Past Positions popup (a person's allocation across every rehearsal date) ===== */
.pp-scroll {
  display: flex;
  gap: 16px;
  overflow: auto;
  max-height: 70vh;
  padding: 4px 2px 10px;
  align-items: flex-start;
}
.pp-tab {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: 110px;
}
.pp-tab-name {
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  text-align: center;
  max-width: 240px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pp-noassign {
  font-size: 12px;
  color: var(--faint);
  font-style: italic;
  padding: 28px 12px;
  text-align: center;
}
.pp-stage {
  position: relative;
  display: inline-block;
  padding:
    calc(22px + var(--appt-top, 0px))
    calc(22px + var(--appt-right, 0px))
    calc(22px + var(--appt-bottom, 0px))
    calc(22px + var(--appt-left, 0px));
}
.pp-canvas {
  display: inline-grid;
  gap: 4px;
  position: relative;
}
/* Mini-grid counterpart of .appt-slot, with the appointment name under the box. */
.pp-appt {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--appt-line);
  border-radius: 5px;
  background: #131c2e;
}
.pp-appt.filled { border-style: solid; background: var(--panel-2); }
.pp-appt-label {
  position: absolute;
  top: 100%;
  left: -12px;
  right: -12px;
  text-align: center;
  font-size: 7px;
  font-weight: 700;
  font-family: monospace;
  color: var(--faint);
  line-height: 1.4;
}
.pp-slot {
  border: 1px dashed var(--border);
  border-radius: 5px;
  background: #131c2e;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pp-slot.filled { border-style: solid; background: var(--panel-2); }
.pp-front {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: rgba(56, 189, 248, 0.16);
  border: 1.5px solid var(--accent);
  border-radius: 5px;
  color: var(--accent);
  font-weight: 800;
  font-size: 7px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  overflow: hidden;
}
.pp-front.pos-north { top: calc(3px + var(--appt-top, 0px));    left: calc(22px + var(--appt-left, 0px)); right: calc(22px + var(--appt-right, 0px)); height: 15px; }
.pp-front.pos-south { bottom: calc(3px + var(--appt-bottom, 0px)); left: calc(22px + var(--appt-left, 0px)); right: calc(22px + var(--appt-right, 0px)); height: 15px; }
.pp-front.pos-west  { left: calc(2px + var(--appt-left, 0px));  top: calc(22px + var(--appt-top, 0px)); bottom: calc(22px + var(--appt-bottom, 0px)); width: 15px; }
.pp-front.pos-east  { right: calc(2px + var(--appt-right, 0px)); top: calc(22px + var(--appt-top, 0px)); bottom: calc(22px + var(--appt-bottom, 0px)); width: 15px; }
.pp-front .front-text { display: block; white-space: nowrap; flex: 0 0 auto; }
.pp-front.pos-west .front-text,
.pp-front.pos-east .front-text { transform: rotate(90deg); }

/* ===== Attendance tab (owner/editor only) ===== */
/* Pinned-first pseudo-tab. It has no contingent arranger, so on it the toolbar row is
   blanked, the sidebar is hidden, and the grid workspace is swapped for the single
   full-width Attendance panel. */
.tab.tab-overview { color: var(--accent); }
.tab.tab-overview.active { color: var(--accent); }

body.on-overview .toolbar > * { display: none; }
body.on-overview .sidebar { display: none; }
body.on-overview .workspace { display: none; }

.overview-panel { display: none; }
body.on-overview .overview-panel {
  display: flex;
  flex: 1;
  min-height: 0;
  padding: 16px;
  overflow: hidden;
}

.ov-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.ov-attendance { flex: 1; min-width: 0; }   /* fills the whole panel width */

/* The panel hosts both matrices; body.ov-prof swaps Attendance for Proficiency. */
.ov-proficiency { display: none; }
body.ov-prof .ov-attendance:not(.ov-proficiency) { display: none; }
body.ov-prof .ov-proficiency { display: flex; }

.ov-box-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.ov-box-title { font-weight: 700; font-size: 14px; color: #f0f4ff; }
/* Details + Multi-Select + Export sit together on the header's right; they wrap onto a
   second line rather than squeezing the title on a narrow phone. */
.ov-box-header-actions { display: flex; align-items: center; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
#attDetailsBtn.active, .ms-btn.active { background: var(--accent); color: #0e1525; }
.ov-empty { color: var(--faint); font-size: 13px; text-align: center; padding: 24px 12px; line-height: 1.5; }

/* Attendance: a frozen-header / frozen-first-column matrix that scrolls both ways.
   Column widths are fixed (min = max) so changing a status never resizes — and thus
   never shifts — a column. The horizontal scrollbar is always shown (overflow-x:
   scroll) so the side-to-side affordance is obvious; it simply doesn't move when the
   columns already fit. */
.ov-attendance-scroll { flex: 1; min-height: 0; overflow-x: scroll; overflow-y: auto; }
.att-table {
  --att-name: 190px;   /* frozen Personnel column */
  --att-col: 150px;    /* each standardised rehearsal-date column ("Fall Out Until Evening: n" fits) */
  border-collapse: separate;
  border-spacing: 0;
  font-size: 12px;
  width: max-content;
}
.att-table th, .att-table td {
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
}

/* Rehearsal-date columns: fixed, standardised width. */
.att-date, .att-summary, .att-cell {
  width: var(--att-col);
  min-width: var(--att-col);
  max-width: var(--att-col);
}
/* The dates row has a fixed height (room for a two-line wrapped name) so the sticky
   summary row's `top` offset below it is stable. */
.att-row-dates th {
  position: sticky;
  top: 0;
  z-index: 4;
  height: 46px;
  box-sizing: border-box;
  padding: 4px 8px;
  background: var(--panel-2);
  font-weight: 700;
  color: #f0f4ff;
  text-align: center;
}
.att-date { white-space: normal; overflow: hidden; overflow-wrap: anywhere; line-height: 1.2; }
.att-row-summary th {
  position: sticky;
  top: 46px;
  z-index: 4;
  padding: 4px 8px;
  background: var(--panel-2);
  font-weight: 600;
  color: var(--muted);
  text-align: center;
}
/* Summary cell: vertical text lines (Total, then one count per status). The extra
   right padding keeps the lines clear of the absolutely-placed sort button. */
.att-summary { position: relative; white-space: normal; text-align: left; padding: 6px 24px 6px 10px; line-height: 1.4; }
.att-sum-line { display: block; white-space: nowrap; }

/* Filter (sort) symbols on the summary header row: the Status corner cell's sorts
   names, each summary cell's sorts that rehearsal date's statuses. Both sit on the
   row's vertical centerline, level with the "Status" label. */
.att-sort-btn {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  padding: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--muted);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}
.att-sort-btn:hover { color: var(--text); border-color: var(--accent); }
.att-sort-btn.active { color: var(--accent); border-color: var(--accent); }
.att-sort-col { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); }

/* Frozen first column within the two header rows. These must outrank the column
   headers (z-index 4) so the corner labels stay on top while scrolling both ways;
   the qualified selectors beat the `.att-row-* th` specificity. position/top/
   background are inherited from those `th` rules above. */
.att-row-dates th.att-corner,
.att-row-summary th.att-corner-2 {
  left: 0;
  z-index: 6;
  width: var(--att-name);
  min-width: var(--att-name);
  max-width: var(--att-name);
  text-align: left;
}
.att-row-dates th.att-corner { color: #f0f4ff; }
.att-row-summary th.att-corner-2 { color: var(--muted); }
/* Personnel header cell: label + count on the left, + Add on the right. */
.att-corner-inner { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.att-corner-label { white-space: nowrap; }
.att-count { color: var(--muted); font-weight: 600; }
.att-add-btn {
  flex-shrink: 0;
  background: var(--accent);
  color: #0e1525;
  border: none;
  border-radius: 6px;
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}
.att-add-btn:hover { filter: brightness(1.08); }

.att-person {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--panel);
  width: var(--att-name);
  min-width: var(--att-name);
  max-width: var(--att-name);
  padding: 4px 10px;
  font-weight: 600;
  color: var(--text);
  text-align: left;
  vertical-align: middle;
}
.att-person-inner { display: flex; align-items: center; gap: 8px; }
/* Names wrap inside the fixed column rather than stretching it. */
.att-person-name { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; line-height: 1.25; }
.att-person-actions { display: flex; gap: 1px; flex-shrink: 0; }
.att-person-actions .icon-btn { width: 22px; height: 22px; font-size: 12px; }
.att-cell { padding: 3px 6px; text-align: center; background: var(--bg); }
.att-empty { padding: 26px 12px; text-align: center; color: var(--faint); background: var(--bg); }
/* The dropdown and its duration clock share the cell's first line; only the dropdown
   stretches. The duration goes underneath in smaller type — a <select> can only render
   one font, so keeping the dates inside the value made every column as wide as the
   longest status plus its dates. */
.att-cell-wrap { display: flex; align-items: center; gap: 3px; }
.att-cell-wrap .att-select { flex: 1 1 auto; min-width: 0; }
.att-dur {
  display: block;
  margin: 2px 26px 0 2px;   /* right margin keeps it clear of the clock above */
  font-size: 10px;
  line-height: 1.2;
  color: var(--faint);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.att-clock {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}
.att-clock:not(:disabled):hover { background: var(--panel-2); color: var(--text); }
.att-clock:disabled { opacity: 0.3; cursor: not-allowed; }
.att-clock.has-dates { color: var(--accent); }
.att-select {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 3px 6px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.att-select.s-present { color: var(--present); border-color: rgba(34,197,94,0.5); }
.att-select.s-absent_mc { color: var(--absent); border-color: rgba(239,68,68,0.5); }
.att-select.s-absent_loa { color: var(--absent); border-color: rgba(239,68,68,0.5); }
.att-select.s-rso_rsi { color: var(--fallout); border-color: rgba(245,158,11,0.5); }
.att-select.s-ld { color: var(--fallout); border-color: rgba(245,158,11,0.5); }
.att-select.s-extra { color: var(--extra); border-color: rgba(168,85,247,0.5); }
.att-select.s-fallout { color: var(--fallout); border-color: rgba(245,158,11,0.5); }
.att-select.s-fallout_evening { color: var(--fallout); border-color: rgba(245,158,11,0.5); }
.att-select.s-none { color: var(--none); }
/* Keep a readable dropdown background; per-option text colours are set inline in JS so
   the open list shows each status in its own colour rather than the selected one. */
.att-select option { background: var(--panel-2); }

/* Multi-select: cells picked for a mass designation carry a blue outline. Drawn inside
   the cell (outline-offset) so it can't nudge the fixed column widths. */
.att-cell.ms-cell {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  background: rgba(56,189,248,0.10);
}
/* While the Multi-Select button is on, a tap has to land on the cell rather than open
   its dropdown — that's what lets a phone build a selection without a shift key. */
body.ms-on .att-cell[data-ms-person] .att-select { pointer-events: none; }
body.ms-on .att-cell[data-ms-person] { cursor: pointer; }

/* Details view: single Height column. White bold header (qualified to beat the
   `.att-row-summary th` colour); each cell is a directly-editable number + cm unit. */
.att-row-summary th.att-height-head { font-weight: 700; color: #f0f4ff; }
.att-height-wrap { display: flex; align-items: center; gap: 6px; }
.att-height-input {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 3px 6px;
  font-size: 12px;
  font-family: inherit;
  text-align: center;
}
.att-height-input:focus { outline: none; border-color: var(--accent); }
.att-height-unit { flex-shrink: 0; color: var(--muted); font-size: 12px; }

/* Proficiency matrix: dropdown colours mirror Absent / Fall Out / Present / None; Star is a
   brighter green. */
.att-select.p-fair { color: var(--absent); border-color: rgba(239,68,68,0.5); }
.att-select.p-satisfactory { color: var(--fallout); border-color: rgba(245,158,11,0.5); }
.att-select.p-competent { color: var(--present); border-color: rgba(34,197,94,0.5); }
.att-select.p-star { color: var(--star); border-color: rgba(74,222,128,0.5); }
.att-select.p-none { color: var(--none); }
/* Absent / attendance unset on that date: no dropdown, just a greyed-out marker. */
.prof-absent { display: block; padding: 4px 0; color: var(--none); font-weight: 600; opacity: 0.7; }
/* Non-Present attendance bracket under the dropdown (colour set inline per status). */
.prof-note { margin-top: 2px; font-size: 11px; font-weight: 600; line-height: 1.2; }

/* Phone / tablet: with the tab bar pinned to the top, the Attendance box fills the rest
   of the (bounded) viewport and scrolls internally — both axes — so the frozen date /
   status header rows and personnel column stay put, and there's no page-scroll fight. */
@media (max-width: 820px), (hover: none) and (pointer: coarse) {
  .ov-attendance { flex: 1; min-height: 0; height: auto; }
}

/* ===== Suggested Line Up ===== */
/* Tabs generated by the Suggested Line Up flow carry a permanent red outline; a
   shift-click selection temporarily takes over with the usual accent outline. */
.tab.tab-suggested { box-shadow: inset 0 0 0 2px var(--absent); }
.tab.tab-suggested.selected { box-shadow: inset 0 0 0 2px var(--accent); }

/* Setup popup: vertically scrolling rehearsal-tab checklist. */
#suggestModal .config-row { margin-top: 8px; }
.suggest-tab-list { display: flex; flex-direction: column; gap: 2px; max-height: 200px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; padding: 8px; margin: 8px 0 10px; }
.suggest-tab-row { display: flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 6px; font-size: 13px; color: var(--text); cursor: pointer; }
.suggest-tab-row:hover { background: var(--panel-2); }
.suggest-tab-row input { accent-color: var(--accent); cursor: pointer; flex-shrink: 0; }
.suggest-tab-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Suggested Personnel popup: ranked shortlist table (reuses the matrix skeleton). */
.sl-modal { width: 720px; max-width: 94vw; }
.shortlist-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.shortlist-scroll { max-height: 58vh; overflow: auto; border: 1px solid var(--border); border-radius: 8px; }
.shortlist-scroll .att-table { --att-col: 128px; min-width: 100%; }
.shortlist-scroll .sl-col { padding: 6px 10px; }
/* People ranked past the cutoff stay visible but dimmed; the separator row marks the
   boundary while the table reads in ranking order. */
.sl-excluded { opacity: 0.45; }
.sl-cutoff-row td { padding: 4px 8px; text-align: center; font-size: 11px; font-weight: 700; color: var(--absent); background: rgba(239, 68, 68, 0.12); border-top: 2px solid var(--absent); border-bottom: 2px solid var(--absent); }

/* ===== After Action Review ===== */
/* Two rows — the rehearsal dates, then a box under each — kept as one table on every
   screen, scrolling sideways when there are more dates than fit. The dates read across
   the top exactly as they do on the tab bar, which is the point of the shape. */
.aar-modal { width: 760px; max-width: 94vw; }
.aar-intro { margin-bottom: 16px; }
/* The padding under the table is what keeps the horizontal scrollbar clear of its
   bottom edge instead of sitting flush against it. */
.aar-scroll {
  overflow-x: auto;
  padding-bottom: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.aar-table { border-collapse: collapse; }
/* Shaded like the Suggested Personnel shortlist: header rows on the raised panel tone,
   the cells themselves on the page tone, so the boxes read as sunk into the table. */
.aar-table th,
.aar-table td {
  border: 1px solid var(--border);
  padding: 8px;
  vertical-align: top;
  min-width: 240px;
  width: 240px;
  background: var(--bg);
}
.aar-table thead th {
  background: var(--panel-2);
  color: #f0f4ff;
  font-size: 12px;
  font-weight: 700;
  text-align: left;
  white-space: nowrap;
}
/* The row labels are the table's spine: they stay put while the dates scroll past.
   They and the date headers are also the text a scrollbar drag would otherwise sweep
   into a selection, so neither takes one; the boxes stay selectable. */
.aar-table th,
.aar-table td.aar-row-label { user-select: none; }
.aar-table th.aar-row-label,
.aar-table td.aar-row-label {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--panel-2);
  min-width: 130px;
  width: 130px;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
}
.aar-box {
  width: 100%;
  box-sizing: border-box;
  /* Tall enough to write a real review into; the grip still resizes it either way. */
  height: 300px;
  min-height: 120px;
  resize: vertical;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px;
  color: var(--text);
  font: inherit;
  font-size: 13px;
  line-height: 1.5;
}
.aar-box:focus { outline: none; border-color: var(--accent); }
.aar-box[readonly] { color: var(--muted); cursor: default; }

/* ===== Online-edition chrome ===== */
/* Pages and controls that only exist on parade.sg: the login policy hint and legacy
   banner, the /rescue and /admin lists, and the save-conflict prompt. */

.password-hint {
  margin: 4px 2px 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}

/* Shown on the login page when this browser still holds offline-app data. */
.legacy-banner {
  margin: 0 0 14px;
  padding: 10px 12px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: rgba(59, 130, 246, 0.10);
  font-size: 13px;
  line-height: 1.6;
}
.legacy-banner .auth-link { white-space: nowrap; }

/* Full-width call-to-action button on /install and /rescue. */
.install-cta {
  display: block;
  margin: 14px 0 6px;
  text-align: center;
  text-decoration: none;
}

.rescue-warning { color: var(--fallout); }

.rescue-list, .admin-list { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.rescue-list .project-row, .admin-list .project-row { cursor: default; }
/* A disabled account stays legible but visibly inert. Its name is not a way in either:
   entering a disabled account is refused server-side, so the row does not offer it. */
.admin-list .row-disabled { opacity: 0.55; }
.admin-list .project-row .pr-main { cursor: pointer; }
.admin-list .row-disabled .pr-main { cursor: default; }
.shared-tag.tag-danger { background: rgba(239, 68, 68, 0.18); color: var(--absent); }

/* ===== Acting as another account ===== */
/* A site admin standing in someone else's account. Deliberately loud and in the page
   flow rather than floating: every page below it holds another user's data, and an
   admin must never mistake it for their own. */
.acting-banner {
  flex-shrink: 0;
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: 8px 16px;
  background: rgba(239, 68, 68, 0.9);
  border-bottom: 1px solid rgba(0, 0, 0, 0.35);
  color: #fff;
  font-size: 13px;
}
.acting-banner strong { font-weight: 800; }
.acting-banner button {
  padding: 4px 12px;
  font-size: 12px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.25);
  color: #fff;
  font-weight: 600;
  cursor: pointer;
}
.acting-banner button:hover { background: rgba(0, 0, 0, 0.45); }

/* The conflict prompt must be answered, so it has no close affordance — keep its
   backdrop visually heavier than an ordinary dismissible modal. */
#conflictModal .modal-header { border-bottom: none; }
#conflictModal .settings-sub { margin: 0 2px 10px; }

/* ===== Parades ===== */
/* A parade LINKS contingents that stay independent projects. The Parade button sits
   left of the Project button; on mobile both stay on one row with the save status
   beneath them. */
.topbar-btnrow { display: flex; align-items: center; gap: 8px; }
.button-outline-grey.sm,
.button-outline-blue.sm { padding: 4px 10px; font-size: 12px; }

/* The board popup: contingents laid out on the parade board, with a ＋ on each side to
   grow it by a row/column (disabled at the 9×9 cap). */
.cg-modal { width: 700px; max-width: 94vw; }
.cg-hint { font-size: 12px; color: var(--muted); line-height: 1.4; margin-top: 10px; }
.cg-link-row { display: flex; gap: 8px; flex-wrap: wrap; }
.cg-link-row button { flex: 1 1 220px; }
/* Both rows set display, which beats the UA stylesheet's [hidden] { display: none } —
   without these the two link buttons stay on screen after the parade is created. */
.cg-link-row[hidden] { display: none; }
.cg-linked-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel-2);
}
.cg-linked-row[hidden] { display: none; }
.cg-parade-name { font-size: 15px; font-weight: 700; color: #f0f4ff; overflow-wrap: anywhere; }
.cg-board-wrap {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 34px;
  grid-template-rows: 34px auto 34px;
  grid-template-areas:
    ".    top    ."
    "left board  right"
    ".    bottom .";
  gap: 6px;
  align-items: center;
  justify-items: center;
  margin-top: 10px;
}
/* Until this contingent is on a parade there is nothing to arrange: the board is shown
   so its purpose is obvious, but it is inert. */
.cg-board-wrap.cg-disabled { opacity: 0.4; pointer-events: none; }
/* Each side carries its ＋ (grow) and − (remove that row/column), outside the frame. */
.cg-side { display: flex; gap: 4px; }
.cg-side-top { grid-area: top; }
.cg-side-bottom { grid-area: bottom; }
.cg-side-left { grid-area: left; flex-direction: column; }
.cg-side-right { grid-area: right; flex-direction: column; }
.cg-expand,
.cg-shrink {
  width: 30px;
  height: 30px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
  color: var(--accent);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.cg-shrink { color: var(--muted); }
.cg-expand:hover:not(:disabled) { background: var(--accent); color: #0e1525; }
.cg-shrink:hover:not(:disabled) { background: var(--absent); border-color: var(--absent); color: #fff; }
.cg-expand:disabled,
.cg-shrink:disabled { opacity: 0.3; cursor: default; }
/* The frame marks the board's limit and sits outside its scrollbars. */
.cg-board-scroll {
  grid-area: board;
  width: 100%;
  max-height: 46vh;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px;
  background: rgba(14, 21, 37, 0.35);
}
.cg-board { display: grid; gap: 8px; }
.cg-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 116px;
  padding: 8px;
  border: 2px dashed var(--border);
  border-radius: 10px;
  text-align: center;
}
.cg-cell.empty { cursor: pointer; }
.cg-cell.empty:hover { border-color: var(--accent); }
.cg-cell.empty:hover .cg-plus { color: var(--accent); }
.cg-plus { font-size: 26px; line-height: 1; color: var(--faint); }
/* Another contingent reads white; the one you have open is accented and says so. */
.cg-cell.filled {
  border-style: solid;
  border-color: #f0f4ff;
  background: var(--panel-2);
  cursor: pointer;
}
.cg-cell.current { border-color: var(--accent); background: rgba(56, 189, 248, 0.10); }
/* A saluting dias is board furniture, not a contingent: red, name + Delete only. */
.cg-cell.cg-dias { border-color: var(--absent); background: rgba(239, 68, 68, 0.10); }
.cg-cell.cg-dias .cg-name { color: #fecaca; }
.cg-cell.dragging { opacity: 0.45; }
.cg-cell.picked,
.cg-cell.drop-target { box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.35); }
.cg-name { font-size: 13px; font-weight: 700; color: #f0f4ff; overflow-wrap: anywhere; }
.cg-owner { font-size: 11px; color: var(--muted); }
.cg-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.cg-current { font-size: 11px; font-weight: 600; color: var(--accent); }
.cg-locked { font-size: 11px; font-weight: 600; color: var(--fallout); }

/* Entering the parade and administering it sit side by side, equally weighted: on the
   parade page the first is greyed out, and the second is why the row is still there. */
.cg-view-row { margin-top: 12px; display: flex; gap: 8px; }
.cg-view-row button { flex: 1; }

/* The parades a user administers, in the Link popup. */
.parade-join-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
.parade-join-row:last-child { border-bottom: none; }
.parade-join-name { font-size: 13px; font-weight: 600; color: #f0f4ff; }
.parade-join-meta { font-size: 11px; color: var(--muted); }

/* ===== Parade-level rehearsal dates ===== */
/* A lock sits where the ✕ would be. Nothing else marks these: on a linked contingent
   every tab in the bar is one, so an outline would be decorating the whole bar. */
.tab .tab-lock {
  flex-shrink: 0;
  font-size: 10px;
  line-height: 1;
  opacity: 0.75;
  cursor: default;
  margin-left: 2px;
}
.tab.active .tab-lock { opacity: 1; }

/* Actions a parade-level date does not support stay in place, greyed, so the menu still
   reads as the full list of what a tab can do rather than quietly losing rows. */
.tab-context-menu button.disabled,
.edit-tab-actions button.disabled,
.edit-tab-handle.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.tab-context-menu button.disabled:hover { background: none; color: inherit; }

.tab-name-notice {
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
  border-left: 2px solid var(--border);
  padding: 2px 0 2px 8px;
  margin: 8px 2px 0;
}
.tab-name-notice[hidden] { display: none; }

/* ===== Corresponding tabs when linking to a parade ===== */
/* Left column: the contingent's own tabs. Right: the parade's dates, in the parade's
   order. Tap to pick a tab up, tap to put it down — including into the discard pile,
   which is what the link deletes. With a mouse it can be dragged straight there. */
.lt-hint {
  font-size: 12px;
  color: var(--accent);
  margin: 6px 2px 10px;
  min-height: 16px;
}
.lt-table {
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.lt-head,
.lt-row {
  display: grid;
  grid-template-columns: 1fr 28px 1fr;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
}
.lt-head {
  background: var(--panel-2);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.lt-row { border-top: 1px solid var(--border); }
.lt-arrow { text-align: center; color: var(--muted); }
.lt-parade-date { font-size: 13px; font-weight: 600; color: var(--text); }

.lt-slot {
  min-height: 32px;
  display: flex;
  align-items: center;
  padding: 3px;
  border: 1px dashed transparent;
  border-radius: 8px;
}
.lt-slot.empty { border-color: var(--border); }
/* Only while a tab is lifted: shows every place it can be put down. */
.lt-slot.droppable { border-color: var(--accent); background: rgba(56, 189, 248, 0.06); }
.lt-blank { font-size: 12px; color: var(--muted); font-style: italic; padding-left: 4px; }

.lt-chip {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--border);
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  /* A mouse drag starts on the chip; without this it would sweep a text selection along. */
  user-select: none;
}
.lt-chip:hover { border-color: var(--accent); }
.lt-chip.held {
  border-color: var(--accent);
  background: rgba(56, 189, 248, 0.16);
  box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.35);
}

.lt-discard-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--absent);
  margin: 14px 2px 6px;
}
.lt-discard {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 44px;
  align-items: center;
  padding: 8px;
  border: 1px dashed var(--absent);
  border-radius: 8px;
  background: rgba(239, 68, 68, 0.05);
}
.lt-discard.droppable { border-style: solid; }

/* Mouse drag (touch keeps tap-to-lift). The ghost is the chip itself, following the
   cursor; pointer-events:none is what lets elementFromPoint read the zone underneath. */
.lt-chip-ghost {
  position: fixed;
  z-index: 3000;
  pointer-events: none;
  opacity: 0.92;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.45);
}
.lt-slot.lt-drop-hover,
.lt-discard.lt-drop-hover {
  border-style: solid;
  border-color: var(--accent);
  background: rgba(56, 189, 248, 0.14);
}

.lt-export-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 12px 2px 0;
}
.lt-export-hint { font-size: 11px; color: var(--muted); }

/* Final warning: the same three groups, itemised, before anything is written. */
.lt-summary { margin: 4px 2px 0; max-height: 320px; overflow-y: auto; }
.lt-sum-block { margin-bottom: 12px; }
.lt-sum-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 4px;
}
.lt-sum-block.danger .lt-sum-title { color: var(--absent); }
.lt-summary ul { margin: 0; padding-left: 18px; }
.lt-summary li { font-size: 13px; color: var(--text); line-height: 1.6; }
.lt-sum-from { color: var(--muted); }
.lt-sum-to { font-weight: 600; }

@media (max-width: 620px) {
  .lt-head, .lt-row { grid-template-columns: 1fr 20px 1fr; gap: 4px; padding: 8px 6px; }
  .lt-chip { font-size: 12px; padding: 5px 8px; }
}

/* ===== Parade Master Mode page (/parade) ===== */
/* Its own page, not a mode of the editor: no contingent is open, so the project name,
   the Project menu and the save status have nothing to say and the parade's own name
   takes the middle of the bar. */
.topbar { position: relative; }
.topbar-centre {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  pointer-events: auto;
}
.topbar-centre[hidden] { display: none; }
/* Wide enough that a full formal parade name ("Officer Commissioning Parade 127/26")
   sits inside the box rather than scrolling within it; a short name simply centres in
   the same box, so the title does not jump about while it is being typed. */
.parade-name-input { font-size: 16px; font-weight: 700; color: #f0f4ff; text-align: center; width: 34ch; max-width: 42vw; }
.parade-name-input:read-only { border-color: transparent; cursor: default; }
.parade-name-input:read-only:hover { border-color: transparent; }
body.parade-page #projectName,
body.parade-page .topbar-divider,
body.parade-page .menu-wrap,
body.parade-page .save-status { display: none; }

/* ===== Parade view ===== */
/* Fills the workspace column: every contingent drawn at its board position for the open
   rehearsal date, on a pannable / zoomable field. */
.parade-stage { display: none; }
.parade-bar { display: none; }
.parade-panel { display: none; }
body.parade-view .parade-stage {
  display: block;
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  /* Dragging the field must never start a text selection across the contingents. */
  user-select: none;
  -webkit-user-select: none;
}
body.parade-view .parade-stage:active { cursor: grabbing; }
body.parade-view .workspace,
body.parade-view .overview-panel,
body.parade-view .sidebar,
body.parade-view .toolbar,
body.parade-view .mobile-toolbar { display: none; }
/* Desktop manages the parade's dates from the tab bar itself — right-click, double-click,
   drag — so Edit Tabs is mobile's alone, exactly as it is in the contingent editor. */
#paradeEditTabsBtn { display: none; }
/* The parade button bar takes the place of the toolbar / mobile grid-button row. */
body.parade-view .parade-bar {
  order: -1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
/* The pinned Overview panel replaces the field on its own tab. */
body.parade-view.on-parade-panel .parade-stage,
body.parade-view.on-parade-panel .parade-bar { display: none; }
body.parade-view.on-parade-overview #paradeOverviewPanel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex: 1;
  min-height: 0;
  padding: 16px;
  overflow: hidden;
}
/* The table box takes the leftover height so the panel fills the workspace rather than
   floating above the page backdrop. */
#paradeOverviewPanel .ov-box { flex: 1; min-height: 0; }
.perm-body { display: flex; flex-direction: column; gap: 10px; padding: 12px 14px; min-height: 0; }
/* In the Editor Permissions popup the two blocks are the popup's whole content, so they
   carry their own separator rather than the panel boxes they used to sit in. */
#paradeEditorPermsModal .perm-body { padding: 12px 0; }
#paradeEditorPermsModal .perm-body + .perm-body { border-top: 1px solid var(--border); }
/* The roles table scrolls inside the popup instead of pushing it past its max height. */
#paradeEditorPermsModal #paradeUsersScroll { max-height: 42vh; }
.perm-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.perm-code {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--accent);
  padding: 6px 12px;
  border: 1px dashed var(--border);
  border-radius: 8px;
}
.perm-note { font-size: 12px; color: var(--muted); line-height: 1.45; }
.perm-note b { color: var(--text); }
.perm-intro { display: flex; flex-direction: column; gap: 3px; }
/* The add-by-username row is three controls, not a full-width form: bounded so it does
   not stretch across the whole panel. */
.perm-add-row { max-width: 560px; }
.perm-add-row .field-input { margin-bottom: 0; }
.perm-notes-input { margin-bottom: 0; min-width: 180px; }
.perm-scope { display: flex; flex-wrap: wrap; gap: 6px; }
.perm-scope label { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text); cursor: pointer; }
.perm-scope input { accent-color: var(--accent); cursor: pointer; }

.parade-bar-hint { font-size: 12px; color: var(--muted); }
.parade-canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; }
.parade-slot { position: absolute; display: flex; align-items: center; justify-content: center; }
.parade-cg { display: flex; flex-direction: column; align-items: center; }
.parade-cg-name {
  height: 22px;
  font-size: 13px;
  font-weight: 700;
  color: #f0f4ff;
  white-space: nowrap;
}
.parade-cg-stage { border-radius: 12px; }
.parade-cg-stage .grid-canvas { padding: 0; }
/* Saluting dias: a thin red bar as long as the contingent it sits under. */
.parade-dias-block { display: flex; align-items: center; justify-content: center; height: 100%; }
.parade-dias {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--absent);
  border-radius: 6px;
  background: rgba(239, 68, 68, 0.14);
  color: #fecaca;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
}
.parade-stage .slot { cursor: default; }
.parade-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 13px; }
.parade-empty[hidden] { display: none; }

/* ===== Parade Overview table ===== */
/* One row per contingent, one column per parade-wide rehearsal date. Reuses the
   attendance matrix skeleton (frozen header row + frozen first column). */
.pov-table {
  --pov-name: 200px;
  --pov-col: 240px;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 12px;
}
.pov-table th,
.pov-table td {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 8px 10px;
  vertical-align: top;
  text-align: left;
}
.pov-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--panel-2);
  color: #f0f4ff;
  font-weight: 700;
  white-space: nowrap;
}
/* Overview only: the rehearsal-date headers sit centred over their column. The
   Contingents corner keeps its label left, with its sort button hard right. */
#paradeOverviewScroll .pov-table thead th { text-align: center; }
#paradeOverviewScroll .pov-table thead th.pov-name-col { text-align: left; }
.pov-table th.pov-name-col,
.pov-table td.pov-name-col {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--panel-2);
  min-width: var(--pov-name);
  max-width: var(--pov-name);
  width: var(--pov-name);
}
.pov-table thead th.pov-name-col { z-index: 4; }
.pov-table td { min-width: var(--pov-col); max-width: var(--pov-col); width: var(--pov-col); background: var(--panel); }
.pov-cg-name { font-weight: 700; color: #f0f4ff; overflow-wrap: anywhere; }
.pov-cg-owner { font-size: 11px; color: var(--muted); margin-top: 2px; }
.pov-head { font-weight: 800; color: #f0f4ff; letter-spacing: 0.02em; }
.pov-present { color: var(--text); }
.pov-present b { color: var(--present); }
/* Average competency reads like the attendance headline under it. Both figures are
   recoloured inline by the Overview's thresholds; these are only the fallbacks for when
   there is nothing to band (no personnel, or nobody graded). */
.pov-avg { color: var(--text); }
.pov-avg b { color: var(--muted); }

/* Attendance / Competency each live in their own raised box inside the cell. Collapsed
   they show only their headline; the caret unfolds that box's status dropdowns, one box
   at a time, so a wide table stays readable. */
.pov-group {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 8px;
}
.pov-group + .pov-group { margin-top: 8px; }
.pov-group > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.pov-group > summary::-webkit-details-marker { display: none; }
.pov-group-lines { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pov-caret { flex-shrink: 0; color: var(--muted); font-size: 13px; line-height: 1; transition: transform 0.15s; }
.pov-group[open] > summary .pov-caret { transform: rotate(180deg); color: var(--accent); }
.pov-group[open] > .pov-drops { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }

/* The status / competency dropdowns sit side by side and wrap within the box. */
.pov-drops { display: flex; flex-wrap: wrap; gap: 4px; }
.pov-drop { min-width: 0; }
.pov-drop > summary {
  list-style: none;
  cursor: pointer;
  font-size: 11px;
  padding: 3px 7px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--text);
  white-space: nowrap;
}
/* The chips sit on the raised box, so they take the darker cell tone to stay legible. */
.pov-group .pov-drop > summary { background: var(--panel); }
.pov-drop > summary::-webkit-details-marker { display: none; }
.pov-drop[open] > summary { border-color: var(--accent); color: var(--accent); }
.pov-names {
  margin-top: 4px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: rgba(14, 21, 37, 0.5);
  font-size: 11px;
  line-height: 1.5;
  color: var(--muted);
  max-height: 160px;
  overflow: auto;
}
.pov-empty-cell { color: var(--faint); font-style: italic; }

/* The AAR block reads like the two tallies above it: one line collapsed, the review in
   full behind the caret, with the contingent's own line breaks kept. */
.pov-aar-line {
  color: var(--muted);
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pov-aar-text {
  flex: 1 1 100%;   /* it sits in .pov-drops, which lays its chips out in a row */
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: rgba(14, 21, 37, 0.5);
  font-size: 11px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 200px;
  overflow: auto;
}

/* ===== Overview Settings — the threshold number lines =====
   One track per metric, painted with the three bands it defines, and two handles sitting
   on top of it at the values yellow and green start at. Built by hand rather than from
   two range inputs: the two boundaries share one line, and the paint under them has to
   follow the drag. */
.thr-block { margin-top: 16px; }
.thr-title { font-weight: 700; color: #f0f4ff; margin-bottom: 10px; }
.thr-line {
  position: relative;
  height: 22px;
  cursor: pointer;
  touch-action: none;   /* a drag on the line must not scroll the popup behind it */
}
.thr-track {
  position: absolute;
  inset: 6px 0;
  display: flex;
  border-radius: 5px;
  overflow: hidden;
  border: 1px solid var(--border);
}
.thr-band { height: 100%; }
.thr-red { background: #ef4444; }
.thr-yellow { background: #f59e0b; }
.thr-green { background: #22c55e; }
.thr-handle {
  position: absolute;
  top: 0;
  width: 14px;
  height: 22px;
  margin-left: -7px;   /* centres the handle on its value, including at 0% and 100% */
  border-radius: 4px;
  background: var(--panel-2);
  border: 2px solid #f0f4ff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  cursor: grab;
}
.thr-handle:focus-visible { outline: none; border-color: var(--accent); }
.thr-handle:active { cursor: grabbing; }
.thr-scale {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  font-size: 11px;
  color: var(--faint);
}
.thr-legend { margin-top: 6px; font-size: 12px; color: var(--muted); }

.thr-preview { margin-top: 18px; }
.thr-preview-title { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.thr-preview-cell {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 13px;
}
.thr-preview-gap { margin-top: 8px; }

@media (max-width: 820px), (hover: none) and (pointer: coarse) {
  /* Bound the viewport so the field fills it and pans inside itself, exactly like the
     Attendance tab does. The button bar sits directly below the pinned tab bar. */
  body.parade-view { overflow: hidden; min-height: 0; height: 100vh; height: 100dvh; }
  body.parade-view .layout,
  body.parade-view .workspace-col { overflow: hidden; }
  body.parade-view .parade-bar { padding: 8px 10px; }
  body.parade-view.on-parade-overview #paradeOverviewPanel { padding: 8px; }
  /* :not([hidden]) so the role check in JS still wins — a contingent admin has nothing to
     manage and never sees it. */
  #paradeEditTabsBtn:not([hidden]) { display: inline-block; }
  /* Two full-width buttons stacked, the way the single one used to read. */
  .cg-view-row { flex-direction: column; }

  /* The topbar is a 2×2 grid on mobile, so the parade's name takes the project name's
     cell rather than being absolutely centred over the top of it. */
  .topbar-centre {
    position: static;
    transform: none;
    grid-area: project;
    min-width: 0;
  }
  .parade-name-input { width: 100%; max-width: none; text-align: left; border-color: var(--border); }
  .parade-name-input:read-only { border-color: var(--border); }

  /* Narrower tiles so a 3×3 board fits a phone without scrolling sideways; the buttons
     stack inside them and the board still scrolls once it grows past 3 columns. */
  .cg-board { --cg-cell-min: 96px; }
  .cg-cell { min-height: 130px; padding: 6px; gap: 4px; }
  .cg-name { font-size: 12px; }
  .cg-owner { font-size: 10px; }
  .cg-actions { gap: 4px; }
  .cg-actions .sm { padding: 3px 6px; font-size: 11px; }
  .cg-board-scroll { padding: 6px; }
}
