/* Every width breakpoint here (and in app.css) is scoped to `screen`: the
   printable roster inlines both stylesheets, and PAPER has a width too. A4
   landscape less the margins is ~1063 CSS px — a hair above the 1024px
   breakpoint — so a different paper size, margin or print scale flipped the
   printed table into the phone stacking rules, and the same roster came out
   looking different from one print dialog to the next (customer 2026-08-27).
   Print styling belongs in rosters/print.html, not in a viewport query. */
/* Edge-to-edge: every page uses the full window width (the design system caps main/header at 1100px). */
main{max-width:none}
.site-header .bar,.banners{max-width:none}
/* Review page two-pane: 30/70 source/grid, edge-to-edge when no source. */
.week-controls{margin-bottom:var(--s3,12px)}
.two-pane{display:flex;gap:var(--s4,16px);align-items:flex-start}
.grid-pane{flex:1;min-width:0}
@media screen and (min-width:1025px){
  .grid-pane .grid-scroll{max-height:calc(100vh - 230px)}
}

/* ---- mobile reflow: no horizontal scroll below 1024px ---- */
#grid-mobile-bar{display:none}
@media screen and (max-width:1024px){
  .two-pane{flex-direction:column}
  #grid-mobile-bar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:var(--s2,8px)}
  .m-views,.m-days{display:inline-flex;gap:4px;flex-wrap:wrap}
  .m-vbtn,.m-day-tab{border:1px solid var(--border,#ddd);background:var(--surface,#fff);color:var(--muted,#666);
    border-radius:var(--pill,20px);padding:5px 12px;font-weight:600;font-size:calc(12px * var(--fs, 1));line-height:1;font-family:system-ui;cursor:pointer}
  .m-vbtn.on,.m-day-tab.on{background:var(--accent,#A8842C);color:#fff;border-color:var(--accent,#A8842C)}
  .m-days{display:none}
  #grid-mobile-bar.m-day-mode .m-days{display:inline-flex}
  .grid-scroll{overflow:visible;max-height:none;border:0}
  /* turn the table into stacked blocks */
  .grid, .grid thead, .grid tbody, .grid tr, .grid td, .grid th{display:block}
  .grid thead{display:none}
  .grid tr{position:relative;margin-bottom:10px;border:1px solid var(--border,#e3dccb);border-radius:var(--radius,8px);overflow:hidden;background:var(--surface,#fff)}
  .grid td{border:0;border-bottom:1px solid var(--border,#eee);text-align:left}
  .grid td.staff{position:static;background:var(--bg,#faf7f0);font-weight:700;display:flex;justify-content:space-between;align-items:center;padding-left:30px}
  /* drag handle: overlay it in the card's top-left corner instead of its own full-width block row.
     tbody in the selector so this outranks app.css's `.grid tbody td.drag` terracotta fill —
     the overlay must stay transparent over the staff header. */
  .grid tbody td.drag{position:absolute;top:0;left:0;width:auto;height:auto;padding:6px 4px;border:0;background:transparent;z-index:2}
  .grid td.cell{display:none;flex-direction:column}   /* hidden until a view shows it (shown = flex column, so the actions toolbar can order last) */
  .grid td.cell::before{content:attr(data-daylabel);font-weight:700;font-size:calc(10px * var(--fs, 1));line-height:1;font-family:system-ui;text-transform:uppercase;color:var(--muted,#999);margin-right:8px}
  .grid td.cell .cell-drag-handle{display:none}       /* DRAG-swap stays desktop-only; touch scroll wins */
  /* clipboard + two-tap-swap toolbar: always visible on the stacked cards (no hover
     on touch) — a normal-flow right-aligned row at the card's bottom, touch-sized */
  .grid td.cell .cell-actions{position:static;display:flex;gap:6px;order:99;align-self:flex-end;
    margin-top:6px;background:transparent;border:0;box-shadow:none;padding:0}
  .grid td.cell .cell-actions button{border:1px solid #E2C4AE;background:var(--surface);padding:7px 10px}
  .grid td.cell .cell-actions button svg{width:calc(18px * var(--fs, 1));height:calc(18px * var(--fs, 1))}
  .grid td.cell .cell-actions .ca-swap{display:inline-flex}
  .grid .totals-cell{font-weight:700}
  /* day view: show only the active day's cell per row */
  .grid.m-day[data-active-day="0"] td.cell[data-day="0"],
  .grid.m-day[data-active-day="1"] td.cell[data-day="1"],
  .grid.m-day[data-active-day="2"] td.cell[data-day="2"],
  .grid.m-day[data-active-day="3"] td.cell[data-day="3"],
  .grid.m-day[data-active-day="4"] td.cell[data-day="4"],
  .grid.m-day[data-active-day="5"] td.cell[data-day="5"],
  .grid.m-day[data-active-day="6"] td.cell[data-day="6"]{display:flex}
  /* accordion view: show a row's cells only when open */
  .grid.m-acc tr.m-open td.cell{display:flex}
  .grid.m-acc td.staff{cursor:pointer}
  .grid.m-acc td.staff .m-chev{transition:transform .15s}
  .grid.m-acc tr.m-open td.staff .m-chev{transform:rotate(90deg)}
  .m-chev{display:none}
  /* big, touch-friendly accordion chevron on the stacked cards */
  .grid.m-acc .m-chev{display:inline-flex;align-items:center;justify-content:center;
    font-size:calc(24px * var(--fs, 1));min-width:44px;min-height:44px;color:var(--accent,#A8842C)}
}

/* dashboard cards: clickable + bigger status/actions */
.wk{cursor:pointer}
.wk:focus-visible{outline:2px solid var(--accent,#A8842C);outline-offset:2px}
.wk-actions{display:flex;align-items:center;gap:var(--s3,12px);margin-left:auto}
.wk-actions .badge{font-size:calc(13px * var(--fs, 1));padding:5px 14px}
.wk-actions .btn{font-size:calc(15px * var(--fs, 1));padding:9px 18px}

/* dashboard multi-select filter bar */
#wk-filters{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:var(--s3,12px)}
.ms{position:relative}
.ms-btn{border:1px solid var(--border,#ddd);background:var(--surface,#fff);color:var(--ink,#1c1b19);
  border-radius:var(--pill,20px);padding:6px 14px;font-weight:600;font-size:calc(13px * var(--fs, 1));line-height:1;font-family:system-ui;cursor:pointer}
.ms.has-sel .ms-btn{background:var(--accent,#A8842C);color:#fff;border-color:var(--accent,#A8842C)}
/* per-category clear ✕ beside the label — shown once the category has selections */
.ms-clear{display:none;border:1px solid var(--border,#ddd);background:var(--surface,#fff);color:var(--muted,#6f6a61);
  border-radius:50%;width:calc(23px * var(--fs, 1));height:calc(23px * var(--fs, 1));line-height:1;cursor:pointer;margin-left:2px;
  align-items:center;justify-content:center;font-size:calc(13px * var(--fs, 1));vertical-align:middle}
.ms.has-sel .ms-clear{display:inline-flex}
.ms-clear:hover{color:var(--error,#C0392B);border-color:var(--error,#C0392B)}
.ms-pop{display:none;position:absolute;top:100%;left:0;margin-top:4px;z-index:60;min-width:160px;max-height:240px;overflow:auto;
  background:var(--surface,#fff);border:1px solid var(--border,#ddd);border-radius:var(--radius-sm,6px);box-shadow:var(--shadow-lg,0 6px 24px rgba(0,0,0,.16));padding:6px}
.ms.open .ms-pop{display:block}
.ms-pop label{display:flex;align-items:center;gap:7px;padding:4px 6px;border-radius:4px;font-size:calc(13px * var(--fs, 1));cursor:pointer;white-space:nowrap}
.ms-pop label:hover{background:var(--bg,#f7f4ef)}
#wk-clear{padding:6px 10px}

/* master-detail (staff / portfolio) */
.md-layout{display:flex;gap:var(--s4,16px);align-items:flex-start}
.md-list{flex:0 0 calc(340px * var(--fs, 1));display:flex;flex-direction:column;gap:8px;overflow-x:hidden}  /* wide enough for logo + name on one line */
.md-list .card{overflow-wrap:anywhere}
/* the type-to-filter box rides the top of the scrolling outlet list; the spread
   shadow paints an opaque strip over the 8px flex gap so cards vanish under it */
#md-filter{position:sticky;top:0;z-index:5;flex-shrink:0;box-shadow:0 0 0 8px var(--bg,#F7F4EF)}
html:not(.hdr-sticky) .md-page #md-filter{box-shadow:0 0 0 8px var(--surface,#fff)}
.md-list .card{display:block;text-decoration:none;color:var(--ink,#1c1b19);cursor:pointer}
.md-list .card.active{outline:2px solid var(--accent,#A8842C);outline-offset:-1px}
#md-pane{flex:1;min-width:0}
.crumbs{font-size:calc(13px * var(--fs, 1));color:var(--muted,#6f6a61);margin-bottom:var(--s3,12px)}
/* breadcrumb row with a right-edge action (staff/template list panes) */
.crumb-row{display:flex;justify-content:space-between;align-items:center;gap:var(--s3,12px);flex-wrap:wrap;margin-bottom:var(--s3,12px)}
.crumb-row .crumbs{margin-bottom:0}
.crumbs a{color:var(--accent,#A8842C);text-decoration:none;padding:2px 6px;margin:-2px -6px;border-radius:4px}
/* highlight, not underline, on hover and while pressed (customer 2026-09-09) */
.crumbs a:hover,.crumbs a:active,.crumbs a:focus-visible{background:#F3EAD3;text-decoration:none}
.crumb-sep{margin:0 6px;color:var(--muted,#9a9384)}

/* Desktop app-shell: the top bar and page heading stay put while the outlet list and
   the detail pane each scroll on their own; the page itself never scrolls, so nothing
   drifts at any screen size or content length. Without hdr-sticky -> normal page flow. */
html.hdr-sticky .md-page{height:100vh;display:flex;flex-direction:column;overflow:hidden}
html.hdr-sticky .md-page .site-header,html.hdr-sticky .md-page .banners{flex:0 0 auto}
html.hdr-sticky .md-page main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;padding-top:0;overflow:hidden;margin:0}  /* margin:0 auto would center & shrink main inside the flex column */
html.hdr-sticky .md-page .md-title-row{flex:0 0 auto}
html.hdr-sticky .md-page .md-layout{flex:1 1 auto;min-height:0}
html.hdr-sticky .md-page .md-list,html.hdr-sticky .md-page #md-pane{align-self:stretch;min-height:0;max-height:100%;overflow:auto}
/* A pane hosting a grid (the template editor) must not scroll itself — the
   grid's own .grid-scroll is the single vertical scroller (keeps its sticky
   day-header row working and avoids double scrollbars when a cell editor
   opens). Other panes (staff list/forms) keep the pane scroll above. */
html.hdr-sticky .md-page #md-pane:has(.grid-scroll){display:flex;flex-direction:column;overflow:hidden}
html.hdr-sticky .md-page #md-pane:has(.grid-scroll) > *{flex:0 0 auto}
html.hdr-sticky .md-page #md-pane:has(.grid-scroll) .grid-scroll{flex:0 1 auto;min-height:0;max-height:none}
html.hdr-sticky .md-page .md-list>*{flex-shrink:0}  /* keep cards their natural size so the list overflows & scrolls (not squashed) */
html.hdr-sticky .md-page .md-list{padding-right:8px}  /* column mode: breathing room between the cards and the scrollbar (the overlay drawer has its own 8px padding) */
.md-list-toggle{display:none}  /* desktop app-shell: the outlet list is the left column, no toggle needed */

/* "Outlet list" toggle beside the page title: desktop app-shell only — on
   page-flow/mobile the dropdown above already collapses the list. Choosing an
   outlet adds .md-list-hidden (server-rendered when one is active) so the pane
   takes the full width; the button brings the column back. */
.md-title-row{display:flex;align-items:center;gap:var(--s3,12px);margin:0 0 var(--s3,12px)}
.md-title-row .md-title{margin:0}
.md-side-toggle{display:none}
html.hdr-sticky .md-page .md-side-toggle{display:inline-flex}
/* the two states look different: pressed (list shown) = accent outline + filled
   left-panel glyph; unpressed (hidden) = plain secondary + hollow glyph */
.md-side-toggle::before{content:"◧";color:var(--accent,#A8842C)}
.md-side-toggle[aria-pressed="true"]{border-color:var(--accent,#A8842C);background:var(--bg,#F4F1E9);box-shadow:inset 0 0 0 1px var(--accent,#A8842C)}
.md-side-toggle[aria-pressed="false"]::before{content:"◻";color:var(--muted,#6f6a61)}
html.hdr-sticky .md-page .md-layout.md-list-hidden .md-list{display:none}
/* Staff page (.md-overlay): the shown outlet list floats over the full-width
   content as a drawer instead of squeezing the pane. Patterns keeps the
   side-by-side column (picking an outlet still leaves a template to choose). */
html.hdr-sticky .md-page .md-layout.md-overlay{position:relative}
html.hdr-sticky .md-page .md-layout.md-overlay .md-list{position:absolute;top:0;left:0;bottom:0;z-index:30;
  width:calc(340px * var(--fs, 1));background:var(--surface,#fff);border:1px solid var(--border,#e3dccb);
  border-radius:var(--radius,8px);padding:8px;box-shadow:0 10px 30px rgba(0,0,0,.18)}

/* Without the app-shell (html lacks hdr-sticky): normal page flow, and the
   outlet list collapses into a dropdown so it doesn't consume the screen. */
html:not(.hdr-sticky) .md-page .md-layout{flex-direction:column;position:relative}
html:not(.hdr-sticky) .md-page .md-list-toggle{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;
  margin-bottom:var(--s3);padding:10px 12px;border:1px solid var(--border,#e3dccb);border-radius:var(--radius,8px);
  background:var(--surface,#fff);color:var(--ink,#1c1b19);cursor:pointer;font:inherit;font-weight:600}
.md-list-toggle-caret{color:var(--muted,#6f6a61)}
html:not(.hdr-sticky) .md-page .md-list{display:none;position:absolute;top:0;left:0;right:0;z-index:40;flex-direction:column;width:auto;
  max-height:60vh;overflow:auto;padding:8px;background:var(--surface,#fff);
  border:1px solid var(--border,#e3dccb);border-radius:var(--radius,8px);box-shadow:0 10px 30px rgba(0,0,0,.18)}
html:not(.hdr-sticky) .md-page .md-list.open{display:flex}

/* dashboard week-sort toggle */
#wk-sort{border:1px solid var(--border,#ddd);background:var(--surface,#fff);color:var(--ink,#1c1b19);
  border-radius:var(--pill,20px);padding:6px 14px;font-weight:600;font-size:calc(13px * var(--fs, 1));line-height:1;font-family:system-ui;cursor:pointer}

/* brand logo in the dark header — shown white (the source SVG is near-black + purple) */
.brand-logo{height:32px;width:auto;vertical-align:middle;filter:brightness(0) invert(1)}
/* the brand must never break internally ("ROSTER" wrapping under the logo when the bar is tight) */
.brand{white-space:nowrap;flex-shrink:0}
/* mid widths (641-1024): the bar's content doesn't fit one row — wrap the nav to a clean second row
   (app.css only wraps it <=640) */
@media screen and (max-width:1024px){
  .site-header .bar{flex-wrap:wrap;gap:var(--s2) var(--s4)}
  .site-nav{flex-wrap:wrap}
}

/* keep native form controls (e.g. the outlet <select>) from overflowing their card */
main form select, main form input, main form textarea{max-width:100%}

/* Review uploaded changes: decisions left, live source viewer right */
.changes-layout{display:flex;gap:var(--s4,16px);align-items:flex-start}
.changes-main{flex:1;min-width:0}
.changes-src{flex:0 0 38%;min-width:300px}
@media screen and (max-width:1100px){ .changes-layout{flex-direction:column} .changes-src{flex:0 0 auto;width:100%} }
.ex-cell{background:var(--bg,#F7F4EF)}
.up-cell{background:#eaf6ef}
.changes-table td{vertical-align:top}

/* Review page desktop app-shell (hdr-sticky = desktop): the page itself never
   scrolls — the staff rows scroll inside .grid-scroll under the sticky thead
   (thead/day headers are already position:sticky within it). */
html.hdr-sticky body.review-page{height:100vh;display:flex;flex-direction:column;overflow:hidden}
html.hdr-sticky .review-page .site-header,html.hdr-sticky .review-page .banners{flex:0 0 auto}
html.hdr-sticky .review-page main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden;margin:0}
html.hdr-sticky .review-page .two-pane{flex:1 1 auto;min-height:0}
html.hdr-sticky .review-page .grid-pane{display:flex;flex-direction:column;min-height:0;height:100%}  /* align-self:stretch loses to the flex line's intrinsic size here */
html.hdr-sticky .review-page .grid-scroll{flex:1 1 auto;min-height:0;max-height:none}

/* One-row action bar on desktop: no wrapping, no visible scrollbar; when the
   button set is too wide, edge chevrons (sticky flex items, shown by
   cdgBarNav in app.js) scroll it horizontally. */
html.hdr-sticky .review-page .title-row{margin-bottom:4px}
html.hdr-sticky .review-page main{padding-top:10px}
html.hdr-sticky .review-page .actionbar{position:static;flex-wrap:nowrap;flex-shrink:0;overflow-x:auto;scrollbar-width:none;padding:7px var(--s3);gap:8px;margin-bottom:4px}  /* sticky+top offset would displace it inside the no-scroll shell; flex-shrink:0 — it is an item of main's column flex, which otherwise SQUASHES it to make room for the grid, vertically cropping the button text (customer 2026-08-18) */
html.hdr-sticky .review-page .actionbar::-webkit-scrollbar{display:none}
html.hdr-sticky .review-page .actionbar .btn{font-size:min(calc(14px * var(--fs, 1)), 1.2vw);padding:7px 12px;white-space:nowrap}
html.hdr-sticky .review-page .actionbar .bar-nav{display:flex;align-items:center;justify-content:center;position:sticky;
  flex:0 0 auto;width:28px;align-self:stretch;border:1px solid var(--border,#e3dccb);border-radius:6px;
  background:var(--surface,#fff);color:var(--ink,#1c1b19);cursor:pointer;font-size:min(calc(16px * var(--fs, 1)), 1.3vw);
  z-index:3;box-shadow:0 0 6px rgba(0,0,0,.12)}
html.hdr-sticky .review-page .actionbar .bar-nav[hidden]{display:none}
/* the empty submission-panel / week-controls spacers otherwise add ~24px
   between the bar and the grid (flex items: margins don't collapse) */
html.hdr-sticky .review-page #submission-panel,
html.hdr-sticky .review-page .week-controls{margin:0}
.bar-nav{display:none}
.bar-prev{left:0}
.bar-next{right:0}
/* the empty submission-panel / week-controls spacers otherwise add ~24px
   between the bar and the grid (flex items: margins don't collapse) */
html.hdr-sticky .review-page #submission-panel,
html.hdr-sticky .review-page .week-controls{margin:0}


/* the stacked (card) layout has no columns for the per-day foot rows to sit
   under, so they step aside there (customer 2026-09-07) */
@media screen and (max-width:1024px){ .grid tfoot{display:none} }

/* ---- below laptop width (<1024): the top bar collapses behind ☰ ----
   Everything in .site-nav (links, text-size, language, user, log out) moves
   into a dropdown panel anchored to the bar; app.js::cdgNavToggle opens it. */
.nav-toggle{display:none;margin-left:auto;background:transparent;border:1px solid rgba(255,255,255,.3);
  color:#fff;border-radius:var(--radius-sm,6px);padding:4px 12px;line-height:1.2;cursor:pointer;
  font-size:calc(20px * var(--fs, 1))}
.nav-toggle:hover{background:rgba(255,255,255,.16)}
/* the version only sits in the panel at collapsed widths (see below) */
.nav-ver-panel{display:none}
@media screen and (max-width:1023px){
  .site-header .bar{position:relative;flex-wrap:nowrap;gap:var(--s3,12px)}
  .nav-toggle{display:inline-block;flex:0 0 auto}
  /* The bar holds just the brand and ☰ here, and the brand is 284px wide on
     its own: with nothing able to give, the toggle was pushed clean off the
     right edge on a phone — visible only by scrolling sideways (customer
     2026-09-03). So the LOGO gives way instead. object-fit keeps it
     undistorted as it narrows, and the toggle keeps its whole width. */
  .brand{display:flex;align-items:center;gap:8px;min-width:0;flex:0 1 auto;padding:6px 4px}
  .brand-logo{height:28px;min-width:0;flex:0 1 auto;object-fit:contain;object-position:left center}
  .brand b{flex:0 0 auto}
  /* no room for it beside them — it reappears at the foot of the panel */
  .nav-ver{display:none}
  .nav-ver-panel{display:block;padding:2px 12px;color:rgba(233,230,223,.55);
    font-size:calc(12px * var(--fs, 1))}
  .site-nav{display:none;position:absolute;top:100%;right:var(--s4,16px);z-index:40;
    flex-direction:column;align-items:stretch;justify-content:flex-start;gap:var(--s2,8px);
    min-width:min(280px,86vw);margin:0;padding:var(--s3,12px);
    background:var(--ink,#1c1b19);border:1px solid rgba(255,255,255,.18);
    border-radius:var(--radius,8px);box-shadow:var(--shadow-lg,0 6px 24px rgba(0,0,0,.16))}
  .site-header.nav-open .site-nav{display:flex}
  .site-nav a{padding:10px 12px}                    /* comfortable tap targets */
  .site-nav .switch{align-self:flex-start}
  .site-nav select{width:100%}
  .site-nav form{display:block !important}
  .site-nav form button{width:100%;justify-content:center}
}

/* ---- below laptop width: keep the vertical scrollbar visible ----
   (the page scrolls normally here — the app-shell doesn't apply — so
   the bar must be a permanent affordance, not an overlay that fades out.) */
@media screen and (max-width:1023px){
  html{overflow-y:scroll;scrollbar-gutter:stable}
  .grid-scroll,.md-pane,#md-pane{scrollbar-width:auto}
  .grid-scroll::-webkit-scrollbar,html::-webkit-scrollbar{width:12px;height:12px}
  .grid-scroll::-webkit-scrollbar-track,html::-webkit-scrollbar-track{background:var(--bg,#f7f4ef)}
  .grid-scroll::-webkit-scrollbar-thumb,html::-webkit-scrollbar-thumb{
    background:#c9c2b3;border-radius:6px;border:2px solid var(--bg,#f7f4ef)}
}

/* ---- touch tablets above the stacked layout (1025-1299, coarse pointer) ----
   The week table stays, but a finger needs room and must not trip the
   mouse-only affordances: the ⠿ cell-drag strip and the ✂/⧉/⎘ hover toolbar
   are dropped (they are hover/drag gestures — on a touchscreen they only
   fire by accident; the same tools live in the stacked portrait layout and
   on desktop), leaving the whole roomier cell as one tap target. */
@media screen and (min-width:1025px) and (max-width:1299px) and (pointer:coarse){
  .grid th,.grid td{padding:12px 10px}
  /* a finger-sized target for the staff-details 📁 (customer 2026-08-20) */
  .staff-info{font-size:calc(22px * var(--fs, 1));padding:4px 6px;opacity:.85}
  .grid td.cell{min-height:calc(64px * var(--fs, 1))}
  .grid td.cell .cell-drag-handle,.grid td.cell .cell-actions{display:none !important}
  .grid td.drag,.grid th.drag{width:34px}
  .grid td.drag .drag-handle{display:inline-block;padding:8px 2px}   /* row drag stays: a deliberate grab */
}

/* ---- the cell editor always opens as a centered popup (all widths) ----
   The editor is swapped into the cell's own <td> (that td is the HTMX target,
   so it must stay put): lift the .editor panel itself out of the table into a
   fixed, centered dialog with a full-screen backdrop. The td keeps its place
   and its id, so Save/Reset/Cancel swap back in place. (Was <1300px-only;
   made universal 2026-07-27 by request.) */
/* centred with inset+margin:auto, NOT a transform — a transform would make
   the panel the containing block for the backdrop below (and clip it). */
/* While the popup is open the page behind must not move. This is done with
   overscroll-behavior on the panel + touch-action:none on the backdrop —
   NOT html{overflow:hidden}: locking the document on iOS Safari clamps the
   scroll position and its first tap afterwards gets eaten (Cancel then
   needed two taps). */
:is(.grid td, .editor-host) .editor{
  position:fixed;inset:0;margin:auto;z-index:120;overscroll-behavior:contain;
  width:min(1660px,96vw);height:max-content;max-height:96vh;overflow:hidden;
  background:var(--surface,#fff);border:1px solid var(--border,#e3dccb);
  border-radius:var(--radius,8px);box-shadow:var(--shadow-lg,0 6px 24px rgba(0,0,0,.16));
  padding:var(--s4,16px) var(--s4,16px) 0}   /* no bottom pad: the sticky button bar IS the footer */
/* backdrop on the cell (a static td = no stacking context), so it paints
   under the panel and over the page instead of inside the panel */
:is(.grid td, .editor-host):has(.editor){position:static}
:is(.grid td, .editor-host):has(.editor)::before{
  content:"";position:fixed;inset:0;z-index:110;background:rgba(28,27,25,.45);
  touch-action:none}                    /* a drag over the dim area must not scroll the page */
:is(.grid td, .editor-host) .editor .rec{margin-bottom:var(--s3,12px)}
/* the action row stays put while the form scrolls, so Save/Cancel are
   always one tap away (never below the fold) */
:is(.grid td, .editor-host) .editor form>.cluster{position:sticky;bottom:0;z-index:2;
  margin:0;padding:10px 0;
  background:var(--surface,#fff);border-top:1px solid var(--border,#e3dccb)}
/* the ✕ rides along at the panel's top-right while the form scrolls
   (a flex column gives the sticky button its own right-aligned row) */
:is(.grid td, .editor-host) .editor{display:flex;flex-direction:column}
/* Desktop 2-col: the ✕ floats in the panel's top-right corner (absolute — a
   sticky flex row can't sit this high, top:0 clamps it to the scrollport) and
   the record column gets matching top clearance so the first card never sits
   under it. The scrolling stacked/mobile modes keep it sticky in flow. */
:is(.grid td, .editor-host) .editor .editor-x{display:inline-flex;position:absolute;top:8px;right:10px;flex:0 0 auto}
:is(.grid td, .editor-host) .editor .ed-mid{padding-top:calc(34px * var(--fs, 1))}
:is(.grid td, .editor-host) .editor.ed-stack .editor-x{position:sticky;top:0;align-self:flex-end;margin:0 0 6px 0}
:is(.grid td, .editor-host) .editor.ed-stack .ed-mid{padding-top:0}
:is(.grid td, .editor-host) .editor button{touch-action:manipulation}   /* no double-tap-zoom delay */
/* 2-column editor: the left column stacks context → hints (description) →
   per-field highlights; the record fields take the whole remaining width.
   Buttons row spans the bottom. The LEFT column never scrolls or squeezes —
   its rows floor at their content (the flexible row's min-content floor), so
   only the RECORD column scrolls at the 96vh cap; when even the left
   column's content cannot fit, editorstack.js switches to the stacked
   layout. The button bar never leaves the bottom. */
:is(.grid td, .editor-host) .editor .ed-grid{display:grid;grid-template-columns:560px 1fr;gap:0 14px;
  grid-template-areas:"ctx mid" "hints mid" "right mid" "btns btns";
  flex:1 1 auto;min-height:0;grid-template-rows:auto auto minmax(min-content,1fr) auto}
:is(.grid td, .editor-host) .editor .ed-ctx{grid-area:ctx;min-width:0}
:is(.grid td, .editor-host) .editor .ed-hints{grid-area:hints;min-width:0}
:is(.grid td, .editor-host) .editor .ed-mid{grid-area:mid;min-width:0;min-height:0;overflow-y:auto;
  contain:size}  /* the record column must not inflate the left column's flexible row (its box comes from the grid; it scrolls internally) */
:is(.grid td, .editor-host) .editor .ed-right{grid-area:right;min-width:0;margin-top:18px}
:is(.grid td, .editor-host) .editor .ed-btns{grid-area:btns}
/* THE stacked layout — one ordering everywhere it stacks: context, record
   fields, hints, colour panel, buttons; the whole popup scrolls as one
   column and the button bar stays sticky at the bottom. Applied by
   editorstack.js (.ed-stack, when the window is too short for the side
   cells to show unscrolled) AND by the ≤1024 media query below. */
:is(.grid td, .editor-host) .editor.ed-stack{overflow-y:auto;overflow-x:hidden}
/* flex/min-height reset: without it the desktop rule's flex:1 1 auto +
   min-height:0 clamp the form to the popup height and the auto rows get
   COMPRESSED — every section's content then paints over the next. Stacked
   mode must let the form grow and the popup scroll it. */
:is(.grid td, .editor-host) .editor.ed-stack .ed-grid{grid-template-columns:1fr;grid-template-rows:none;
  grid-template-areas:"ctx" "mid" "hints" "right" "btns";flex:0 0 auto;min-height:auto}
:is(.grid td, .editor-host) .editor.ed-stack .ed-hints,:is(.grid td, .editor-host) .editor.ed-stack .ed-mid,
:is(.grid td, .editor-host) .editor.ed-stack .ed-right{overflow-y:visible;min-height:auto}
:is(.grid td, .editor-host) .editor.ed-stack .ed-mid{contain:none}   /* stacked: the column grows with its content again */
@media screen and (max-width:1024px){
  :is(.grid td, .editor-host) .editor{overflow-y:auto;overflow-x:hidden}
  :is(.grid td, .editor-host) .editor .editor-x{position:sticky;top:0;align-self:flex-end;margin:0 0 6px 0}
  :is(.grid td, .editor-host) .editor .ed-mid{padding-top:0}
  :is(.grid td, .editor-host) .editor .ed-grid{grid-template-columns:1fr;grid-template-rows:none;
    grid-template-areas:"ctx" "mid" "hints" "right" "btns";flex:0 0 auto;min-height:auto}
  :is(.grid td, .editor-host) .editor .ed-hints,:is(.grid td, .editor-host) .editor .ed-mid,:is(.grid td, .editor-host) .editor .ed-right{overflow-y:visible;min-height:auto}
  :is(.grid td, .editor-host) .editor .ed-mid{contain:none}
}

/* ---- review-page flash messages: chips in the title row ----
   Confirm/reopen feedback shows beside the error/amber counts instead of the
   global top banners (suppressed via the banners block). Successes never
   coincide with the counts (confirm needs 0 errors + 0 amber); the
   cannot-confirm chip simply sits next to them. */
.review-page .title-msg{display:inline-flex;align-items:center;margin:0;
  flex:0 1 auto;min-width:0;padding:3px 10px;
  font-size:calc(13px * var(--fs, 1))}

/* ---- below desktop width: the review header fits its rows ----
   (1) the action bar stays ONE row — buttons shrink, and if they still
   overflow the bar scrolls sideways with the ‹ › chevrons cdgBarNav shows;
   (2) "Back" stays on the title row, pinned to the right. */
@media screen and (max-width:1299px){
  .review-page .actionbar{flex-wrap:nowrap;flex-shrink:0;overflow-x:auto;scrollbar-width:none;gap:8px}
  .review-page .actionbar::-webkit-scrollbar{display:none}
  .review-page .actionbar .btn{flex:0 0 auto;white-space:nowrap;
    font-size:min(calc(14px * var(--fs, 1)), 2.1vw);padding:7px 11px}
  .review-page .actionbar .bar-nav{display:flex;align-items:center;justify-content:center;position:sticky;
    flex:0 0 auto;width:26px;align-self:stretch;border:1px solid var(--border,#e3dccb);border-radius:6px;
    background:var(--surface,#fff);color:var(--ink,#1c1b19);cursor:pointer;
    font-size:calc(15px * var(--fs, 1));z-index:3;box-shadow:0 0 6px rgba(0,0,0,.12)}
  .review-page .actionbar .bar-nav[hidden]{display:none}
  .review-page .title-row{flex-wrap:nowrap;align-items:center}
  .review-page .title-row>*{flex:0 0 auto}
  .review-page .title-row .page-title{flex:0 1 auto;min-width:0}
  .review-page .title-row .spacer{flex:1 1 auto}
}

/* the week date rides smaller than the outlet/section, so the review title
   keeps to one row (em keeps it tied to the title's own font scale) */
.review-page .title-row .wk-date{font-size:.78em;font-weight:400;white-space:nowrap}

/* ---- dashboard: sticky title row, calendar left, weeks right ----
   (customer 2026-08-27) The heading and its two buttons stay put while the
   weeks scroll under them; --hdr-h is the real header's measured height, so
   the two stack instead of overlapping. */
.dash-head{position:sticky;top:var(--hdr-h, 0px);z-index:6;background:var(--bg,#F7F4EF);
  padding:0 0 var(--s2,8px);margin-bottom:var(--s2,8px)}
/* the title sits right under the bar (customer 2026-08-27): the design
   system's 24px page padding left a band of empty cream above it */
.dash-page main{padding-top:var(--s2, 8px)}
.dash-layout{display:flex;gap:var(--s4,16px);align-items:flex-start}
/* The COLUMN sticks, not the card inside it: .dash-layout aligns its items to
   flex-start, so the aside is exactly as tall as the calendar and a sticky
   card has no room to travel in (customer 2026-08-27 — it scrolled away). */
.dash-side{flex:0 0 clamp(240px, 22vw, 320px);position:sticky;align-self:flex-start;
  top:calc(var(--hdr-h, 0px) + var(--dash-head-h, 64px) + 8px)}
.dash-main{flex:1;min-width:0}
@media screen and (max-width:1024px){
  .dash-layout{flex-direction:column}
  .dash-side{flex:0 0 auto;width:100%;order:2;position:static}   /* the weeks are what a phone came for */
  .dash-main{order:1;width:100%}
}

/* ---- month calendar ---- */
.cal{padding:var(--s3,12px)}
.cal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:var(--s2,8px)}
.cal-title{font-size:calc(15px * var(--fs, 1))}
.cal-nav{display:inline-flex;align-items:center;justify-content:center;width:calc(24px * var(--fs, 1));
  height:calc(24px * var(--fs, 1));border-radius:50%;color:var(--ink,#1c1b19);text-decoration:none;
  font-size:calc(16px * var(--fs, 1));line-height:1}
.cal-nav:hover{background:var(--bg,#F7F4EF);text-decoration:none}
.cal-grid{width:100%;table-layout:fixed;border-collapse:collapse}
.cal-grid th{font-size:calc(11px * var(--fs, 1));font-weight:600;color:var(--muted,#6f6a61);
  padding:2px 0;text-align:center}
.cal-grid td{text-align:center;padding:3px 0;font-size:calc(13px * var(--fs, 1));border-radius:4px}
.cal-grid th.cal-we,.cal-grid td.cal-we{color:var(--error,#C0392B)}   /* Sat/Sun read red */
.cal-grid td.cal-out{color:#c9c2b3}                                    /* the months either side */
.cal-grid td.cal-today{outline:2px solid var(--accent,#A8842C);font-weight:700}
/* a day carrying a holiday takes that holiday's colour, PH and SH apart */
.cal-grid td.cal-ph{background:#F6DEDE;color:#8E2B2B;font-weight:700}
.cal-grid td.cal-sh{background:#DCEBF5;color:#1F5C7A;font-weight:700}
.cal-grid td.cal-out.cal-ph,.cal-grid td.cal-out.cal-sh{opacity:.45}
/* the month's holidays, named */
.cal-hols{list-style:none;margin:var(--s3,12px) 0 0;padding:0;display:flex;flex-direction:column;gap:4px}
.cal-hol{display:flex;align-items:baseline;gap:6px;font-size:calc(12px * var(--fs, 1));line-height:1.3}
.cal-hol-date{flex:0 0 auto;font-weight:700;white-space:nowrap}
.cal-hol-kind{flex:0 0 auto;font-size:calc(10px * var(--fs, 1));font-weight:700;letter-spacing:.04em;
  padding:1px 5px;border-radius:var(--pill,20px);color:#fff}
.cal-hol-name{min-width:0;overflow-wrap:anywhere}
.cal-hol-ph{color:#8E2B2B}
.cal-hol-ph .cal-hol-kind{background:#C23B3B}
.cal-hol-sh{color:#1F5C7A}
.cal-hol-sh .cal-hol-kind{background:#2C6E8F}
/* a special day wears the amber the roster grid pills it with */
.cal-grid td.cal-sp{background:#F4E8C8;color:#7A5C12;font-weight:700}
.cal-hol-sp{color:#7A5C12}
.cal-hol-sp .cal-hol-kind{background:#C09A3E}
/* every row picks its own date, like the day in the grid does */
.cal-hol>a{display:flex;align-items:baseline;gap:6px;color:inherit;text-decoration:none;
  padding:2px 4px;border-radius:var(--radius-sm,6px)}
.cal-hol>a:hover{background:var(--bg,#F7F4EF);text-decoration:none}
.cal-hol.on>a{background:var(--accent,#A8842C);color:#fff}
.cal-hol.on .cal-hol-kind{background:rgba(255,255,255,.28)}
/* ‹ 1 / 3 › under a long list */
.cal-hols-nav{display:flex;align-items:center;justify-content:center;gap:8px;margin-top:4px}
.cal-hols-nav button[disabled]{opacity:.35;cursor:default}
.cal-none{margin:var(--s3,12px) 0 0}

/* the calendar title opens a month/year picker (customer 2026-08-27) */
.cal-title{border:0;background:transparent;color:var(--ink,#1c1b19);cursor:pointer;
  font-weight:700;font-size:calc(14px * var(--fs, 1));padding:2px 6px;border-radius:var(--radius-sm,6px);
  white-space:nowrap}   /* "December 2026 ▾" must not drop its arrow to a second line */
.cal-title:hover{background:var(--bg,#F7F4EF)}
.cal-pick{margin-bottom:var(--s2,8px);padding:8px;border:1px solid var(--border,#e3dccb);
  border-radius:var(--radius-sm,6px);background:var(--bg,#F7F4EF)}
.cal-pick-year{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;
  font-size:calc(14px * var(--fs, 1))}
.cal-pick-months{display:grid;grid-template-columns:repeat(3, 1fr);gap:4px}
.cal-pick-m{display:block;text-align:center;padding:5px 0;border-radius:var(--radius-sm,6px);
  color:var(--ink,#1c1b19);text-decoration:none;font-size:calc(12px * var(--fs, 1))}
.cal-pick-m:hover{background:var(--surface,#fff);text-decoration:none}
.cal-pick-m.on{background:var(--accent,#A8842C);color:#fff;font-weight:700}
/* every day is a link: pick it to narrow the list, pick it again to let go */
.cal-grid td a{display:block;color:inherit;text-decoration:none;padding:3px 0;border-radius:4px}
.cal-grid td a:hover{background:var(--bg,#F7F4EF);text-decoration:none}
.cal-grid td{padding:0}
.cal-grid td.cal-sel{background:var(--accent,#A8842C)}
.cal-grid td.cal-sel a{color:#fff;font-weight:700}
/* the hover tint must not paint over the picked day, which then reads as
   unpicked while the pointer rests on it */
.cal-grid td.cal-sel a:hover{background:transparent}
/* the picked date, named beside the filters, with the way back out */
.wk-datepick{display:inline-flex;align-items:center;gap:6px;border-radius:var(--pill,20px);
  background:var(--accent,#A8842C);color:#fff;text-decoration:none;padding:6px 14px;
  font-weight:600;font-size:calc(13px * var(--fs, 1));line-height:1}
.wk-datepick:hover{background:#8c6d23;color:#fff;text-decoration:none}

/* ---- dashboard app-shell: the WEEK LIST scrolls, not the page ----
   (customer 2026-08-27) Same shape as the review page: on desktop
   (html.hdr-sticky) the page itself never scrolls, so the title row, the
   filters and the calendar are always there and only the records move. The
   calendar column scrolls on its own when a short window cannot hold it. */
html.hdr-sticky body.dash-page{height:100vh;display:flex;flex-direction:column;overflow:hidden}
html.hdr-sticky .dash-page .site-header,html.hdr-sticky .dash-page .banners{flex:0 0 auto}
html.hdr-sticky .dash-page main{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;
  overflow:hidden;margin:0}
html.hdr-sticky .dash-page .dash-head{position:static;flex:0 0 auto}
html.hdr-sticky .dash-page .dash-layout{flex:1 1 auto;min-height:0}
html.hdr-sticky .dash-page .dash-side{position:static;align-self:stretch;overflow-y:auto}
html.hdr-sticky .dash-page .dash-main{display:flex;flex-direction:column;min-height:0;height:100%}
html.hdr-sticky .dash-page #wk-filters{flex:0 0 auto}
html.hdr-sticky .dash-page #wk-list{flex:1 1 auto;min-height:0;overflow-y:auto;
  padding-right:4px}   /* room for the scrollbar beside the cards */

/* the shift-pattern editor's host on the Template & Pattern page (2026-09-09): an
   empty div in the list pane; filled, it shows the cell editor's popup above */
.editor-host{position:static}

/* ---- the app's own scroll containers get a bar you can grab ----
   The browser's default is 15px, which is a small target beside a full-height
   list of roster weeks or outlets (customer 2026-09-15). 18px, with the same
   thumb the narrow-width bars already use, so it reads as one system rather
   than a raw OS bar.

   Chrome/Edge only, and deliberately so: Firefox's `scrollbar-width` takes
   only auto|thin|none (no length), and setting either standard property here
   would make Chrome 121+ IGNORE these pseudo-elements entirely — which is
   also why .dash-side no longer asks for `thin`. Firefox keeps the OS width.

   Below 1023px the narrow-width block further up wins for .grid-scroll and
   the page itself, which is intended: that layout styles its own 12px bars. */
#wk-list::-webkit-scrollbar,
.dash-page .dash-side::-webkit-scrollbar,
.md-list::-webkit-scrollbar,
#md-pane::-webkit-scrollbar,
.md-pane::-webkit-scrollbar,
.grid-scroll::-webkit-scrollbar{width:18px;height:18px}
#wk-list::-webkit-scrollbar-track,
.dash-page .dash-side::-webkit-scrollbar-track,
.md-list::-webkit-scrollbar-track,
#md-pane::-webkit-scrollbar-track,
.md-pane::-webkit-scrollbar-track,
.grid-scroll::-webkit-scrollbar-track{background:var(--bg,#f7f4ef)}
#wk-list::-webkit-scrollbar-thumb,
.dash-page .dash-side::-webkit-scrollbar-thumb,
.md-list::-webkit-scrollbar-thumb,
#md-pane::-webkit-scrollbar-thumb,
.md-pane::-webkit-scrollbar-thumb,
.grid-scroll::-webkit-scrollbar-thumb{background:#c9c2b3;border-radius:9px;
  border:3px solid var(--bg,#f7f4ef)}   /* 18px track less 2x3 = a 12px thumb */
#wk-list::-webkit-scrollbar-thumb:hover,
.dash-page .dash-side::-webkit-scrollbar-thumb:hover,
.md-list::-webkit-scrollbar-thumb:hover,
#md-pane::-webkit-scrollbar-thumb:hover,
.md-pane::-webkit-scrollbar-thumb:hover,
.grid-scroll::-webkit-scrollbar-thumb:hover{background:#b3ab99}
