/* NAMU is the base/default theme -- no data-restaurant attribute needed for
   it (RESTAURANT_THEMES in app.js only special-cases restaurant_id 2/ANJU;
   Namu is whatever's left over). Black/white with a beige hairline, warmed
   (revised 2026-09-10 after an independent design critique flagged the
   first pass as flat and cold -- see CLAUDE.md's "Per-restaurant color
   theme" section for the full before/after reasoning): --bg is a warm
   paper tone rather than pure white, so panels (still pure white) actually
   separate from the page instead of sitting on identical white with only
   a hairline between them; --text/--accent are a warm brown-black ink
   rather than a cold neutral black, which also *increases* the contrast
   with ANJU's cool dark-green text/accent rather than competing with it.
   --border stays the raw beige swatch sampled from ANJU's brand image
   (#d5cab6, used at full strength here -- ANJU's own border is a lighter
   derived tint of the same swatch, #dbd2c1, not this exact value) -- the
   one color the two brands deliberately share, so the two still read as
   siblings despite otherwise-opposite palettes. The many existing
   `:hover { background: var(--bg) }` rules throughout this file (tabs,
   pills, table rows, menu buttons) all inherit this same warm paper tone
   for free -- that's deliberately how hover/selected states get a felt
   beige presence instead of relying on the border hairline alone, with no
   new token or per-rule changes needed. */
:root {
  --bg: #faf7f1;
  --panel: #ffffff;
  --border: #d5cab6;
  --text: #2a241c;
  --muted: #6e665a;
  --accent: #2a241c;
  --accent-hover: #504b45;
  /* Text color to use ON TOP of --accent (buttons, active tabs/pills) --
     needed because Namu's dark theme below inverts --accent to near-white,
     at which point the alternative (hardcoding "white" at every such call
     site, as this app did before this theme existed) would put white text
     on a near-white background. Every theme block (including ANJU's, even
     though its own accent never inverts) sets this explicitly rather than
     relying on cascade, so no theme can silently inherit another's flip. */
  --accent-ink: #ffffff;
  --danger: #dc2626;
  --hole: #fecaca;
  --low: #fef9c3;
  --high: #bbf7d0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1f1b16;
    --panel: #2a251f;
    --border: #474239;
    --text: #f5f3ee;
    --muted: #999081;
    --accent: #f5f3ee;
    --accent-hover: #d5d3ce;
    --accent-ink: #1f1b16;
    --hole: #5b2323;
    --low: #4a441c;
    --high: #1f4a30;
  }
}

/* ANJU brand theme: swaps the base palette (not --danger/--hole/--low/--high,
   which stay the universal red/yellow/green status colors regardless of
   restaurant). Applied by setting data-restaurant="anju" on <html> --
   syncRestaurantTheme() in app.js, based on state.user.restaurant_id, only
   ever while the main app screen is showing (see its comment for why the
   login/public-lookup screens never get this). Colors picked directly from
   the client's brand palette image (white/beige/green/dark-green swatches);
   --border/--muted/--accent-hover are this app's own derived shades, not
   from the brand image itself. */
:root[data-restaurant="anju"] {
  --bg: #f0ece5;
  --panel: #ffffff;
  --border: #dbd2c1;
  --text: #222a1a;
  --muted: #687667;
  --accent: #657c49;
  --accent-hover: #50623a;
  --accent-ink: #ffffff;
}
@media (prefers-color-scheme: dark) {
  :root[data-restaurant="anju"] {
    --bg: #222a1a;
    --panel: #2f3a24;
    --border: #415031;
    --text: #dbd2c1;
    --muted: #aeaf90;
    /* --accent/--accent-hover intentionally not overridden here, same as
       the base (non-ANJU) theme just above -- #657c49 already contrasts
       ~4.6:1 against white (buttons/active-tab text) and ~3.2:1 against
       this dark background (hover-state text), and brightening it to
       stand out more from the dark bg would only weaken the white-text
       contrast on buttons, which matters more since that's body-sized UI
       text rather than a hover accent. --accent-ink stays white (pinned
       explicitly, not inherited) for the same reason -- unlike Namu's dark
       theme, ANJU's accent was never inverted, so white text on it is still
       correct here. */
    --accent-ink: #ffffff;
  }
}

* { box-sizing: border-box; }

.app-version-badge {
  font-size: 0.68rem;
  color: var(--muted);
  opacity: 0.6;
  user-select: text;
}
.login-card .app-version-badge { display: block; text-align: center; margin-top: 0.25rem; }
.topbar-right .app-version-badge { margin-left: 0.25rem; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
}

h1 { font-size: 1.25rem; margin: 0; }

.brand { display: flex; align-items: center; gap: 0.5rem; }
.brand-logo { height: 28px; width: auto; display: block; }
.login-card .brand { justify-content: center; margin-bottom: 0.25rem; }
.login-card .brand-logo { height: 40px; }
h2 { font-size: 1.1rem; margin: 0 0 0.5rem; }

button {
  font: inherit;
  cursor: pointer;
}

.screen { min-height: 100vh; }
[hidden] { display: none !important; }

/* ---------- Login ---------- */
/* #lookup-screen doesn't get this same treatment despite also using
   .login-card -- a pre-existing gap, not introduced here, left alone since
   fixing it is outside this change's scope. */
#login-screen, #reset-token-screen {
  display: flex;
  align-items: center;
  justify-content: center;
}
.login-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 2rem;
  width: 320px;
  max-width: 92vw;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.login-card label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.login-card input {
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 16px; /* prevents iOS Safari auto-zoom on focus */
}
.login-card button {
  margin-top: 0.4rem;
  padding: 0.6rem;
  border: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.login-card button:hover { background: var(--accent-hover); }

.login-card .link-btn:not(.lang-toggle) {
  margin-top: 0;
  padding: 0;
  border: none;
  background: none;
  color: var(--accent);
  font-weight: 500;
  /* Underlined by default, not just on hover -- with Namu's near-black
     accent this text-only link would otherwise be indistinguishable from
     plain body text at a glance (color alone isn't a reliable "this is
     clickable" signal once the accent and the text color are this close).
     Harmless for the other themes too. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.lang-toggle,
.login-card .lang-toggle {
  background: none;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  /* Also has .link-btn, whose new default underline (see its comment)
     doesn't belong here -- this button already reads as interactive via
     its own border, same as every other bordered pill button in the app. */
  text-decoration: none;
  padding: 0.25rem 0.7rem;
  font-size: 0.8rem;
  font-weight: 500;
  margin-top: 0;
  align-self: center;
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.lang-toggle:hover,
.login-card .lang-toggle:hover { background: var(--bg); color: var(--accent); border-color: var(--accent); }

.error-text {
  color: var(--danger);
  font-size: 0.85rem;
  margin: 0;
}

/* Success-state text for the self-service reset flow -- same treatment as
   .staff-card-badge.active (the other place --high is used with text on
   top of it, not blended into it): a solid --high chip with a fixed
   readable dark-green ink, not a color-mix, which would read muddy against
   a pastel background token never designed to be mixed into text. */
.success-text {
  background: var(--high);
  color: #14532d;
  font-size: 0.9rem;
  margin: 0;
  padding: 0.6rem 0.75rem;
  border-radius: 8px;
  text-align: center;
}

.reset-token-sub {
  color: var(--muted);
  font-size: 0.9rem;
  margin: -0.5rem 0 0.25rem;
  text-align: center;
}

/* ---------- Topbar / nav ---------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1.25rem;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  gap: 0.75rem;
}
.topbar-left { display: flex; align-items: center; gap: 1.5rem; }
.topbar-right { display: flex; align-items: center; gap: 0.75rem; color: var(--muted); font-size: 0.9rem; }
.topbar-right .link-btn {
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.4rem;
}

.tabs { display: flex; gap: 0.25rem; }
.tab-btn {
  border: none;
  background: transparent;
  padding: 0.6rem 0.8rem;
  min-height: 44px;
  border-radius: 8px;
  color: var(--muted);
  font-weight: 500;
  white-space: nowrap;
}
.tab-btn.active { background: var(--accent); color: var(--accent-ink); }
.tab-btn:not(.active):hover { background: var(--bg); }

.link-btn {
  border: none;
  background: none;
  color: var(--accent);
  padding: 0;
  /* See .login-card .link-btn's comment -- same reasoning applies to every
     plain text-only button (topbar logout, "search again", etc). */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
.primary-btn {
  border: none;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 0.5rem 0.9rem;
  min-height: 44px;
  border-radius: 8px;
  font-weight: 600;
}
.primary-btn:hover { background: var(--accent-hover); }
.danger-btn {
  border: none;
  background: none;
  color: var(--danger);
  padding: 0.5rem 0;
  min-height: 44px;
  font-weight: 600;
}

/* ---------- Week nav ---------- */
.week-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 1.25rem;
  font-size: 0.9rem;
}
.week-nav button {
  border: 1px solid var(--border);
  background: var(--panel);
  border-radius: 8px;
  padding: 0.45rem 0.7rem;
  min-height: 44px;
  color: var(--text);
}
.week-nav button:hover { background: var(--bg); }
#week-label { font-weight: 600; }

/* ---------- Section filter pills ---------- */
.pill-bar {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}
.pill-btn {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: 999px;
  padding: 0.45rem 0.9rem;
  min-height: 44px;
  font-size: 0.85rem;
  font-weight: 500;
}
.pill-btn:hover { background: var(--bg); }
.pill-btn.active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
/* A user's home restaurant is always implicitly accessible (not a
   revocable grant) -- shown as a locked-on pill in the store-access picker
   rather than hidden, so the full picture ("every store this account can
   reach") is visible in one glance, not split across a pill bar plus a
   separate "home restaurant" label. Deliberately NOT dimmed (a first
   version used opacity:0.6 here, which read as "less active" -- backwards,
   since the home pill is exactly as active as any other granted one, just
   not revocable from this control) -- full color, cursor-only affordance
   change, and an inline "(home)" tag inside the pill itself carries the
   "locked" meaning instead. */
.pill-btn:disabled { cursor: default; }
.pill-home-tag { font-size: 0.75em; opacity: 0.85; }

.section-header-row td {
  background: var(--bg);
  font-weight: 700;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  position: sticky;
  left: 0;
}

.shift-section-badge {
  display: inline-block;
  margin-top: 0.15rem;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--accent);
}

/* ---------- Tab panels ---------- */
.tab-panel { display: none; padding: 0 1.25rem 2rem; }
.tab-panel.active { display: block; }

.table-scroll {
  overflow-x: auto;
  /* -webkit-overflow-scrolling: touch (removed) is a pre-iOS-13 property
     that forces this element onto its own GPU-composited scrolling layer,
     with historically quirky paint/invalidation timing on that layer.
     Modern iOS has had native momentum scrolling on a plain overflow:auto
     element since iOS 13 without it. It's the likely root cause of the
     shift-chip-painted-in-the-wrong-column bug (confirmed: a newly
     created/copied chip renders in the wrong column on its first paint and
     only snaps to the right one after a *later, separate* browser paint
     cycle -- e.g. triggered by deleting an unrelated shift -- which several
     attempts to force an equivalent extra repaint synchronously in the same
     render pass all failed to reproduce). Removing the property removes the
     dedicated compositing layer, rather than continuing to patch around its
     quirks after the fact.
  */
  border: 1px solid var(--border);
  border-radius: 10px;
  /* Scroll shadows: each pair of gradients only shows on the side that still
     has more content — the "local" layer scrolls with the content and cancels
     the "scroll" layer's shadow once you reach that edge. No JS needed. */
  background-image:
    linear-gradient(to right, var(--panel) 30%, rgba(255, 255, 255, 0)),
    linear-gradient(to left, var(--panel) 30%, rgba(255, 255, 255, 0)),
    linear-gradient(to right, rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0)),
    linear-gradient(to left, rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0));
  background-position: left center, right center, left center, right center;
  background-repeat: no-repeat;
  background-color: var(--panel);
  background-size: 32px 100%, 32px 100%, 14px 100%, 14px 100%;
  background-attachment: local, local, scroll, scroll;
}
.swipe-hint {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--muted);
  font-size: 0.78rem;
  margin: -0.4rem 0 0.5rem;
}

.hours-days-off-summary {
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 600;
  margin: 0 0 0.6rem;
}

table.grid-table {
  border-collapse: collapse;
  width: 100%;
  min-width: 720px;
  font-size: 0.88rem;
}
table.grid-table th, table.grid-table td {
  border: 1px solid var(--border);
  padding: 0.5rem 0.6rem;
  text-align: left;
  vertical-align: top;
}
/* Sticky-layer stack, low to high: body cells (auto) < sticky header row (2)
   < sticky employee column (1, but see below) < the corner cell that's
   sticky in BOTH directions (3) — it has to sit above the other two sticky
   layers or it gets overlapped by whichever one scrolls past it. */
table.grid-table thead th {
  background: var(--bg);
  position: sticky;
  top: 0;
  font-weight: 600;
  z-index: 2;
}
/* Class-only (no th/td element requirement) so this also covers
   #schedule-table's div-based cells (see .schedule-grid below) alongside
   the other tables' real <td>/<th> ones. */
.grid-table .staff-col {
  position: sticky;
  left: 0;
  background: var(--panel);
  min-width: 150px;
  z-index: 1;
}
table.grid-table thead th.staff-col {
  z-index: 3;
  background: var(--bg);
}

/* #schedule-table is a CSS Grid (div-based), not an HTML <table> — WebKit's
   table-layout:fixed was found to deterministically paint a shift under the
   wrong day column on real iOS Safari when the shift was created via copy
   next to an existing one (reproduced even after a full page reload, so not
   a caching/paint-timing issue; three different "force a repaint" fixes and
   a full table-node rebuild all failed to resolve it; never reproduced in
   Chromium). CSS Grid's column layout is a completely different algorithm,
   sidestepping that engine-specific bug rather than continuing to patch
   around it. grid-template-columns is the single source of truth for every
   column's width — no more per-cell width rules needed at all.
   width: max-content (not the inherited 100%) lets the grid grow past its
   container to the sum of its column widths, which is what drives the
   horizontal scroll/sticky-column design (same intent the old table's
   width:auto had). */
.schedule-grid {
  display: grid;
  grid-template-columns: 150px repeat(7, 110px);
  width: max-content;
  border-collapse: collapse;
  font-size: 0.88rem;
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
}
.schedule-grid .grid-cell {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 0.5rem 0.6rem;
  text-align: left;
  min-width: 0;
}
.schedule-grid .grid-head-cell {
  background: var(--bg);
  position: sticky;
  top: 0;
  font-weight: 600;
  z-index: 2;
}
.schedule-grid .grid-head-cell.staff-col {
  z-index: 3;
  background: var(--bg);
}
.schedule-grid .section-header-row {
  grid-column: 1 / -1;
}

#coverage-table td.staff-col, #coverage-table th.staff-col {
  min-width: 60px;
  width: 60px;
  white-space: nowrap;
}

.day-header-date { color: var(--muted); font-weight: 400; font-size: 0.78rem; }

.field-hint { color: var(--muted); font-size: 0.8rem; margin: -0.5rem 0 0.5rem; }

.staff-name-cell { display: flex; align-items: center; gap: 0.5rem; }
.color-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }

/* Up/down reorder buttons on the Schedule grid (2026-09-21) -- kept tiny
   and flex-shrink:0 since they share the already-narrow staff-col with
   the color dot, name, position and start date; see the mobile
   breakpoint below for the even-narrower phone treatment.

   Always stacked vertically, independent of neighboring content
   (2026-09-21 polish): flex-direction:column on the wrapper is the real
   layout rule, but each button is ALSO forced display:block with a fixed
   width/height and flex:none -- belt-and-suspenders so the pair can never
   end up side-by-side no matter what a stale cached stylesheet or an
   unexpected layout context does to the wrapper. width is set on the
   wrapper too so it can't stretch to match a taller sibling (the 3-line
   name+position+start-date block) and pull the buttons apart. */
.staff-reorder-buttons { display: flex; flex-direction: column; gap: 1px; flex-shrink: 0; width: 16px; }
.staff-reorder-btn {
  display: block;
  flex: none;
  width: 16px;
  height: 14px;
  padding: 0;
  line-height: 1;
  font-size: 8px;
  /* Accent-colored (2026-09-21), not a flat neutral gray, so these pick up
     each restaurant's own theme the same way every other small control
     (pill-btn, primary-btn) already does -- NAMU's near-black or ANJU's
     green, automatically, via the same --accent/--accent-ink tokens. */
  border: 1px solid var(--accent);
  border-radius: 2px;
  background: var(--panel);
  color: var(--accent);
  cursor: pointer;
}
.staff-reorder-btn:hover:not(:disabled) { background: var(--accent); color: var(--accent-ink); }
.staff-reorder-btn:disabled { opacity: 0.4; cursor: default; border-color: var(--border); color: var(--muted); }
/* min-width:0 lets this flex item shrink below its content's intrinsic
   width — without it, a long unbroken line (e.g. the start-date text)
   pushes the box past the column instead of the ellipsis rule below ever
   engaging. */
.staff-name-info { min-width: 0; }

/* Reset native <button> chrome so a staff member's name (Schedule grid,
   Staff tab table, Staff tab cards) reads as plain clickable text, not a
   button — clicking it opens the monthly summary modal. */
.staff-name-link {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: inherit;
  cursor: pointer;
  min-height: 0;
}
.staff-name-link:hover, .staff-name-link:focus-visible { color: var(--accent); }

/* schedule cell */
.shift-cell { min-width: 110px; cursor: pointer; }
.shift-cell:hover { background: var(--bg); }
.shift-cell.empty { color: var(--muted); }
.shift-chip {
  border-radius: 6px;
  padding: 0.25rem 0.4rem;
  font-size: 0.82rem;
  line-height: 1.25;
  /* Background is the staff's own color at ~20% opacity (see
     renderShiftChip in app.js: `${staffMember.color}33`), blended over
     whichever theme's panel color sits behind it -- a light pastel tint in
     light mode, but a dark, muted tint in dark mode. Hardcoding near-black
     text here made it low-contrast against that dark tint; var(--text)
     tracks the active theme instead (identical to the old hardcoded value
     in light mode, near-white in dark mode). */
  color: var(--text);
}
.shift-chip .shift-time { font-weight: 600; }
.shift-time-compact { display: none; }
.shift-chip .shift-pos { font-size: 0.75rem; opacity: 0.8; }
.shift-chip-off {
  background: var(--bg) !important;
  color: var(--muted);
  font-style: italic;
  border: 1px dashed var(--border);
}
.shift-chip-on .shift-time { color: var(--accent); }

/* ---------- Day nav + day list (used by Coverage's mobile day view, and
   .day-list-empty is reused as a generic empty-state message elsewhere) ---------- */
.day-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
.day-nav button {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: 8px;
  min-height: 44px;
  min-width: 44px;
  padding: 0.45rem 0.9rem;
  font-size: 1rem;
}
.day-nav button:hover { background: var(--bg); }
.day-nav-label { font-weight: 600; text-align: center; flex: 1; }
.day-nav-label .day-nav-date { display: block; color: var(--muted); font-weight: 400; font-size: 0.82rem; }

.day-list { display: flex; flex-direction: column; gap: 0.6rem; }
.day-list-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 0.85rem;
  min-height: 44px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  cursor: pointer;
}
.day-list-row:hover { background: var(--bg); }
.day-list-row .staff-name-cell { flex: 0 0 auto; min-width: 40%; }
.day-list-row .day-list-shift { flex: 1; text-align: right; color: var(--text); }
.day-list-row .day-list-shift.empty { color: var(--muted); font-style: italic; }
.day-list-empty { text-align: center; color: var(--muted); font-style: italic; padding: 1.5rem 1rem; }

/* ---------- Generic mobile/desktop view toggle (Staff/Users cards, Coverage day view) ----------
   !important because these are state utility classes meant to win regardless of a
   component's own `display` rule (e.g. .staff-card-list sets display:flex for when
   it's actually shown) and regardless of stylesheet source order. */
.mobile-only-view { display: none !important; }
.desktop-tablet-view { display: block !important; }
@media (max-width: 767px) {
  .mobile-only-view { display: block !important; }
  .desktop-tablet-view { display: none !important; }
}

/* ---------- Staff cards (mobile) ---------- */
.staff-card-list { display: flex; flex-direction: column; gap: 0.6rem; }
.staff-card {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  padding: 0.8rem 3.2rem 0.8rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}
.staff-card .color-dot { margin-top: 0.3rem; }
.staff-card-body { flex: 1; min-width: 0; }
.staff-card-name { font-weight: 600; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.staff-card-meta { color: var(--muted); font-size: 0.82rem; margin-top: 0.15rem; }
.user-card-role-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.5rem;
  font-size: 0.82rem;
  color: var(--muted);
}
.user-card-role-row .user-role-select {
  min-height: 40px;
  padding: 0.3rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 0.85rem;
}
.staff-card .user-sections-pills { margin-top: 0.5rem; }
.staff-card-badge {
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: var(--bg);
  color: var(--muted);
}
.staff-card-badge.active { background: var(--high); color: #14532d; }

.staff-card-menu-btn {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  width: 44px;
  height: 44px;
  border: none;
  background: none;
  color: var(--muted);
  font-size: 1.3rem;
  border-radius: 8px;
  line-height: 1;
}
.staff-card-menu-btn:hover, .staff-card-menu-btn[aria-expanded="true"] { background: var(--bg); color: var(--text); }

.staff-card-menu {
  position: absolute;
  top: 2.9rem;
  right: 0.4rem;
  z-index: 5;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  display: flex;
  flex-direction: column;
  min-width: 130px;
  overflow: hidden;
}
.staff-card-menu[hidden] { display: none; }
.staff-card-menu button {
  border: none;
  background: none;
  color: var(--text);
  text-align: left;
  padding: 0.7rem 0.9rem;
  min-height: 44px;
  font-size: 0.9rem;
}
.staff-card-menu button:hover { background: var(--bg); }
.staff-card-menu button[data-action="delete"] { color: var(--danger); }

/* ---------- Coverage heatmap ---------- */
.coverage-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.coverage-controls select {
  padding: 0.35rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  min-height: 44px;
  font-size: 16px;
}
.legend { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: var(--muted); flex-wrap: wrap; }
.legend-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; margin-left: 0.6rem; }
.legend-swatch:first-child { margin-left: 0; }
.legend-swatch.hole { background: var(--hole); }
.legend-swatch.low { background: var(--low); }
.legend-swatch.high { background: var(--high); }

table.heatmap td { text-align: center; font-weight: 600; min-width: 44px; }
table.heatmap td.count-0 { background: var(--hole); }

/* ---------- Empty states ---------- */
td.empty-state {
  text-align: center;
  color: var(--muted);
  font-style: italic;
  padding: 1.5rem 1rem;
  position: static;
}

/* ---------- Modal ---------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}
.modal-backdrop[hidden] { display: none; }
.modal-card {
  background: var(--panel);
  border-radius: 12px;
  padding: 1.5rem;
  width: 340px;
  max-width: 92vw;
  max-height: 90vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.modal-card label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.modal-card input[type="text"], .modal-card input[type="time"],
.modal-card input[type="number"], .modal-card input[type="tel"],
.modal-card input[type="password"], .modal-card select,
.modal-card textarea {
  padding: 0.45rem 0.55rem;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 16px; /* prevents iOS Safari auto-zoom on focus */
  min-height: 44px;
}
.modal-card textarea {
  width: 100%;
  font-family: inherit;
  resize: vertical;
}

/* ---------- Copy-to-date month calendar ---------- */
.calendar-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0.5rem 0;
}
.calendar-nav button {
  min-width: 44px;
  min-height: 44px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 1rem;
}
.calendar-nav span { font-weight: 600; }
.calendar-weekdays, .calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}
.calendar-weekdays {
  text-align: center;
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 0.25rem;
}
.calendar-grid { gap: 3px; }
.calendar-day {
  position: relative;
  aspect-ratio: 1;
  min-height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.85rem;
}
.calendar-day.outside-month { visibility: hidden; pointer-events: none; }
.calendar-day.active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.calendar-day .has-shift-dot {
  position: absolute;
  bottom: 4px;
  right: 4px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--danger);
}
.calendar-day.active .has-shift-dot { background: var(--accent-ink); }

/* ---------- Loading indicators ---------- */
@keyframes spin { to { transform: rotate(360deg); } }

/* Thin indeterminate bar at the very top of the viewport, shown while any
   api() call is in flight (see showGlobalLoading/hideGlobalLoading). A
   sliding-gradient animation rather than a real progress percentage, since
   there's no way to know how long a request will take. */
.global-loading-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 500;
  overflow: hidden;
  background: transparent;
}
.global-loading-bar::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40%;
  background: var(--accent);
  animation: global-loading-slide 1.1s ease-in-out infinite;
}
@keyframes global-loading-slide {
  0% { left: -40%; }
  100% { left: 100%; }
}

/* Applied to a submit button while its own async action (save/copy) is in
   flight — hides the label and shows a centered spinner instead, so the
   button can't be tapped again mid-request. Spinner color assumes a
   .primary-btn's accent background, so it's --accent-ink (not a literal
   white) -- NAMU's dark theme inverts --accent to near-white, and a literal
   white spinner would vanish on it exactly like the literal-white button
   text this same theme change already fixed elsewhere. */
.btn-loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
.btn-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid color-mix(in srgb, var(--accent-ink) 40%, transparent);
  border-top-color: var(--accent-ink);
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}
.modal-card input[type="color"] {
  min-height: 44px;
  min-width: 44px;
  padding: 2px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--bg);
  cursor: pointer;
}
.modal-sub { color: var(--muted); font-size: 0.85rem; margin: -0.5rem 0 0; }

/* ---------- Add existing staff modal ---------- */
.add-existing-staff-results { display: flex; flex-direction: column; gap: 0.5rem; max-height: 320px; overflow-y: auto; }
.add-existing-staff-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.add-existing-staff-row .modal-sub { margin: 0.15rem 0 0; }
.add-existing-staff-row-actions { display: flex; gap: 0.4rem; flex-shrink: 0; }

/* ---------- Staff monthly summary modal ---------- */
.staff-summary-header { display: flex; align-items: center; gap: 0.6rem; }
.staff-summary-header .color-dot { width: 12px; height: 12px; }
.staff-summary-name { font-weight: 600; font-size: 1.05rem; }
.staff-summary-facts { margin: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.staff-summary-facts > div { display: flex; justify-content: space-between; gap: 1rem; }
.staff-summary-facts dt { color: var(--muted); font-size: 0.85rem; }
.staff-summary-facts dd { margin: 0; font-size: 0.85rem; }
#staff-summary-month-nav { justify-content: center; padding: 0; }
#staff-summary-month-label { text-align: center; font-weight: 500; }
.staff-summary-stats { display: flex; gap: 0.75rem; }
.staff-summary-stat {
  flex: 1;
  text-align: center;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.75rem 0.5rem;
}
.staff-summary-stat-value { font-size: 1.5rem; font-weight: 700; }
.staff-summary-stat-label { color: var(--muted); font-size: 0.78rem; margin-top: 0.2rem; }
/* Read-only, so cells aren't buttons and don't need the 44px tap-target
   sizing the (clickable) Copy-to-date calendar's .calendar-day cells use. */
#staff-summary-calendar .calendar-day { min-height: 32px; cursor: default; }
#staff-summary-calendar .calendar-day.rest-day { background: var(--low); border-color: var(--low); color: var(--text); }
.staff-summary-legend { display: flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; color: var(--muted); }
.staff-summary-legend-swatch { width: 12px; height: 12px; border-radius: 3px; background: var(--low); }
.legacy-override-notice {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
  margin: 0;
}
.legacy-override-notice #shift-legacy-override-text { flex: 1; min-width: 0; }
.legacy-override-notice .link-btn { flex-shrink: 0; min-height: 32px; }
.row-2 { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.row-2 label { flex: 1; min-width: 120px; }
.checkbox-row {
  flex-direction: row !important;
  align-items: center;
  gap: 0.6rem !important;
  min-height: 44px;
  cursor: pointer;
}
.checkbox-row input[type="checkbox"] { width: 20px; height: 20px; flex-shrink: 0; cursor: pointer; }
.modal-actions { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.4rem; }
.modal-actions button { min-height: 44px; padding-left: 0.9rem; padding-right: 0.9rem; }
.spacer { flex: 1; }

/* ---------- Shift action sheet (Edit / Copy to next day / Copy to…) ---------- */
.action-sheet-card {
  gap: 0.5rem;
}
.action-sheet-title { font-size: 0.95rem; text-align: center; color: var(--muted); }
.action-sheet-actions { display: flex; flex-direction: column; gap: 0.4rem; }
.action-sheet-actions button {
  width: 100%;
  text-align: left;
  padding: 0.8rem 0.9rem;
  min-height: 44px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
  color: var(--text);
  font-size: 0.95rem;
}
.action-sheet-actions button:hover { background: var(--bg); }
.action-sheet-cancel {
  text-align: center;
  justify-content: center;
  min-height: 44px;
  margin-top: 0.2rem;
}
@media (max-width: 640px) {
  .action-sheet-backdrop { align-items: flex-end; }
  .action-sheet-backdrop .modal-card {
    width: 100%;
    max-width: 100%;
    border-radius: 16px 16px 0 0;
    padding-bottom: 1.4rem;
  }
}

/* ---------- Toast (shift-copy confirmation + Undo) ---------- */
.toast {
  /* Deliberately a fixed dark pill (not var(--panel)/var(--text)) so it reads
     consistently in both themes instead of inverting in dark mode. */
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: #262626;
  color: #f5f5f5;
  padding: 0.75rem 0.9rem;
  border-radius: 10px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  max-width: 92vw;
  font-size: 0.88rem;
}
.toast[hidden] { display: none; }
.toast-action {
  border: none;
  background: none;
  color: #7cb0ff;
  font-weight: 700;
  min-height: 44px;
  padding: 0 0.4rem;
  flex-shrink: 0;
}
.toast-dismiss {
  border: none;
  background: none;
  color: #f5f5f5;
  opacity: 0.7;
  font-size: 1.1rem;
  min-width: 44px;
  min-height: 44px;
  flex-shrink: 0;
}
.toast-dismiss:hover { opacity: 1; }

/* ---------- Shift chip overflow ("⋮") — desktop grid + mobile day list ---------- */
/* A cell with shifts stacks one independently-interactive chip per shift,
   plus a small dedicated "add another" button — never a single cell-level
   click target defaulting to shifts[0]. */
td.shift-cell.has-shifts {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 3px;
  cursor: default;
}
.shift-chip-wrap { position: relative; }
.shift-chip-wrap.has-shift-actions { cursor: pointer; }
.chip-menu-btn {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 22px;
  height: 22px;
  min-height: 0;
  padding: 0;
  border: none;
  background: rgba(0, 0, 0, 0.12);
  /* Same dark-mode contrast issue as .shift-chip: near-black text over a
     background that's already dark in dark mode was nearly invisible. */
  color: var(--text);
  border-radius: 4px;
  font-size: 0.9rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.chip-menu-btn:hover, .chip-menu-btn:focus-visible { background: rgba(0, 0, 0, 0.24); }
/* Invisible expanded hit area — the visible button stays small so it doesn't
   dominate a compact chip, but ::before (still part of the button for click
   purposes, since pseudo-elements aren't separate DOM nodes) extends the
   actual tappable region out to a full 44x44. */
.chip-menu-btn::before {
  content: "";
  position: absolute;
  inset: -11px;
}
.cell-add-shift-btn {
  width: 100%;
  min-height: 22px;
  border: 1px dashed var(--border);
  background: none;
  color: var(--muted);
  border-radius: 4px;
  font-size: 0.8rem;
  line-height: 1.4;
}
.cell-add-shift-btn:hover { background: var(--bg); color: var(--text); }

.staff-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.staff-toolbar label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.staff-toolbar select {
  padding: 0.35rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  min-height: 44px;
  font-size: 16px;
}

/* staff / users table row actions */
#staff-table td button, #users-table td button {
  margin-right: 0.4rem;
  margin-bottom: 0.3rem;
  padding: 0.4rem 0.7rem;
  min-height: 44px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: 6px;
}
/* The name cell's button is a plain-text link, not a row action — undo the
   row-action button chrome above (higher specificity than .staff-name-link
   alone, since #staff-table td button would otherwise win). */
#staff-table td button.staff-name-link {
  margin: 0;
  padding: 0;
  min-height: 0;
  border: none;
  background: none;
  border-radius: 0;
}
#users-table td button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* #users-table td button (above) is an ID selector, so it silently beats
   .pill-btn.active's plain class rule regardless of source order -- the
   pills would render with their action-button background (var(--panel),
   effectively white) no matter what .active said, which is very likely
   the root cause of a real bug report: a store-access pill inside this
   table looked unstyled/inactive even when it correctly WAS active,
   reading as "nothing happens" on click. These two rules restore the
   pill's own active/disabled treatment with equal (ID-scoped) specificity
   so they actually win inside this table. Table pills only ever get
   data-section/data-restaurant-id (never a plain action button), so this
   can't accidentally re-style Reset password/Delete/etc. */
#users-table td button.pill-btn.active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
#users-table td button.pill-btn:disabled { opacity: 1; cursor: default; }
#users-table .user-role-select {
  padding: 0.4rem 0.5rem;
  min-height: 44px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 0.9rem;
}

/* ---------- Mobile ---------- */
@media (max-width: 640px) {
  .topbar {
    padding: 0.6rem 0.75rem;
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
  }
  .topbar-left {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
    width: 100%;
  }
  .topbar-right {
    width: 100%;
    justify-content: space-between;
    order: 3;
  }
  .brand { justify-content: center; }
  .tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    width: 100%;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab-btn { padding: 0.6rem 0.9rem; }

  .week-nav {
    flex-wrap: wrap;
    justify-content: center;
    padding: 0.6rem 0.75rem;
    gap: 0.5rem;
  }
  .week-nav button { flex: 0 0 auto; }
  #week-label { flex: 1 1 100%; text-align: center; order: -1; }

  .tab-panel { padding: 0 0.6rem 2rem; }

  .coverage-controls { justify-content: flex-start; }
  .coverage-controls label { flex: 1 1 auto; }

  table.grid-table { font-size: 0.82rem; }
  table.grid-table th, table.grid-table td { padding: 0.4rem 0.5rem; }

  .modal-card { padding: 1.1rem; width: 92vw; }
  .row-2 { flex-direction: column; }
  .row-2 label { min-width: 0; }

  #shift-type-pills .pill-btn { flex: 1 1 auto; text-align: center; }

  /* ---------- Compact weekly schedule grid (320-430px) ----------
     The grid stays the only schedule view at every width — this just makes
     it dense: a narrower sticky employee column with wrappable names, a
     single-line "Mon 17" day heading, and narrower shift cells with compact
     chip text. Horizontal scrolling for the rest of the week is expected
     (see .table-scroll's scroll-shadow/.swipe-hint) — this only reduces how
     much of it is needed. Empty cells and each shift's tap target stay full
     padding-driven size even though the visual cell is small. */
  /* grid-template-columns (not per-cell width) is the single source of
     truth for column sizing — see .schedule-grid above. Widened from the
     original 68px to 84px (2026-09-21) specifically to make room for
     touch-sized reorder buttons -- see .staff-reorder-btn override below. */
  .schedule-grid { grid-template-columns: 84px repeat(7, 58px); }
  .schedule-grid .staff-col { padding: 0.35rem 0.4rem; }
  #schedule-table .staff-name-cell { gap: 0.25rem; align-items: flex-start; }
  #schedule-table .staff-name-cell .color-dot { margin-top: 0.2rem; }
  /* Sized for a thumb, not the 16x14px desktop version, which would be a
     genuine miss-tap trap on a phone -- but not a full 44px iOS-ideal
     target either, since two of them have to share this already-narrow
     column with the avatar, name, position and start date. ~24x21px is
     the practical middle ground: comfortably tappable, still leaves room
     for everything else. */
  .staff-reorder-buttons { width: 24px; }
  .staff-reorder-btn { width: 24px; height: 21px; font-size: 11px; }
  #schedule-table .staff-name-text {
    white-space: normal;
    overflow-wrap: break-word;
    font-size: 0.76rem;
    line-height: 1.15;
  }
  .schedule-grid .staff-col .day-header-date { font-size: 0.68rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; max-width: 100%; }
  #schedule-table .schedule-day-heading { white-space: nowrap; }
  .schedule-grid .grid-head-cell { font-size: 0.72rem; padding: 0.35rem 0.25rem; }
  #schedule-table .shift-cell { padding: 0.25rem; }
  /* Visual cells are compact, but every tap target is a real >=44px box —
     the empty cell's whole <td> is the click target, and each chip/add
     button keeps a real minimum height even though its padding/font are
     small (only the width stays narrow, which is what "compact" means for
     day columns — height isn't part of that density goal). */
  #schedule-table .shift-cell.empty { min-height: 44px; }
  #schedule-table .shift-chip-wrap { min-height: 44px; display: flex; align-items: center; }
  #schedule-table .shift-chip { padding: 0.15rem 0.25rem; font-size: 0.72rem; width: 100%; }
  #schedule-table .shift-chip .shift-time { font-size: 0.72rem; }
  #schedule-table .shift-time-full { display: none; }
  #schedule-table .shift-time-compact { display: inline; }
  #schedule-table .shift-pos { display: none; }
  #schedule-table .cell-add-shift-btn { min-height: 44px; }
}

/* ---------- Public schedule lookup ---------- */
#lookup-staff-header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1rem 1.25rem 0;
}
#lookup-staff-header .color-dot { width: 14px; height: 14px; }
#lookup-staff-header h2 { font-size: 1.1rem; margin: 0; }
#lookup-staff-header .day-header-date { font-size: 0.85rem; }

.lookup-shift-list { padding: 0.75rem 1.25rem 2rem; display: flex; flex-direction: column; gap: 0.5rem; max-width: 640px; }
.lookup-day-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}
.lookup-day-label { min-width: 90px; font-weight: 600; }
.lookup-day-content { color: var(--text); }
.lookup-day-content .lookup-no-shift { color: var(--muted); font-style: italic; }
.lookup-shift-off { color: var(--muted); font-style: italic; }

/* ---------- Print ---------- */
#print-area { display: none; }

@media print {
  @page { size: landscape; margin: 1cm; }

  body > *:not(#print-area) { display: none !important; }
  #print-area { display: block !important; }

  .print-page {
    page-break-after: always;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .print-page:last-child { page-break-after: auto; }

  .print-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 0.4cm;
    border-bottom: 2px solid #000;
    padding-bottom: 0.2cm;
  }
  .print-header h1 { font-size: 16pt; margin: 0; }
  .print-header .print-week { font-size: 11pt; color: #444; }

  .print-table { width: 100%; border-collapse: collapse; font-size: 9pt; }
  .print-table th, .print-table td {
    border: 1px solid #999;
    padding: 4px 6px;
    text-align: left;
    vertical-align: top;
  }
  .print-table th { background: #eee !important; }
  .print-staff-name { font-weight: 600; }
  .print-staff-position { font-size: 8pt; color: #555; }
  .print-color-dot {
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 50%;
    margin-right: 0.3em;
  }
  .print-shift-off { color: #888; font-style: italic; }

  .print-footer { margin-top: 0.3cm; font-size: 7.5pt; color: #777; }
}

/* ICP filing footer (China mainland hosting requirement, anjugroup.store).
   Already excluded from print output by the body > *:not(#print-area)
   @media print rule above. */
.icp-footer { text-align: center; padding: 12px 0; font-size: 12px; color: #999; }
.icp-footer a { color: inherit; text-decoration: none; }
.icp-footer a:hover { text-decoration: underline; }
