:root {
  --accent: #2f6fed;
  --saved: #2a8a3e;
  --error: #d33;
  --purple: #7c3aed;
}

/* Only the main table's frame breaks out of the shared body's
   max-width:1100px cap - everything else on the page (header, filter-bar,
   and the separate trash panel/table) stays within it, that layout was
   fine as-is. The classic "full-bleed within a centered container" trick:
   100vw + negative margins re-centers this element on the viewport
   instead of the (narrower) body. Scoped to the #table-scroll id
   specifically, not the shared .table-scroll class, so it doesn't also
   catch #trash-table-scroll. #schedule below keeps width:auto (overriding
   the shared table's width:100%) so the table itself doesn't stretch to
   fill this wider frame and grow its columns - it still sizes to its own
   content/min-width like before, just with more room to do that in before
   a horizontal scrollbar kicks in. */
#table-scroll {
  width: 100vw;
  margin-left: calc(-50vw + 50%);
  margin-right: calc(-50vw + 50%);
}

#schedule {
  width: auto;
  margin: 0 auto;
}

@media (prefers-color-scheme: dark) {
  :root {
    --accent: #6ea1ff;
    --saved: #5cc47a;
    --error: #ff6b6b;
    --purple: #b794f6;
  }
}

/* ---- Login view ---- */

#login-view {
  max-width: 320px;
  margin: 3rem auto;
}

#login-view h1 { font-size: 1.2rem; }

#login-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

#login-form input {
  font-size: 1.05rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
}

#login-form button {
  padding: 0.7rem;
  font-size: 1.05rem;
  border: none;
  border-radius: 8px;
  background: var(--accent);
  color: white;
  cursor: pointer;
}

.error { color: var(--error); }

#forbidden-view {
  max-width: 320px;
  margin: 3rem auto;
  text-align: center;
}

#forbidden-view button {
  padding: 0.5rem 1rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: inherit;
  cursor: pointer;
}

.status-error {
  color: var(--error);
  opacity: 1;
  font-weight: 600;
}

/* Admin has one extra column (hide checkbox) versus the public page's
   shared table styling, so it needs a bit more minimum width. */
table {
  min-width: 126ch;
}

/* The shared styles.css column rules (nth-child 2 = Productie, nth-child 3 =
   √) are tuned for the public page's layout. Admin has two extra leading
   columns (select checkbox, expand arrow) shifting everything over by two,
   so those rules land on the wrong columns here - undo them and reapply to
   the correct columns (4 = Productie, 5 = √). */
#schedule thead th:nth-child(3),
#schedule tbody td:nth-child(3) {
  font-size: inherit;
  text-align: left;
  padding-left: 0.4rem;
  padding-right: 0.4rem;
}

#schedule thead th:nth-child(4),
#schedule tbody td:nth-child(4) {
  font-size: inherit;
  text-align: left;
  padding-left: 0.1rem;
  padding-right: 0.1rem;
}

#schedule thead th:nth-child(5),
#schedule tbody td:nth-child(5) {
  font-size: 0.6rem;
  text-align: center;
  padding-left: 0.15rem;
  padding-right: 0.15rem;
}

/* Same offset problem: the shared styles.css Reistijd/Aankomst tweaks below
   use nth-child 9/10 there, which lands on Pers 3 and Vertrek here instead -
   undo them back to the untouched default. */
#schedule thead th:nth-child(9),
#schedule tbody td:nth-child(9),
#schedule thead th:nth-child(10),
#schedule tbody td:nth-child(10) {
  font-size: inherit;
  text-align: left;
  padding-left: 0.4rem;
  padding-right: 0.4rem;
}

/* Reistijd/Aankomst hold short time values ("1:30", "20:00") - tighter
   horizontal padding so the column doesn't leave a visible gap before the
   next one starts. */
#schedule thead th:nth-child(11),
#schedule tbody td:nth-child(11),
#schedule thead th:nth-child(12),
#schedule tbody td:nth-child(12) {
  padding-left: 0.2rem;
  padding-right: 0.2rem;
}

/* Reistijd sits between Vertrek and Aankomst - center its value so it reads
   like it belongs in the middle of the two, instead of hugging the left. */
#schedule tbody td:nth-child(11) input {
  text-align: center;
}

/* ---- Table view header ----
   Same grid technique as public/month.css's #month-view header (see that
   rule's own comment) - duplicated here rather than shared, since it's
   scoped to this page's own #table-view. */
#table-view header {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-areas:
    ". title account"
    "filterbar filterbar filterbar";
  align-items: center;
  column-gap: 0.6rem;
  row-gap: 0.6rem;
  margin-bottom: 1rem;
  padding-bottom: 0.75rem;
}

/* Full-bleed divider line under the header - same technique as
   public/month.css's own #month-view header::after. */
#table-view header::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 100vw;
  transform: translateX(-50%);
  border-bottom: 1px solid var(--border);
}

/* Centered independently of both the (empty) first column and account-
   controls - see styles.css's identical #main-view rule for the full
   comment. The "synced: ..." status line that used to sit next to this
   h1 moved to the Instellingen page, so this is just a plain h1 now,
   matching the other pages sharing this grid pattern. */
#table-view header h1 {
  grid-area: title;
  justify-self: center;
  margin: 0;
}

/* "- beheer" part of the title - unscoped (not #table-view-only) since
   admin/month.html's h1 carries the same suffix and loads this
   stylesheet too. Same smaller/lighter treatment as this app's other
   title/subtitle pairs (e.g. .event-panel-title-date, month.css). */
.title-suffix {
  font-size: 0.75em;
  font-weight: 400;
  opacity: 0.7;
}

#table-view header .filter-bar {
  grid-area: filterbar;
  margin-bottom: 0;
}

#table-view header .account-controls {
  grid-area: account;
  justify-self: end;
}

/* margin-left:auto - the zoom buttons that used to do this job (sitting
   right before this in the filter-bar) are gone, so this is now the
   rightmost item pushing itself to the far edge on its own. */
.view-toggle {
  display: flex;
  gap: 0.3rem;
  margin-left: auto;
}

/* Marks whichever of Lijst/Maand matches the current page - a plain,
   non-link .header-btn (see index.html/month.html) rather than a
   disabled-looking link, since you're already here. */
.header-btn-active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* Icon-only Lijst/Maand toggle buttons - centers the SVG (which just
   inherits the button's own color via currentColor, so it recolors for
   free with .header-btn-active/:hover) instead of the text-oriented
   default padding/inline-block sizing. */
.header-btn-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.4rem 0.55rem;
}

/* Invisible tap-target expansion, not a bigger visible button - see
   styles.css's identical rule for the full reasoning. */
.header-btn-icon::after {
  content: '';
  position: absolute;
  top: -8px;
  bottom: -8px;
  left: -8px;
  right: -8px;
}

/* Same full-height layout as the public page: #table-view fills the
   viewport (minus body's own top/bottom padding) as a column flexbox, so
   .table-scroll can flex-grow to reach the bottom edge with the same
   margin as the sides, and gets its own scrolling box instead of the whole
   page scrolling past it (also mitigates a WebKit bug where scrolled-past
   rows leave a stale gap above the sticky header). */
#table-view {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 1.5rem);
  height: calc(100dvh - 1.5rem);
}

/* #table-view's own display:flex above (an ID selector) outranks the
   browser's default [hidden] { display: none } UA rule (an attribute
   selector), so the hidden attribute alone doesn't actually hide it - it
   was rendering right below the login form before this override. */
#table-view[hidden] {
  display: none;
}

/* border-radius here isn't just cosmetic: removing it (along with the old
   full border) exposed a WebKit bug where overflow:auto without a rounded
   clip lets a sliver of already-scrolled-past rows bleed through above the
   sticky thead (which sticks at top:-1px, see the thead th comment below).
   Keeping a radius forces Safari to composite a proper clip layer even
   though no border is visible on the sides/bottom to show it off. Only
   top/bottom live here - left/right come from each row below instead (see
   the thead/tbody rule further down), so they can skip the blank
   month-spacer-row/end-spacer-row/year-header-row gaps between months
   instead of running straight through them as one continuous line down
   the side. An earlier attempt at this caused a Safari rendering bug
   (sticky columns' content sitting a few pixels off from the rest of the
   row) - that bug is now fixed at the source via the
   tbody tr.event-row { filter: brightness(1) } rule further down, so
   per-row borders are safe again. */
#table-view .table-scroll {
  overflow: auto;
  border-top: 1px solid transparent;
  border-bottom: 1px solid var(--border);
  border-radius: 6px;
  flex: 1;
  min-height: 0;
  margin-bottom: 0;
}

#table-view thead th:first-child,
#table-view tbody tr:not(.month-spacer-row):not(.end-spacer-row):not(.year-header-row) td:first-child {
  border-left: 1px solid var(--border);
}

#table-view thead th:last-child,
#table-view tbody tr:not(.month-spacer-row):not(.end-spacer-row):not(.year-header-row) td:last-child {
  border-right: 1px solid var(--border);
}

/* Rule above each month name, closing off the blank month-spacer-row gap
   above it (this row itself deliberately has no border of its own). */
#table-view tbody tr.month-header-row td {
  border-top: 1px solid var(--border);
}

/* Compact table, same idea as public/styles.css's identical #main-view-
   scoped breakpoint - admin has no separate mobile view to fall back to,
   so this just keeps shrinking the desktop table as the window narrows
   instead of forcing horizontal scroll below 1050px. Drops the Stad
   column and relaxes the table's usual min-width:123.5ch floor. */
@media (max-width: 1060px) {
  /* Forces "nieuw" to start its own new line in the (flex-wrap)
     filter-bar, rather than wherever it happens to naturally wrap to -
     everything after it in DOM order (verwijderd, then the bulk
     verbergen/verwijderen buttons, which only render once a checkbox is
     selected) flows onto that same new line alongside it. A dedicated
     zero-height spacer with flex-basis:100% is a deterministic line-
     break, not the fragile order-based reflow that caused problems
     elsewhere in this app - nothing here depends on variable content
     width. Also applies to the 805px card view below, since this block's
     max-width covers it too.
     This used to be flex-basis:100% on #add-row-btn itself, which also
     forces its *rendered* width to 100% - flex-basis, when not auto,
     overrides width entirely for a flex item, so the button stretched
     full-width instead of staying its natural desktop size. Moving the
     100% claim to this invisible sibling instead keeps the button itself
     unconstrained - it just naturally starts the next line once the
     spacer has already claimed the rest of the current one. */
  #add-row-line-break {
    display: block;
    flex-basis: 100%;
    height: 0;
  }

  /* tr.event-row specifically, not a bare tbody td:last-child - the
     detail-row/month-header-row/year-header-row/spacer rows are each a
     single colspan cell that's simultaneously :first-child AND
     :last-child, so an unscoped rule here would hide their entire content
     instead of just the Stad column. */
  #table-view thead th:last-child,
  #table-view tbody tr.event-row td:last-child {
    display: none;
  }

  /* Hiding the th/td above isn't enough on its own - table-layout:fixed
     still reserves Stad's column track from the <col id="stad-col"> in
     the colgroup, so colspan-based rows (month headers, thead) still
     span the full 13-column width while regular rows' actual cells only
     fill 12, making the two visibly mismatched widths. Hiding the <col>
     itself drops the column from the table's layout entirely. */
  #table-view #stad-col {
    display: none;
  }

  /* Admin has two extra columns public doesn't (checkbox, expand-arrow),
     so it needs to shed more than just Stad to stay compact here - drop
     the confirmation (√) column too. Its <col> is the 5th in #schedule's
     colgroup (checkbox, arrow, datum, productie, √, ...) - same
     <col>-hiding reasoning as Stad above, needed for the same
     table-layout:fixed width-mismatch reason. */
  #table-view thead th:nth-child(5),
  #table-view tbody tr.event-row td.status-cell,
  #table-view #schedule colgroup col:nth-child(5) {
    display: none;
  }

  /* Explicit width, not auto or max-content (both tried - neither
     actually shrank the rendered table under table-layout:fixed, which
     this keeps rather than switching to auto: sticky checkbox/arrow/
     Datum columns' own "left" offsets elsewhere are hardcoded ch values
     that assume fixed's exact, predictable column widths, and auto
     layout's content-measured widths would drift from those, breaking
     the sticky alignment). With Stad (the column that normally has no
     fixed width and soaks up any leftover space - see the colgroup's
     own comment, index.html) and the confirmation column both hidden
     here, nothing was left to explain #schedule's own leftover width as
     genuine column growth, so it just rendered as dead space past
     Aankomst - invisible against the page background in plain data
     rows, but visible as an overwide colored bar in month-header-row,
     which has its own background fill. This is the exact sum of the
     remaining 11 visible columns' own ch widths (colgroup, index.html) -
     checkbox 2.5 + arrow 2.5 + datum 7 + productie 15 + aanvang 15 +
     pers1/2/3 11*3 + vertrek 8 + reistijd 9 + aankomst 9 = 101 - so the
     table's rendered width exactly matches its actual content instead
     of leaving any ambiguity for the browser to resolve. Base rule's
     margin:0 auto still centers it. */
  #table-view #schedule {
    min-width: 0;
    width: calc(101ch * var(--zoom, 1));
  }

  /* :last-child still structurally matches the now-hidden Stad cell
     (display:none doesn't remove it from the DOM), so the general and
     row-open border-right rules above are still attached to an invisible
     column - Aankomst (now visually last) needs the same border-right
     re-applied here via :nth-last-child(2). */
  #table-view thead th:nth-last-child(2),
  #table-view tbody tr:not(.month-spacer-row):not(.end-spacer-row):not(.year-header-row) td:nth-last-child(2) {
    border-right: 1px solid var(--border);
  }

  #table-view tbody tr.event-row.row-open td:nth-last-child(2) {
    border-right: 1px solid var(--fg) !important;
  }
}

/* Reserves the same 3px of space on every event row's checkbox cell,
   whether or not .row-mine's own accent border below is actually showing
   - without this, only "mine" rows had that extra 3px, so their checkbox
   sat 3px further right than every other row's (whose sticky arrow/datum
   columns sit at fixed ch-based offsets that assume a constant checkbox
   width - a per-row width difference read as a border/misalignment
   between the checkbox and arrow columns). Transparent, not the general
   rule's 1px grey - the accent color should be the only visible
   difference between rows, not a width difference. !important for the
   same reason .row-mine below needs it. */
#table-view tbody tr.event-row td:first-child {
  border-left: 3px solid transparent !important;
}

/* Subtle "this involves you" cue - a slightly thicker, accent-colored
   left border on events where the logged-in account's own name appears
   in Pers 1-3 (isMyEvent(), admin.js), independent of whatever the
   persoon filter is currently set to. Per-cell border on the first
   (sticky) column specifically, same reasoning as .row-open's own border
   below. !important: same general grey border rule this table already
   has to fight elsewhere (see .row-open's own comment below) would
   otherwise win over a plain :first-child selector. Declared *before*
   .row-open in source order, not after - when a row is both "mine" and
   open at once, both rules end up with identical specificity, so the one
   declared later wins the tie; .row-open should take precedence while
   it's the active state, with this reappearing once it's closed. */
#table-view tbody tr.event-row.row-mine td:first-child {
  border-left: 3px solid var(--accent) !important;
}

/* Marks the row whose detail panel is expanded - set via .row-open,
   toggled alongside detail.classList.toggle('open'). Per-cell borders
   (rather than outline on the <tr>) because several cells in this table
   are position:sticky, and an outline on the row itself doesn't reliably
   paint over sticky children in every browser. The highlight boxes the
   event-row and its detail-row together as one group (no border between
   them) - the event-row supplies the top/left/right edges, the detail-row
   supplies the bottom (and continues left/right down to it), so the box
   reads as wrapping around both, not just the row. !important on the
   accent declarations: this is a state override that must always win over
   the general grey border rule above regardless of how its :not()
   exclusion list grows, since that keeps shifting its specificity -
   chasing an exact match would be fragile long-term. */
/* 0.5px was tried for a thinner line but horizontal (top/bottom) hairlines
   at that width don't render reliably in Safari. Back to a full 1px,
   which is consistent across all four edges. */
#table-view tbody tr.event-row.row-open td {
  border-top: 1px solid var(--fg) !important;
  /* Suppresses the general tbody td border-bottom (styles.css) - a seam
     there would cut the box in two right where it should read as one
     continuous group with the detail-row below. */
  border-bottom: none !important;
}

/* 3px, not 1px like the row's other three edges - matches the reserved
   3px transparent border-left every checkbox cell always carries (see
   its own comment above). A 1px border here would shrink that reserved
   space, shifting the checkbox 2px left the moment a row opens. */
#table-view tbody tr.event-row.row-open td:first-child {
  border-left: 3px solid var(--fg) !important;
}

#table-view tbody tr.event-row.row-open td:last-child {
  border-right: 1px solid var(--fg) !important;
}

/* Direct-child combinator, not a plain descendant selector - the detail
   row's only direct <td> is its own outer colspan cell; a bare "td" here
   would also catch the individual field <td>s that compact-mode's
   syncExtraFieldsForRow() relocates into .detail-edit-grid (a <div>
   nested inside that outer cell), which would otherwise each pick up
   their own partial border instead of one clean box around the whole
   panel. */
#table-view tbody tr.detail-row.open > td {
  border-bottom: 1px solid var(--fg) !important;
  border-left: 1px solid var(--fg) !important;
  border-right: 1px solid var(--fg) !important;
}

.link-btn {
  background: none;
  border: none;
  color: var(--accent);
  font-size: 0.85rem;
  cursor: pointer;
  padding: 0.25rem 0;
}

/* ---- Editable cells: blend into the table until focused ---- */

#schedule input,
.detail-edit-grid input,
.detail-edit-grid textarea {
  width: 100%;
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 0.1rem 0.2rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

#schedule input::placeholder,
.detail-edit-grid input::placeholder {
  color: inherit;
  opacity: 1;
  font-style: italic;
}

/* Reistijd's placeholder is a format hint ("u:mm"), not an auto-detected
   value, so it stays dimmed grey rather than full-color like the others. */
#schedule td:nth-child(11) input::placeholder {
  opacity: 0.45;
}

/* Vertrek and Aankomst placeholders are computed times, shown upright. */
#schedule td:nth-child(10) input::placeholder,
#schedule td:nth-child(12) input::placeholder {
  font-style: normal;
}

/* Adres can be long - wrap and show all of it instead of truncating,
   no resize handle, but otherwise keeps the same always-visible border as
   the other detail-panel fields. */
.detail-edit-grid textarea[data-field="adres"] {
  overflow: auto;
  text-overflow: clip;
  white-space: pre-wrap;
  resize: none;
}

#schedule input:hover,
.detail-edit-grid input:hover,
.detail-edit-grid textarea:hover {
  border-color: var(--border);
}

#schedule input:focus,
.detail-edit-grid input:focus,
.detail-edit-grid textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg);
}

/* The confirmation column's select: same short labels (√/Optie/−) as the
   old read-only text, so the column never needs to grow - appearance:none
   drops the native dropdown arrow, which wouldn't fit this column's width
   anyway (see .status-cell's own narrow padding a few rules up). */
#schedule .status-cell select {
  width: 100%;
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 0.1rem 0;
  text-align: center;
  text-align-last: center;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

#schedule .status-cell select:hover {
  border-color: var(--border);
}

#schedule .status-cell select:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg);
}

.field-saved {
  background: color-mix(in srgb, var(--saved) 25%, transparent) !important;
  transition: background 0.15s ease;
}

.field-error {
  border-color: var(--error) !important;
}

/* ---- Expandable detail edit fields ---- */

.detail-edit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0.5rem 1.5rem;
}

.detail-edit-grid label {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: 0.7rem;
  text-transform: uppercase;
  opacity: 0.7;
}

/* border/background !important: the base "#schedule input, .detail-edit-
   grid input/textarea { border:1px solid transparent }" rule further up
   only reveals a border on :hover/:focus - fine for the dense main table,
   but in the detail panel it read as "no border at all" until you
   happened to touch a field. Always-visible border + a white background
   (distinct from the panel's own light grey) makes each field read as figs
   its own clickable box instead of blending into the surrounding text.
   readonly-value/select are handled separately below. */
.detail-edit-grid input,
.detail-edit-grid textarea {
  font-size: 0.85rem;
  text-transform: none;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border) !important;
  background: var(--bg) !important;
}

.detail-edit-grid .readonly-value {
  font-size: 0.85rem;
  text-transform: none;
  padding: 0.3rem 0.4rem;
}

.detail-edit-grid .hint {
  text-transform: none;
  font-weight: normal;
  opacity: 0.8;
}

/* An Event (appointment) row's Titel is its whole identity - there's no
   separate Productie the way a Show has, so it plays a headline role
   rather than a secondary field's. Its typed name is stored server-side
   the same way every manually-added row's name is (parsed back out of
   summary as the derived value, not written straight to the productie
   override column - see routes/admin.js's POST /events), so it renders
   through the placeholder slot, not the value slot, picking up the
   general placeholder's italic/hint styling by default - overridden
   here specifically for this one field so it reads as real content,
   not a hint. #schedule (an id) in the selector - needed to outrank the
   general "#schedule input::placeholder { font-style: italic }" rule
   further up, which would otherwise win on specificity despite
   appearing earlier. */
#schedule tr.row-appointment td input[data-field="productie"]::placeholder {
  font-style: normal;
}

/* Titel spans the width Aanvang + Pers 1/2/3 used to occupy (colspan=4,
   see appointmentTitelCellHtml()) - no Pers fields anymore, an Event
   isn't tied to specific people. Explicit font-size/padding/align here,
   not just "let it inherit": colspan doesn't consume DOM child slots,
   so removing Pers 1-3's own <td>s shifted this cell to nth-child(5) -
   the same position the √ (status) column's own tbody td:nth-child(5)
   rule (font-size:0.6rem, further up) targets for every OTHER row kind.
   Tied specificity with that rule, so source order (this rule comes
   later in the file) is what actually wins the override - matches the
   Datum cell's own font-size (nth-child(3), inherits the table's base
   calc(0.82rem*zoom) untouched) so the two read as the same size. */
#schedule tbody td.appointment-titel-cell {
  font-size: calc(0.82rem * var(--zoom, 1));
  text-align: left;
  padding-left: 0.4rem;
  padding-right: 0.4rem;
}

/* Zichtbaar-voor field spans the full grid width instead of sharing a
   column with the other single-value fields, since its account dropdown
   can grow wider than one grid cell as more accounts are added. */
.detail-edit-grid .visibility-field {
  grid-column: 1 / -1;
}

/* Hidden by default (desktop) - the real month-header-row's native
   position:sticky works fine at desktop width, no floating duplicate
   needed there. Shown only in the 805px card view (see that breakpoint). */
.sticky-month-bar {
  display: none;
}

.field-visibility-select {
  margin-top: 0.6rem;
}

/* Notitie moved out of .detail-edit-grid entirely (see admin.js) so it
   can sit full-width below Zichtbaar voor instead of sharing a ~200px
   grid column with the other single-line fields - replicates
   .detail-edit-grid label/input/textarea's own styling by hand, since
   it's no longer a descendant of that scoped selector. */
.field-note-full {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: 0.7rem;
  text-transform: uppercase;
  opacity: 0.7;
  margin-top: 0.6rem;
}

.field-note-full textarea {
  font-size: 0.85rem;
  text-transform: none;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border);
  /* No manual drag-handle - admin.js's autoGrowTextarea() grows this to
     fit its content instead, on load and on every keystroke. */
  resize: none;
  overflow: hidden;
}

.visibility-select {
  position: relative;
  margin-top: 0.3rem;
}

.visibility-select-trigger {
  width: max-content;
  max-width: 100%;
  text-align: left;
  font-size: 0.8rem;
  text-transform: none;
  font-weight: normal;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border);
  background: none;
  color: inherit;
  cursor: pointer;
}

/* position:fixed (top/left set in JS from the trigger's own
   getBoundingClientRect, see openVisibilitySelectList in admin.js) - same
   pattern as .add-row-menu. Two problems this fixes over the previous
   position:absolute version: (1) it was rendering behind the next row -
   this table has several stacking-context-creating properties (the
   sticky checkbox/arrow columns, tr.event-row's unconditional
   filter:brightness(1)) that a plain position:absolute descendant can't
   escape no matter how high its own z-index goes, but position:fixed
   escapes the whole table's stacking/overflow hierarchy the same way
   .add-row-menu already relies on. (2) min-width:100% was a floor
   relative to the *trigger*, which spans the full width of the detail
   panel (nearly the whole table on desktop) - not what "100%" sounds
   like here. Dropped in favor of plain width:max-content, sized purely
   to the longest account name. */
/* max-height was 12rem, then 22rem - still not tall enough per direct
   report, so this errs considerably wider rather than guessing a third
   fixed value: min(70vh, ...) so it still can't overflow off-screen on a
   short window (it's position:fixed, anchored near the trigger, so
   nothing scrolls IT into view if it runs past the viewport edge), but
   comfortably fits a much longer account list than before on any normal
   window height. */
.visibility-select-list {
  position: fixed;
  z-index: 20;
  width: max-content;
  max-height: min(34rem, 70vh);
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.visibility-select-option {
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.4rem 0.5rem;
  font-size: 0.8rem;
  text-transform: none;
  font-weight: normal;
  cursor: pointer;
  white-space: nowrap;
}

.visibility-select-option:hover,
.visibility-select-option.selected {
  background: var(--row-hover, rgba(0, 0, 0, 0.05));
}

/* "Iedereen" isn't a real account like the options below it - a divider
   keeps it visually distinct rather than reading as just another name. */
.visibility-select-everyone {
  border-bottom: 1px solid var(--border);
}

.visibility-select-check {
  width: 1em;
  flex: 0 0 auto;
  text-align: center;
}

/* Only claims a flex-basis (see the 1060px breakpoint below) on the
   narrower views that actually need "nieuw" pushed onto its own
   line - display:none at desktop width so it can't affect that layout
   even as an empty, zero-content flex item. */
#add-row-line-break {
  display: none;
}

.bar-btn {
  /* inline-block, not the button default - the "verwijderd" link on
     accounts.html is the first <a>-based .bar-btn (every other use is a
     <button>), and a plain inline <a> ignores height/aligns padding
     oddly. Harmless on <button>, which already boxes the same way. */
  display: inline-block;
  height: 1.7rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: var(--accent);
  cursor: pointer;
  font-size: 0.75rem;
  text-transform: lowercase;
  text-decoration: none;
}

.bar-btn:hover {
  border-color: var(--accent);
}

/* Menu anchored to "nieuw" for picking the row type up front (event/
   overnachting/blokkade) - positioned in JS next to the trigger, same
   fixed-position popover pattern as accounts.css's .color-swatch-popover. */
.add-row-menu {
  position: fixed;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.35rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}

.add-row-menu[hidden] {
  display: none;
}

.add-row-menu-item {
  padding: 0.35rem 0.75rem;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--fg);
  text-align: left;
  cursor: pointer;
  font-size: 0.8rem;
  white-space: nowrap;
}

.add-row-menu-item:hover,
.add-row-menu-item.pressed-over {
  background: var(--border);
}

.delete-row-btn {
  height: 1.7rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--error);
  border-radius: 6px;
  background: none;
  color: var(--error);
  cursor: pointer;
  font-size: 0.75rem;
  text-transform: lowercase;
}

.hide-row-btn {
  height: 1.7rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--purple);
  border-radius: 6px;
  background: none;
  color: var(--purple);
  cursor: pointer;
  font-size: 0.75rem;
  text-transform: lowercase;
}

.datum-toggle {
  cursor: pointer;
}

/* The shared styles.css nth-child(4) rule (tuned for admin's Productie
   column) also lands on the blokkade banner cell, since it occupies the
   same 4th <td> slot via colspan - re-widen its padding back out here
   (equal specificity, later in the cascade wins). */
#schedule tbody td.blokkade-cell {
  padding-left: 0.6rem;
  padding-right: 0.6rem;
  text-align: left;
  font-size: inherit;
}

/* Blokkade's Pers 1 is plain text (not an editable input like event rows'),
   but should still read the same size/alignment as theirs - explicit here
   instead of relying on this cell's <td> position happening to dodge the
   nth-child rules above (it used to collide with the √ column's nth-
   child(5) centered 0.6rem style before Productie/Status+Aanvang became
   their own cells again). */
.blokkade-pers-cell {
  font-size: inherit;
  text-align: left;
}

/* Reden now spans everything from Pers 2 onward (admin.js) - always this
   row's last child, so the shared "tbody td:last-child" rule (styles.css)
   already keeps it from truncating; explicit here too since that coupling
   is easy to lose track of if a trailing column is ever added after Stad.
   Keeps the same italic/dimmed treatment the old merged banner cell had. */
.blokkade-reden-cell {
  font-style: italic;
  opacity: 0.85;
  white-space: normal;
  overflow: visible;
  text-overflow: unset;
}

/* ---- Select / expand-arrow columns ----
   The select checkbox (1st column) already sticks via the shared
   :first-child rule. The arrow (2nd) and Datum (3rd) columns need their own
   left offsets to sit right after it and stick together as a frozen trio. */

.select-cell {
  text-align: center;
  padding: 0 !important;
}

.expand-toggle {
  text-align: center;
  padding: 0 !important;
  cursor: pointer;
}

/* Fixed width + centered text, not sized to content: the right-pointing
   (closed) and down-pointing (open) triangle glyphs don't necessarily
   share the same natural advance width, so leaving this auto-sized let
   the swap on click nudge Datum/Productie/the name chips after it by a
   pixel or two - reported as them "shifting a bit to the right" whenever
   a row opened. */
.expand-arrow {
  display: inline-block;
  width: 1.1rem;
  text-align: center;
  font-size: 1.1rem;
  line-height: 1;
  opacity: 0.85;
}

/* Small badge in the corner of Datum, flagging upcoming rows that are still
   missing Reistijd/Contact/Adres - positioned absolutely so it never fights
   the column's fixed width for space. */
.missing-warning {
  position: absolute;
  top: 1px;
  right: 2px;
  font-size: 0.65rem;
  line-height: 1;
  color: var(--error);
}

thead th:nth-child(2) {
  position: sticky;
  left: 2ch;
  z-index: 3;
}

tbody tr.event-row td:nth-child(2) {
  position: sticky;
  left: 2ch;
  z-index: 1;
}

thead th:nth-child(3) {
  position: sticky;
  left: 4.5ch;
  z-index: 3;
}

tbody tr.event-row td:nth-child(3) {
  position: sticky;
  left: 4.5ch;
  z-index: 1;
}

tr.row-hidden-event {
  opacity: 0.45;
}

/* Forces the exact repaint that fixes the vertical-shift bug on hover
   (tbody tr.event-row:hover in the shared stylesheet applies a filter,
   which happens to make Safari recompute this row's sticky columns
   correctly) - applying a no-op filter unconditionally gets the same fix
   without waiting for a hover. Admin-only: three stacked sticky columns
   (checkbox/arrow/Datum) is what triggers this; the public page, with
   just one sticky column, never showed the bug. */
#table-view tbody tr.event-row {
  filter: brightness(1);
}

/* A hotel stop has less going on than a real show (no Status, no #Shows) -
   noticeably less vertical padding keeps its row shorter/tighter. The cell
   AND the input inside it both need trimming - the td's own padding alone
   barely moved the total row height, since the input's own 0.1rem/0.1rem
   padding (see "#schedule input" further up) dominated it. Still taller
   than the checkbox cell needs (.select-cell has no padding of its own at
   all, just the checkbox's natural size), so this doesn't actually cramp
   anything even at zero. !important is just for certainty here, not a
   specificity tie - this selector already easily out-specifies the
   general rules it's overriding. Blokkade rows get the same trim (no
   inputs to also trim there - the collapsed row is plain text only). */
#table-view tbody tr.event-row.row-hotel td,
#table-view tbody tr.event-row.row-blokkade td,
#table-view tbody tr.event-row.row-blokkade-conflict td {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

#table-view tbody tr.event-row.row-hotel td input,
#table-view tbody tr.event-row.row-hotel td select {
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

/* ---- Add-row form ---- */

.add-row-form {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(140px, 220px));
  width: 100%;
  gap: 0.75rem 1.5rem;
  margin: 0 0 0.75rem;
  padding: 0.75rem 2.5rem 0.75rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.8rem;
  box-sizing: border-box;
}

/* Every field label is a direct child of the form (the grid container)
   and placed via the explicit .gc*/.gr* classes below: col1 = Soort rij/
   Datum/Adres/Pers 1, col2 = Productie/Tot/Stad/Pers 2, col3 = Status/#
   Shows/Land/Pers 3. Explicit placement (rather than relying on grid
   auto-flow) means a hidden field (Reden/Hotel naam/Persoon/Adres/Stad/
   Land/Pers 1-3) simply leaves its cell empty instead of letting a
   neighbour slide into its spot and misalign everything. Deliberately NOT
   using wrapper divs with display:contents here - that combo (dissolving
   a row-group div so its children become real grid items) doesn't
   reliably pass explicit grid-column/grid-row through in Safari, which
   collapsed several fields into column 1. */

.gc1 { grid-column: 1; }
.gc2 { grid-column: 2; }
.gc3 { grid-column: 3; }
.gr1 { grid-row: 1; }
.gr2 { grid-row: 2; }
.gr3 { grid-row: 3; }
.gr4 { grid-row: 4; }

.add-row-actions {
  grid-column: 1 / -1;
  grid-row: 5;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.add-row-close-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: var(--error);
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
}

.add-row-close-btn:hover {
  border-color: var(--error);
}

.add-row-form[hidden] {
  display: none;
}

.add-row-form label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* Without this, .add-row-form label's own display:flex above (same
   specificity family, but more specific than a bare [hidden]) would win
   over the browser's built-in [hidden] { display: none }, the same bug
   fixed for .header-btn earlier - a conditionally-hidden field like
   Reden/Hotel naam/Persoon would stay visible regardless of kind. */
.add-row-form label[hidden] {
  display: none;
}

.add-row-form input,
.add-row-form select {
  font-size: 0.85rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  /* Identical padding/border/font-size alone still isn't enough - browsers
     add their own extra intrinsic height to a native <select> (the
     dropdown-arrow control's own minimum size) that a same-padding
     <input> doesn't have, so an explicit height (with border-box, so
     padding/border don't add on top of it) is needed to make them match
     exactly rather than relying on the box model to work it out. */
  height: 2.1rem;
  box-sizing: border-box;
}

.add-row-form button[type="submit"] {
  padding: 0.4rem 0.9rem;
  border: none;
  border-radius: 6px;
  background: var(--accent);
  color: white;
  cursor: pointer;
  font-size: 0.85rem;
}


/* ---- Trash panel ---- */

/* A flex column itself (not just a static block) so its .table-scroll child
   can flex:1 and fill the remaining viewport height, matching how the main
   schedule table does it - see #table-view .table-scroll in this file. */
.trash-panel {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem;
  margin: 0 0 0.75rem;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

.trash-panel[hidden] {
  display: none;
}

.trash-header {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

.trash-header h2 {
  font-size: 0.95rem;
  margin: 0;
}

.trash-hint {
  font-size: 0.75rem;
  opacity: 0.7;
  margin: 0;
}

.trash-item-meta {
  font-size: 0.7rem;
  opacity: 0.6;
  margin: 0;
}

/* ---- Detail panel action buttons ---- */

.detail-actions {
  display: flex;
  gap: 0.6rem;
  margin-top: 0.75rem;
}

.hide-toggle-btn {
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: inherit;
  cursor: pointer;
  font-size: 0.8rem;
}

/* ---- Narrow "card" view (below public's own mobile breakpoint) ----
   Unlike the public page, admin has no separate read-only mobile markup -
   editing needs to keep working, so instead of a parallel rendering path
   this reflows the EXISTING table/row/cell DOM with CSS alone (table,tr,td
   -> block/flex), reusing every bit of existing blur-to-save/checkbox/
   detail-panel logic unchanged. Each <td> gets a small uppercase label
   (from the data-label attribute set in admin.js's editableCell()/
   statusEditableCell()/blokkade markup) since the column headers are
   hidden here. First pass - likely needs visual refinement. */
@media (max-width: 805px) {
  /* Left-align the title here too - same technique as the 480px block
     below (that one still applies underneath this one at any width below
     480px too, since both target the same rule; the narrower media query
     just wins on cascade order, not that anything conflicts). */
  #table-view header {
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "title . account"
      "filterbar filterbar filterbar";
  }

  #table-view header h1 {
    justify-self: start;
  }

  #table-view #schedule {
    display: block;
    width: 100%;
    min-width: 0;
  }

  #table-view #schedule thead {
    display: none;
  }

  #table-view #schedule tbody {
    display: block;
  }

  /* Every row except detail-row becomes a block - detail-row keeps its
     normal display:none/[.open]{display:block} toggle (a bare "tr {
     display:block }" here would override that and make every detail
     panel permanently visible regardless of the row's open state). */
  #table-view #schedule tbody tr:not(.detail-row) {
    display: block;
  }

  #table-view #schedule tbody tr.detail-row.open {
    display: block;
  }

  /* One layout, not two - the header line (checkbox/arrow/date/productie/
     Pers 1-3) is identical whether the row is open or not, matching the
     tour page's mobile cards ("the row itself stays the same, extra info
     shows in the detail panel"). Pers 1-3 are visually reordered (via the
     order property, not a DOM change) to sit right after Productie -
     they're actually earlier in the DOM than Pers 1-3. This is a fixed,
     unconditional order (not dependent on wrap timing/content width),
     unlike the flex-order approach that caused problems elsewhere in this
     app for a different reason. Status/Aanvang/Vertrek/Reistijd/Aankomst
     stay hidden (display:none, see further down) while still part of
     this <tr> - they only become visible once admin.js's
     syncExtraFieldsForRow() physically relocates them into the paired
     detail-row's .detail-edit-grid when the row opens (a plain CSS reflow
     can't move a <td> into another row's markup, but JS moving the actual
     node can). Blokkade rows have a different, shorter <td> list (6, not
     13) - these nth-of-type rules place its reden/persoon cells
     reasonably but aren't tuned specifically for it. */
  #table-view #schedule tbody tr.event-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem 0.15rem;
    padding: 0.6rem 0.6rem;
    margin: 0;
    border: 1px solid var(--border);
    /* Reserves the same 3px row-mine below switches to - every card's left
       edge is the same width whether or not it's highlighted, so mine/
       non-mine rows' content (Datum etc.) always starts at the same x
       position. Without this, only row-mine cards had a 3px left border
       against everyone else's 1px, shifting their content 2px right. */
    border-left-width: 3px;
    border-radius: 0;
  }

  /* Same reasoning as the desktop table's own row-hotel padding trim
     further up - a hotel stop has less going on, so its card can be
     noticeably shorter top-to-bottom. Only the vertical padding shrinks;
     left/right stays the card's normal 0.6rem. Blokkade cards get the
     same trim. */
  #table-view #schedule tbody tr.event-row.row-hotel,
  #table-view #schedule tbody tr.event-row.row-blokkade,
  #table-view #schedule tbody tr.event-row.row-blokkade-conflict {
    padding-top: 0.1rem;
    padding-bottom: 0.1rem;
  }

  #table-view #schedule tbody tr.event-row.row-open {
    border-bottom: none;
  }

  /* Desktop puts this accent border on the checkbox cell specifically,
     not the <tr> itself, because an outline on the row doesn't reliably
     paint over sticky columns in every browser (see that rule's own
     comment) - but this card layout has no sticky columns at all
     (position:static !important a few rules down), so that concern
     doesn't apply here, and putting it on the row/card itself instead is
     both simpler and reads better against this layout's own card border
     above. Needed as its own rule at all because the blanket "td {
     border:none !important }" a few rules down would otherwise strip the
     desktop version off entirely once cells become flex items here. Only
     the color changes here, not the width - see border-left-width above. */
  #table-view #schedule tbody tr.event-row.row-mine {
    border-left-color: var(--accent) !important;
  }

  #table-view #schedule tbody tr.event-row td {
    display: block;
    position: static !important;
    width: auto !important;
    left: auto !important;
    border: none !important;
    box-shadow: none !important;
    padding: 0;
  }

  /* The desktop-wide "#schedule input { width: 100% }" rule also matches
     the checkbox (it's an <input> too) - inside an auto-sized flex item
     that renders it as a squished sliver instead of a normal checkbox. */
  /* padding !important: not otherwise set here, so without it the general
     "row-open td input { padding:0.15rem 0.3rem }" rule further down
     would apply unopposed once the row opens, same specificity-tie
     pattern as Productie/Pers 1-3 below - shrinking this fixed 1rem box's
     usable interior and likely distorting the checkbox glyph. */
  #table-view #schedule tbody tr.event-row td.select-cell input {
    width: 1rem !important;
    height: 1rem;
    flex: 0 0 auto;
    padding: 0 !important;
  }

  #table-view #schedule tbody tr.event-row td:nth-of-type(1),
  #table-view #schedule tbody tr.event-row td:nth-of-type(2) {
    flex: 0 0 auto;
  }

  /* min-width (not a hard width) so Productie/the name chips always start
     at the same horizontal position regardless of how wide that row's
     particular date renders - matches public's .mobile-card-date. The
     inner .day-num/.day-abbr spans already get their own sizing from the
     shared (unscoped) "td.datum-cell .day-num/.day-abbr" rule earlier in
     this file - min-width:1.5em, right-aligned, and no fixed width,
     respectively - so nothing extra is needed here for those. */
  #table-view #schedule tbody tr.event-row td:nth-of-type(3) {
    flex: 0 0 auto;
    min-width: 3.1em;
    font-weight: 600;
  }

  /* flex-basis:auto (matching public's plain .mobile-card-productie
     exactly) turned out to compute circularly with the input's own
     width:100% - the td always behaved like flex-grow:1 regardless of
     the declared value, never actually shrinking. On a narrow phone
     (confirmed on iPhone 13 mini) that crowded out Pers 3, pushing it
     onto its own line. An explicit small flex-basis breaks the circular
     reference: still grows to fill space on wider screens (flex-grow:1),
     but now has a real starting point to shrink from (flex-shrink:1) on
     narrow ones instead of being held to its full content width. */
  #table-view #schedule tbody tr.event-row td:nth-of-type(4) {
    flex: 1 1 40px;
    min-width: 0;
    font-weight: 600;
    font-size: 0.9em;
  }

  /* font-size/padding !important: this input never declared its own
     font-size, relying on inheriting the 0.9em set on its <td> above -
     but the general "row-open td input { font-size:0.8rem; padding:
     0.15rem 0.3rem }" rule further down (meant for the relocated detail
     fields) targets the <input> directly, and any explicit rule beats
     inheritance regardless of specificity. That's what made Productie's
     text visibly grow/shrink only while a row was open - font-size:
     inherit !important forces it back to the <td>'s actual computed size
     instead of the general rule's flat 0.8rem. padding !important is the
     same specificity-tie fix as Pers 1-3 above (shifted the text right). */
  #table-view #schedule tbody tr.event-row td:nth-of-type(4) input {
    width: 100%;
    border: none;
    background: none;
    font-style: italic;
    font-size: inherit !important;
    padding: 0 !important;
  }

  /* Pers 1/2/3 and Status/Aanvang/Vertrek/Reistijd/Aankomst/Stad are
     targeted by data-label attribute here, not nth-of-type - nth-of-type
     is a *live* position count, and admin.js physically removes the
     Status/Aanvang/Vertrek/Reistijd/Aankomst <td>s from this row when it
     opens (see syncExtraFieldsForRow). Once those are gone, every td
     after them shifts index (Pers 1 was originally 7th, becomes 5th;
     Stad was 13th, becomes 8th), so a fixed nth-of-type(7/8/9/13) would
     silently start matching the wrong cell the moment a row opens -
     exactly what caused the chips to disappear and Stad to show in their
     place. data-label doesn't care about position or how many siblings
     were removed. */

  #table-view #schedule tbody tr.event-row td[data-label="Pers 1"] {
    order: 5;
    margin-left: auto;
  }

  #table-view #schedule tbody tr.event-row td[data-label="Pers 2"] { order: 6; }
  #table-view #schedule tbody tr.event-row td[data-label="Pers 3"] { order: 7; }

  /* width comes from --admin-name-chip-width (set by updateNameChipWidth()
     in admin.js, measured against the actual longest PERSON_NAMES entry) -
     a flat 5.5em guess measured wider than public's own dynamically-fit
     chips, which was the main reason names wrapped onto a second line here
     at the same viewport width where public's card fit on one line.
     !important because the base reset a few rules up sets width:auto
     !important on every td in this row (to strip the desktop table's
     column widths) - confirmed via the Playwright browser that without
     this, the reset was silently winning and these chips were falling
     back to their content width (~142px) instead of the intended 72px. */
  #table-view #schedule tbody tr.event-row td[data-label="Pers 1"],
  #table-view #schedule tbody tr.event-row td[data-label="Pers 2"],
  #table-view #schedule tbody tr.event-row td[data-label="Pers 3"] {
    flex: 0 0 auto;
    width: var(--admin-name-chip-width, 5.5em) !important;
    padding-left: 0.3rem !important;
    border-left: 1px solid var(--border) !important;
  }

  /* font-size/padding !important: the general "row-open td input {
     font-size:0.8rem; padding:0.15rem 0.3rem }" rule further down (meant
     for the relocated detail fields) has identical specificity and was
     winning by source order whenever the row is open - font-size made
     these chips render ~7% larger than --admin-name-chip-width was
     measured against (confirmed via earlier browser testing, why
     "Anastasiia" only truncated in the expanded state); padding inserted
     left inset that shifted the chip text right the same way it did for
     Productie above. */
  #table-view #schedule tbody tr.event-row td[data-label="Pers 1"] input,
  #table-view #schedule tbody tr.event-row td[data-label="Pers 2"] input,
  #table-view #schedule tbody tr.event-row td[data-label="Pers 3"] input {
    width: 100%;
    border: none;
    background: none;
    font-size: 0.75rem !important;
    padding: 0 !important;
  }

  #table-view #schedule tbody tr.event-row td[data-label="Status"] { order: 8; }
  #table-view #schedule tbody tr.event-row td[data-label="Aanvang"] { order: 9; }
  #table-view #schedule tbody tr.event-row td[data-label="Vertrek"] { order: 10; }
  #table-view #schedule tbody tr.event-row td[data-label="Reistijd"] { order: 11; }
  #table-view #schedule tbody tr.event-row td[data-label="Aankomst"] { order: 12; }

  /* Stad isn't part of the 2-lane detail layout the user asked for -
     hidden permanently in this view now, not just while collapsed
     (already also hidden unconditionally at the wider 1060px tier via
     td:last-child - Stad is always the row's actual last <td>, so that
     one stays position-based safely). */
  #table-view #schedule tbody tr.event-row td[data-label="Status"],
  #table-view #schedule tbody tr.event-row td[data-label="Aanvang"],
  #table-view #schedule tbody tr.event-row td[data-label="Vertrek"],
  #table-view #schedule tbody tr.event-row td[data-label="Reistijd"],
  #table-view #schedule tbody tr.event-row td[data-label="Aankomst"],
  #table-view #schedule tbody tr.event-row td[data-label="Stad"] {
    display: none;
  }

  /* Status/Aanvang/Vertrek/Reistijd/Aankomst no longer get styled here at
     all while open - admin.js physically moves those 5 <td>s into the
     paired detail-row's .detail-edit-grid when a row opens at this width
     (see syncExtraFieldsForRow in admin.js), so they render with the
     detail-grid styling below instead of picking up the row's color.
     They're only ever visible un-relocated in the collapsed state, where
     the display:none rule above already hides them. */

  #table-view #schedule tbody tr.event-row.row-open td input,
  #table-view #schedule tbody tr.event-row.row-open td select {
    width: 100%;
    font-size: 0.8rem;
    padding: 0.15rem 0.3rem;
    box-sizing: border-box;
  }

  /* Detail row/panel: already responsive via .detail-edit-grid's own
     auto-fit columns (see the shared .detail-edit-grid rule) - just stop
     it behaving like a table row/cell, the grid does the rest itself. */
  #table-view #schedule tbody tr.detail-row td {
    display: block;
    width: auto;
  }

  /* Detail panel's own 2-lane layout: lane 1 (Status/# Shows/Soort/
     Locatie/Adres) and lane 2 (Aanvang/Vertrek/Reistijd/Aankomst/Verwacht
     terug/Notitie) get fixed explicit positions instead of the desktop
     auto-fit columns, so each field lands in the exact lane/order
     requested regardless of DOM order. Status/Aanvang/Vertrek/Reistijd/
     Aankomst are relocated <td>s (see syncExtraFieldsForRow in admin.js),
     identified by their stable data-label attribute rather than a class,
     since editableCell()/statusEditableCell() already put one on every
     cell for exactly this kind of card-view targeting. Datum/Land/Contact
     aren't part of this layout - hidden here. */
  #table-view #schedule tbody tr.detail-row .detail-edit-grid {
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem 0.8rem;
  }

  #table-view #schedule tbody tr.detail-row .field-datum,
  #table-view #schedule tbody tr.detail-row .field-land,
  #table-view #schedule tbody tr.detail-row .field-contact {
    display: none;
  }

  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Status"] { grid-column: 1; grid-row: 1; }
  #table-view #schedule tbody tr.detail-row .field-aantal { grid-column: 1; grid-row: 2; }
  #table-view #schedule tbody tr.detail-row .field-soort { grid-column: 1; grid-row: 3; }
  #table-view #schedule tbody tr.detail-row .field-locatie { grid-column: 1; grid-row: 4; }
  #table-view #schedule tbody tr.detail-row .field-adres { grid-column: 1; grid-row: 5; }

  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Aanvang"] { grid-column: 2; grid-row: 1; }
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Vertrek"] { grid-column: 2; grid-row: 2; }
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Reistijd"] { grid-column: 2; grid-row: 3; }
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Aankomst"] { grid-column: 2; grid-row: 4; }
  #table-view #schedule tbody tr.detail-row .field-verwacht-terug { grid-column: 2; grid-row: 5; }
  /* Notitie no longer lives inside .detail-edit-grid at all (see
     admin.js) - it's a full-width block below Zichtbaar voor now, so it
     needs no grid position here. */

  /* Relocated <td>s don't have a real <label> wrapper like the other
     detail fields (editableCell()'s label param is only used by the old
     inline-card layout, empty here) - synthesize one from the same
     data-label attribute via ::before, styled to match .detail-edit-grid
     label's uppercase/dimmed look. */
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label] {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0;
  }

  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label]::before {
    content: attr(data-label);
    font-size: 0.7rem;
    text-transform: uppercase;
    opacity: 0.7;
  }

  /* Verwacht terug included here too, !important - it's a plain
     .detail-edit-grid label (not a relocated td[data-label]) so it was
     only picking up the general .detail-edit-grid input rule further up,
     which looks nearly identical on paper but wasn't rendering the same
     size as Aanvang/Vertrek/Reistijd/Aankomst in practice. Matching this
     rule exactly, byte for byte, removes any doubt. */
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label] input,
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label] select,
  #table-view #schedule tbody tr.detail-row .field-verwacht-terug input {
    font-size: 0.85rem !important;
    padding: 0.3rem 0.4rem !important;
    border: 1px solid var(--border) !important;
    background: var(--bg) !important;
    color: inherit;
    width: 100%;
    box-sizing: border-box;
  }

  /* select uses text-align-last (not text-align) for its own displayed
     value in most browsers - #schedule .status-cell select sets that to
     center for the desktop column; left-align it here to match every
     other field's plain left-aligned value. */
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Status"] select {
    text-align: left;
    text-align-last: left;
  }

  /* Aankomst was rendering centered here despite no rule in this file
     explicitly centering it at this position - left-aligning it directly,
     !important so it wins regardless of whatever upstream rule is
     actually responsible. */
  #table-view #schedule tbody tr.detail-row .detail-edit-grid td[data-label="Aankomst"] input {
    text-align: left !important;
  }

  /* Year headers and spacer rows are each a single cell already spanning
     the full row via colspan - just drop their table-specific
     positioning, no reflow needed since they only ever had one child.
     Their own padding (0.5rem/0/0.6rem 0.4rem 0.2rem respectively, see
     styles.css) is untouched here. */
  #table-view #schedule tbody tr.year-header-row td,
  #table-view #schedule tbody tr.month-spacer-row td,
  #table-view #schedule tbody tr.end-spacer-row td {
    display: block;
    width: auto;
    position: static;
  }

  /* Month header's own padding moves to .month-label below instead (see
     that rule's comment) - zeroed here so it isn't applied twice. */
  #table-view #schedule tbody tr.month-header-row td {
    display: block;
    width: auto;
    position: static;
    padding: 0;
  }

  /* position:sticky directly on this <td>/span never actually held once
     confirmed via real DevTools measurement: .table-scroll (the genuine
     scrolling container) does have real overflow, and the sticky CSS was
     genuinely applying - but position:sticky only "sticks" for as long as
     its own containing block (essentially this element's parent) is tall
     enough to have room to move within. A native <td> gets special
     containing-block handling from the browser for exactly this reason
     (it's what makes ordinary sticky table headers work at all without
     the row needing to be artificially tall) - but this view converts
     the <td> to display:block for the card layout, which loses that
     special handling and falls back to plain block containing-block
     rules: the <td>'s box is only as tall as the month-name text itself,
     so the label had a few px of "stick range" at most before unsticking
     again - indistinguishable from not sticking at all. Fixed via
     #sticky-month-bar below instead (a JS-driven floating duplicate,
     living outside the table structure entirely, in a genuinely tall
     containing block) - this label goes back to a plain, non-sticky
     row like it was originally, and stays visually correct as content
     scrolls normally underneath the floating bar. */
  #table-view #schedule tbody tr.month-header-row .month-label {
    display: block;
    position: static;
    font-weight: 700;
    letter-spacing: 0.04em;
    font-size: 0.78rem;
    padding: 0.35rem 0.4rem;
  }

  /* Floating duplicate of the current month's label, living outside the
     table structure entirely (direct child of #table-scroll, see
     index.html) specifically so its containing block is #table-scroll
     itself - genuinely tall (spans every row), unlike the real
     month-header-row's own <td> above. admin.js's updateStickyMonthBar()
     keeps its text in sync with whichever month is currently scrolled to
     the top; this rule only handles the sticking/visual styling. Same
     visual treatment as the real (now non-sticky) label, matching
     public's .mobile-month-header, which works the same way natively
     because it was never inside a <table> to begin with. */
  .sticky-month-bar {
    display: block;
    position: sticky;
    top: 0;
    z-index: 5;
    font-weight: 700;
    letter-spacing: 0.04em;
    font-size: 0.78rem;
    padding: 0.35rem 0.4rem;
    background: var(--header-bg);
    border-bottom: 1px solid var(--border);
  }

  .sticky-month-bar[hidden] {
    display: none;
  }

  /* The add-row form's 3 fixed columns (each minmax(140px,220px), see the
     desktop rule) need at least ~450px to not get cramped - too wide for
     this breakpoint. 2 columns instead, remapping every field's explicit
     .gcN.grM position (see the desktop rule's own comment for why explicit
     placement is used at all - a hidden field must leave a gap, not let
     its neighbour slide into its spot) rather than just redeclaring
     grid-template-columns alone, which would leave gc3 fields overlapping
     column 2's own fields instead of wrapping to a new row. Each
     .gcN.grM compound selector is more specific than the plain .gcN/.grM
     desktop rules (2 classes vs 1), so this wins regardless of source
     order. minmax(0,1fr), not a fixed minimum - a hard 140px floor per
     column could still overflow a genuinely narrow phone. */
  .add-row-form {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .add-row-form .gc1.gr1 { grid-column: 1; grid-row: 1; }
  .add-row-form .gc2.gr1 { grid-column: 2; grid-row: 1; }
  .add-row-form .gc3.gr1 { grid-column: 1; grid-row: 2; }

  .add-row-form .gc1.gr2 { grid-column: 1; grid-row: 3; }
  .add-row-form .gc2.gr2 { grid-column: 2; grid-row: 3; }
  .add-row-form .gc3.gr2 { grid-column: 1; grid-row: 4; }

  .add-row-form .gc1.gr3 { grid-column: 1; grid-row: 5; }
  .add-row-form .gc2.gr3 { grid-column: 2; grid-row: 5; }
  .add-row-form .gc3.gr3 { grid-column: 1; grid-row: 6; }

  .add-row-form .gc1.gr4 { grid-column: 1; grid-row: 7; }
  .add-row-form .gc2.gr4 { grid-column: 2; grid-row: 7; }
  .add-row-form .gc3.gr4 { grid-column: 1; grid-row: 8; }

  .add-row-form .add-row-actions {
    grid-row: 9;
  }
}

/* Beheer's own compact card rows pack in more per line (checkbox, arrow,
   Datum, Productie, three Pers chips) than the tour page's simpler cards,
   so styles.css's shared 92% at this same breakpoint still isn't tight
   enough here - this loads after styles.css and shares the same html
   selector/specificity, so it simply wins by source order. Scoped to this
   file only (not loaded by the public page), so tour view is unaffected. */
@media (max-width: 480px) {
  html {
    font-size: 78%;
  }

  /* Left-align the title at the narrowest width - see styles.css's
     identical #main-view rule (same file, same breakpoint) for the full
     reasoning; duplicated here since #table-view has its own copy of the
     base header grid. */
  #table-view header {
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "title . account"
      "filterbar filterbar filterbar";
  }

  #table-view header h1 {
    justify-self: start;
  }
}
