/* === RESET & TOKENS === */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* iOS Safari: prevent auto-zoom on input focus; prevent double-tap zoom */
input,select,textarea{font-family:inherit;touch-action:manipulation}
button,a,[role="button"]{font-family:inherit;touch-action:manipulation}
:root{
  --navy:#2B3990;--navy-dark:#1a2570;--teal:#5DB8C0;--teal-light:#eaf6f7;
  --orange:#F15A29;--love-pink:#e91e8c;--love-dark:#880e4f;--love-light:#fce4ec;
  --bg:#0b1020;--surface:rgba(255,255,255,0.07);--surface-hover:rgba(255,255,255,0.04);
  --surface-modal:#1a1f2e;
  --border:rgba(255,255,255,0.1);--border-subtle:rgba(255,255,255,0.06);
  --text-primary:#fff;--text-secondary:rgba(255,255,255,0.7);
  /* --text-faint (white-20%, 1.83:1) is deliberately gone rather than raised.
     Every one of its five consumers turned out to carry information — four
     em-dashes standing for "nothing here" and one empty-state sentence — so
     there was no decorative consumer left for the token to serve, and a token
     whose only remaining job is to fail AA should not exist. The consumers now
     read --text-muted, and absence is kept quieter than data by weight (400
     against the 600-800 of the values around it) rather than by dimming. */
  --text-muted:rgba(255,255,255,0.52);
  /* Placeholders carry their own value rather than --text-muted. Muted text sits
     on the page and on panel surfaces; a placeholder sits inside an input, which
     is the lightest surface in the file once focused (white-12% over the login
     box). 52% clears AA on every muted surface but only reaches 4.30:1 there, so
     the two requirements genuinely differ and a shared token would have to serve
     the tighter one for no reason. Paired with font-style:italic below, so an
     empty field is told from a filled one by more than brightness. */
  --text-placeholder:rgba(255,255,255,0.62);
  --radius:12px;--radius-sm:8px;--radius-lg:16px;--radius-xs:6px;
  --blur:blur(20px);--shadow:0 8px 40px rgba(0,0,0,0.4);
  --transition:0.2s ease;
  --header-h:56px;--tab-h:44px;
  /* Deep variants of the ARK brand colours, for the ONE job the brand values
     cannot do: sit under white text. --navy, --teal, --orange and --love-pink
     each serve twice — as a fill and as accent text on the dark page — and no
     single value clears 4.5:1 both ways. Darkening the brand token itself
     would drop .tpl-section-title, .avail-section-heading, .btn-suggest and
     .login-error from ~7:1 to 3.57:1. So the brand tokens above are untouched
     and these carry the fills. Use a -deep token ONLY where white text sits on
     it; use the brand token everywhere else. */
  --teal-deep:#308285;--orange-deep:#d54410;--love-pink-deep:#e30a87;
  --success:#1d8928;
  --success-bg:rgba(76,175,80,0.15);--success-bg-hover:rgba(76,175,80,0.3);
  --success-border:rgba(76,175,80,0.4);--success-border-hover:rgba(76,175,80,0.7);
  --success-text:#81c784;--success-text-hover:#a5d6a7;
  --danger:#c62828;
  --danger-bg:rgba(198,40,40,0.15);--danger-border:rgba(198,40,40,0.4);--danger-text:#ef9a9a;
  --badge-verified:#69f0ae;--badge-no-location:#ffb74d;
  --badge-overridden:#9e9e9e;--badge-override:#ffd54f;
  /* Text colour for each status fill above. Recomputed at load by textOn() in
     _applyStatusTextColours(), so changing a fill changes its text colour with
     no further edit. The values declared here are what textOn() currently
     returns, so there is no flash before scripts run.
     Every fill is now dark enough to carry white, and the suite fails if any
     of them ever computes #000 again. textOn() stays because it is the
     mechanism that keeps that true without anyone remembering to check. */
  --on-success:#fff;--on-orange-deep:#fff;--on-teal-deep:#fff;--on-love-pink-deep:#fff;
}
/* Every placeholder in the file, not just the login one. The modal and filter
   inputs previously fell through to the UA default, which is a different value
   in every engine and testable in none. One rule makes the contrast a property
   of the stylesheet rather than of the browser. */
input::placeholder,textarea::placeholder{color:var(--text-placeholder);font-style:italic;opacity:1}
html,body{height:100%}
body{font-family:'Montserrat',sans-serif;background:var(--bg);color:var(--text-primary);min-height:100vh;-webkit-font-smoothing:antialiased}

/* === AMBIENT BACKGROUND === */
.bg-fixed{position:fixed;inset:0;z-index:0;pointer-events:none;contain:strict}
.bg-orb{position:absolute;border-radius:50%;filter:blur(100px);will-change:transform;animation:drift 14s ease-in-out infinite alternate}
.bg-orb:nth-child(1){width:700px;height:700px;background:radial-gradient(circle,var(--navy) 0%,transparent 70%);top:-200px;left:-200px;opacity:.55}
.bg-orb:nth-child(2){width:500px;height:500px;background:radial-gradient(circle,var(--teal) 0%,transparent 70%);bottom:-150px;right:-100px;opacity:.3;animation-delay:-5s}
.bg-orb:nth-child(3){width:350px;height:350px;background:radial-gradient(circle,var(--orange) 0%,transparent 70%);top:45%;left:55%;opacity:.12;animation-delay:-9s}
@keyframes drift{from{transform:translate(0,0)}to{transform:translate(40px,-50px)}}

/* === LOGIN === */
.login-wrap{min-height:100vh;position:relative;z-index:1;display:flex;align-items:center;justify-content:center;padding:16px}
.login-box{
  background:var(--surface);backdrop-filter:blur(28px);-webkit-backdrop-filter:blur(28px);
  border:1px solid rgba(255,255,255,0.12);border-radius:24px;padding:48px 40px;
  width:min(360px,100%);text-align:center;box-shadow:0 32px 80px rgba(0,0,0,0.5);
  animation:fadeUp .7s ease both;
}
.login-box h1{font-size:26px;font-weight:900;margin-bottom:4px}
.login-box .subtitle{font-size:12px;color:var(--text-muted);font-weight:500;margin-bottom:32px}
.login-icon{font-size:40px;margin-bottom:12px}
.pw-field{position:relative;width:100%;margin-bottom:12px}
.pw-field input{
  width:100%;padding:14px 52px 14px 18px;border:1px solid rgba(255,255,255,0.15);
  border-radius:10px;font-family:inherit;font-size:16px;text-align:center;
  letter-spacing:.05em;outline:none;background:rgba(255,255,255,0.08);color:#fff;
  transition:border-color var(--transition),background var(--transition);
}
.pw-field input:focus{border-color:var(--teal);background:rgba(255,255,255,0.12)}
.pw-toggle{
  position:absolute;right:14px;top:50%;transform:translateY(-50%);cursor:pointer;
  user-select:none;z-index:2;line-height:0;padding:6px;-webkit-tap-highlight-color:transparent;
}
.login-btn{
  width:100%;padding:14px;background:rgba(93,184,192,0.2);border:1px solid rgba(93,184,192,0.4);
  color:#fff;border-radius:10px;font-family:inherit;font-size:14px;font-weight:800;
  cursor:pointer;transition:background var(--transition),border-color var(--transition);
}
.login-btn:hover,.login-btn:focus-visible{background:rgba(93,184,192,0.35);border-color:rgba(93,184,192,0.7)}
.login-error{color:var(--orange);font-size:12px;font-weight:600;margin-top:10px;display:none}
.back-link{
  display:inline-block;margin-top:20px;color:var(--text-muted);font-size:12px;
  font-weight:600;text-decoration:none;transition:color var(--transition);
}
.back-link:hover,.back-link:focus-visible{color:rgba(255,255,255,0.7)}

@keyframes fadeUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}

/* === APP SHELL === */
.app{display:none;flex-direction:column;min-height:100vh;position:relative;z-index:1}
.app.visible{display:flex}

/* Header */
.app-header{
  background:var(--surface);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border-bottom:1px solid var(--border);color:#fff;padding:0 24px;height:var(--header-h);
  display:flex;align-items:center;justify-content:space-between;flex-shrink:0;
}
.app-header-left{display:flex;align-items:center;gap:12px;min-width:0}
.portal-btn{
  display:inline-flex;align-items:center;padding:5px 12px;border-radius:20px;
  font-size:11px;font-weight:700;letter-spacing:.05em;color:var(--text-muted);
  background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.12);
  text-decoration:none;transition:background 200ms cubic-bezier(0.2,0,0,1),border-color 200ms cubic-bezier(0.2,0,0,1),color 200ms cubic-bezier(0.2,0,0,1);white-space:nowrap;
}
.portal-btn:hover{color:#fff;background:rgba(255,255,255,0.14);border-color:rgba(255,255,255,0.25)}
.header-badge{
  background:rgba(255,255,255,0.12);padding:3px 10px;border-radius:20px;
  font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);
}
.app-header h1{font-size:17px;font-weight:900;letter-spacing:-.01em;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* Month nav */
.month-nav{display:flex;align-items:center;gap:8px}
.month-nav button{
  background:none;border:none;color:rgba(255,255,255,0.7);
  width:32px;height:32px;border-radius:var(--radius-xs);cursor:pointer;font-size:14px;
  display:flex;align-items:center;justify-content:center;
  transition:background 150ms cubic-bezier(0.2,0,0,1);
}
.month-nav button:hover{background:rgba(255,255,255,0.12);color:#fff}
.month-label{font-size:18px;font-weight:800;min-width:170px;text-align:center}

/* Venue tabs */
.venue-bar{
  background:rgba(255,255,255,0.04);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border-bottom:1px solid rgba(255,255,255,0.08);padding:0 24px;
  display:flex;align-items:flex-end;gap:4px;flex-shrink:0;
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.venue-bar::-webkit-scrollbar{display:none}
.venue-tab{
  padding:12px 24px;border:none;background:none;font-family:inherit;font-size:13px;
  font-weight:700;color:var(--text-muted);cursor:pointer;border-bottom:3px solid transparent;
  margin-bottom:-1px;transition:color var(--transition),border-bottom-color var(--transition);white-space:nowrap;
}
.venue-tab:hover{color:var(--text-secondary)}
.venue-tab.active{color:#fff;border-bottom-color:var(--teal)}
.venue-tab.love.active{border-bottom-color:var(--love-pink)}
.venue-tab.hours.active{border-bottom-color:var(--success)}
.venue-tab.manage.active{border-bottom-color:#ff9800}
.venue-tab.templates.active{border-bottom-color:#ab47bc}
.venue-tab.signoffs.active{border-bottom-color:#26c6da}
/* Sign-Offs tab */
.signoffs-wrap{padding:20px;max-width:1200px;margin:0 auto}
.signoffs-filters{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:16px;padding:14px 16px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm)}
.signoffs-filters label{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted)}
.signoffs-filters input,.signoffs-filters select{padding:8px 10px;border:1px solid var(--border);background:var(--bg);color:var(--text-primary);border-radius:var(--radius-xs);font-family:inherit;font-weight:500;min-height:36px;transition:border-color 150ms cubic-bezier(0.2,0,0,1)}
.signoffs-filters input{font-size:16px}
/* 16px, not 13px: iOS zooms the page on focus for anything under 16px. #250 fixed
   the inputs for that reason and the select was missed. Same fix, same reason. */
.signoffs-filters select{font-size:16px}
.signoffs-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.signoffs-table{width:100%;border-collapse:collapse;font-family:'Montserrat',sans-serif}
.signoffs-table th{text-align:left;padding:10px 12px;border-bottom:2px solid var(--border);color:var(--text-secondary);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;line-height:1.4}
.signoffs-table td{padding:12px;border-bottom:1px solid var(--border);font-size:13px;vertical-align:middle;line-height:1.4}
.signoffs-table tr:hover{background:rgba(255,255,255,0.03)}
.signoffs-table tr.so-overridden td{opacity:0.45;text-decoration:line-through}
.signoffs-table tr.so-overridden .so-override-badge{text-decoration:none;opacity:1}
/* #252 — Sign-Offs mobile layout.
   .so-field is display:contents on desktop, so its label and control stay
   DIRECT flex children of .signoffs-filters and the desktop row renders exactly
   as it did before this run. The mobile block at the bottom of this file turns
   the wrapper back into a real box and stacks it. Nothing here introduces a new
   colour value: every declaration below resolves to an existing token or to an
   rgba tint the file already uses. */
.so-field{display:contents}
.so-cards{display:none}
.so-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px 14px;margin-bottom:8px}
.so-card-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:6px}
.so-card-dj{font-size:14px;font-weight:800}
.so-card-meta{font-size:12px;color:var(--text-secondary);line-height:1.5}
.so-card-actions{display:flex;gap:8px;margin-top:10px}
.so-chips{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:-4px 0 14px}
.so-chip{display:inline-flex;align-items:center;gap:6px;padding:5px 10px;background:rgba(93,184,192,0.14);border:1px solid rgba(93,184,192,0.36);color:var(--teal);border-radius:20px;font-size:11px;font-weight:700}
.so-chip b{font-weight:800;color:var(--text-primary)}
.so-clear{margin-left:auto;background:none;border:none;color:var(--text-muted);font-family:inherit;font-size:11px;font-weight:700;text-decoration:underline;cursor:pointer;min-height:44px;padding:0 4px}
.so-empty{text-align:center;padding:40px 24px;background:var(--surface);border:1px dashed var(--border);border-radius:var(--radius-sm)}
.so-empty-t{font-size:14px;font-weight:800;margin-bottom:6px}
.so-empty-d{font-size:12px;color:var(--text-muted);line-height:1.6;margin-bottom:16px}
.so-empty-a{background:none;border:none;color:var(--teal);font-family:inherit;font-size:12px;font-weight:800;text-decoration:underline;cursor:pointer;min-height:44px}
.so-skel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px 14px;margin-bottom:8px}
.so-skel-bar{height:11px;border-radius:6px;background:rgba(255,255,255,0.09);animation:soPulse 1.4s ease-in-out infinite}
.so-skel-bar+.so-skel-bar{margin-top:9px}
@keyframes soPulse{0%,100%{opacity:.5}50%{opacity:1}}
.badge-verified{background:rgba(76,175,80,0.18);color:var(--badge-verified);border:1px solid var(--success-border);padding:4px 10px;border-radius:20px;font-size:10px;font-weight:800;white-space:nowrap;letter-spacing:.04em;display:inline-block;line-height:1.2}
.badge-no-location{background:rgba(255,152,0,0.14);color:var(--badge-no-location);border:1px solid rgba(255,152,0,0.35);padding:4px 10px;border-radius:20px;font-size:10px;font-weight:800;white-space:nowrap;letter-spacing:.04em;display:inline-block;line-height:1.2}
.badge-overridden{background:rgba(120,120,120,0.18);color:var(--badge-overridden);border:1px solid rgba(120,120,120,0.3);padding:4px 10px;border-radius:20px;font-size:10px;font-weight:800;white-space:nowrap;letter-spacing:.04em;display:inline-block;line-height:1.2}
.badge-signed-off{background:rgba(76,175,80,0.18);color:var(--badge-verified);border:1px solid var(--success-border);padding:4px 10px;border-radius:20px;font-size:10px;font-weight:800;white-space:nowrap;letter-spacing:.04em;display:inline-block;line-height:1.2}
.badge-override{background:rgba(255,193,7,0.16);color:var(--badge-override);border:1px solid rgba(255,193,7,0.4);padding:4px 10px;border-radius:20px;font-size:10px;font-weight:800;white-space:nowrap;letter-spacing:.04em;display:inline-block;line-height:1.2}
/* #282 — this record holds at least one hour that is no longer on the roster,
   so that hour has stopped counting towards pay. Same pill shape as the badge
   family above, in the danger colours already defined at the top of this file. */
.badge-not-on-roster{background:var(--danger-bg);color:var(--danger-text);border:1px solid var(--danger-border);padding:4px 10px;border-radius:20px;font-size:10px;font-weight:800;white-space:nowrap;letter-spacing:.04em;display:inline-block;line-height:1.2}
.so-counted{font-size:11px;color:var(--danger-text);font-weight:700;white-space:nowrap}

/* Outstanding DJ help requests (#232 Group B, B2).
   Sits at the top of the Sign-Offs tab because this is the queue a manager is
   meant to act on, and it is deliberately NOT filtered by the date picker below
   it — "what is still outstanding" is not a question about one day.
   Every colour here is an existing roster.html token, so no new value is
   introduced; the three pairings are pinned in tests/group-b-dj-surfaces.test.js
   because roster.html has no per-rule contrast register the way dj.html does. */
.so-help-panel{margin-bottom:16px;padding:14px 16px;background:rgba(241,90,41,0.10);border:1px solid rgba(241,90,41,0.32);border-radius:var(--radius-sm)}
.so-help-title{font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--orange);margin-bottom:10px}
.so-help-row{display:flex;align-items:center;gap:10px;padding:8px 0;font-size:13px;flex-wrap:wrap}
.so-help-row+.so-help-row{border-top:1px solid var(--border)}
.so-help-dj{font-weight:800;min-width:110px}
.so-help-when{color:var(--text-secondary);flex:1;min-width:180px}
.so-help-unsent{color:var(--badge-no-location);font-size:10px;font-weight:800;letter-spacing:.04em}

/* Templates tab */
.tpl-wrap{padding:20px;max-width:1200px;margin:0 auto}
.tpl-section{margin-bottom:32px}
.tpl-section-title{font-size:14px;font-weight:800;text-transform:uppercase;letter-spacing:.08em;color:var(--teal);margin-bottom:12px}
.tpl-grid-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.tpl-grid{border-collapse:collapse;font-family:'Montserrat',sans-serif;width:100%}
.tpl-grid th{padding:8px 6px;border-bottom:2px solid var(--border);color:var(--text-secondary);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;text-align:center;white-space:nowrap}
.tpl-grid td{padding:6px 4px;border-bottom:1px solid var(--border);text-align:center;font-size:11px;vertical-align:middle}
.tpl-grid tr:hover{background:rgba(255,255,255,0.03)}
.tpl-grid select{padding:4px 6px;background-color:var(--surface-modal);border:1px solid var(--border);color:var(--text-primary);border-radius:4px;font-family:inherit;font-size:11px;max-width:110px;cursor:pointer}
.tpl-grid select:focus{outline:none;border-color:var(--teal)}
.tpl-grid select option{background:var(--surface-modal);color:#ffffff}
.tpl-grid select option:hover,
.tpl-grid select option:checked{background:var(--teal-deep);color:var(--on-teal-deep)}
.tpl-grid .slot-label{text-align:right;padding-right:10px;color:var(--text-muted);font-weight:600;white-space:nowrap;font-size:11px}
.tpl-btn{padding:8px 20px;border:1px solid transparent;border-radius:var(--radius-xs);font-family:inherit;font-size:12px;font-weight:700;cursor:not-allowed;opacity:0.5}
.tpl-btn.save{background:rgba(93,184,192,0.15);border-color:rgba(93,184,192,0.4);color:var(--teal)}
.tpl-btn.save:not(:disabled){cursor:pointer;opacity:1;transition:background 150ms cubic-bezier(0.2,0,0,1),border-color 150ms cubic-bezier(0.2,0,0,1),color 150ms cubic-bezier(0.2,0,0,1)}
.tpl-btn.save:not(:disabled):hover{background:rgba(93,184,192,0.3);border-color:var(--teal);color:#fff}
.manage-djs-wrap{padding:20px;max-width:1200px;margin:0 auto}
.manage-djs-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.manage-djs-table{width:100%;border-collapse:collapse;margin-top:12px;font-family:'Montserrat',sans-serif}
.manage-djs-table th{text-align:left;padding:10px 12px;border-bottom:2px solid var(--border);color:var(--text-secondary);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em}
.manage-djs-table td{padding:10px 12px;border-bottom:1px solid var(--border)}
.manage-djs-table tr:hover{background:rgba(255,255,255,0.03)}
@media(min-width:769px){.manage-djs-table td{font-size:13px;font-weight:500}}
.badge-active{background:var(--success);color:var(--on-success);padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;font-family:inherit}
.badge-inactive{background:#666;color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;font-family:inherit}
/* #339: inactive Hours rows, the Changes panel, and the DJ modal states */
/* Greyed by weight, not by contrast: live text never drops below
   --text-secondary (brand colour.md, "drop its weight, not its contrast").
   The bright parts of a row (white hours value, teal bar, orange cost) go
   grey too, and the bar takes the same #aaa as the inactive DJ's dot. */
.hours-table tr.row-inactive td{color:var(--text-secondary);font-weight:400}
.hours-table tr.row-inactive .dj-name,
.hours-table tr.row-inactive .hours-val{color:var(--text-secondary);font-weight:400}
.hours-table tr.row-inactive .hours-bar-fill{background:#aaa}
.name-tags{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.changes-panel{margin-top:20px;padding:14px 16px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm)}
.changes-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-bottom:6px}
.changes-title{font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--text-secondary)}
.changes-month{font-size:11px;font-weight:700;color:var(--text-muted)}
.changes-row{display:grid;grid-template-columns:130px minmax(0,1fr) auto;gap:4px 12px;align-items:start;padding:10px 0;font-size:13px;line-height:1.5}
.changes-row+.changes-row{border-top:1px solid var(--border)}
.changes-row.fresh{animation:fadeUp 220ms cubic-bezier(0.16,1,0.3,1)}
.changes-what{min-width:0}
.changes-what b{font-weight:800}
.changes-why{color:var(--text-secondary);font-size:12px}
.changes-when{font-size:11px;color:var(--text-muted);white-space:nowrap;font-variant-numeric:tabular-nums}
.changes-empty{font-size:12px;color:var(--text-muted);padding:10px 0;line-height:1.6}
.refuse-box{background:var(--danger-bg);border:1px solid var(--danger-border);color:var(--danger-text);padding:10px 12px;border-radius:var(--radius-xs);font-size:13px;line-height:1.55;margin:0 0 16px}
.refuse-box ul{margin:6px 0 0 18px}
.checking{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--text-secondary);margin:0 0 16px}
.rename-note{font-size:12px;color:var(--text-secondary);line-height:1.5;margin:-4px 0 10px}
.field-err{color:var(--danger-text);font-size:12px;margin:0 0 12px;line-height:1.5}
.btn-busy{opacity:.6;pointer-events:none}
@media(max-width:600px){.changes-row{grid-template-columns:minmax(0,1fr)}.changes-when{white-space:normal}}
.badge-locked{background:var(--danger);color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;font-family:inherit}
.manage-btn{padding:6px 12px;border:1px solid var(--border);background:var(--surface);color:var(--text-primary);border-radius:var(--radius-xs);cursor:pointer;font-size:12px;font-weight:600;font-family:'Montserrat',sans-serif;margin:2px;min-height:36px;display:inline-flex;align-items:center;transition:background var(--transition)}
.manage-btn:hover{background:rgba(255,255,255,0.1)}
.manage-btn:disabled{opacity:0.4;cursor:not-allowed}
.manage-btn:disabled:hover{background:var(--surface)}
.manage-btn.danger{border-color:var(--danger-border);color:var(--danger-text)}
.manage-btn.danger:hover{background:var(--danger-bg)}
.manage-btn.teal{background:rgba(93,184,192,0.15);border-color:rgba(93,184,192,0.4);color:var(--teal)}
.manage-btn.teal:hover{background:rgba(93,184,192,0.3);border-color:var(--teal);color:#fff}
/* Actions dropdown for mobile */
.manage-actions-wrap{position:relative;display:inline-block}
.manage-actions-toggle{display:none}
.manage-actions-menu{
  display:none;position:absolute;right:0;top:100%;z-index:100;
  background:rgba(20,25,50,0.98);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  border:1px solid rgba(255,255,255,0.15);border-radius:var(--radius);padding:6px 0;
  min-width:170px;box-shadow:0 12px 40px rgba(0,0,0,0.5);
}
.manage-actions-menu.open{display:block}
.manage-actions-menu button{
  display:block;width:100%;text-align:left;padding:11px 16px;border:none;
  background:none;color:var(--text-primary);font-family:inherit;font-size:13px;
  font-weight:600;cursor:pointer;transition:background 150ms cubic-bezier(0.2,0,0,1);white-space:nowrap;
}
.manage-actions-menu button:hover{background:rgba(255,255,255,0.08)}
.manage-actions-menu button.danger{color:var(--danger-text)}
.manage-add-form{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:16px;padding:12px;background:var(--surface);border:1px solid var(--border);border-radius:8px}
.manage-add-form input,.manage-add-form select{padding:10px 16px;border:1px solid var(--border);background:var(--bg);color:var(--text-primary);border-radius:var(--radius-xs);font-family:inherit;font-weight:500;min-height:44px}
.manage-add-form input{font-size:16px}
.manage-add-form select{font-size:13px}
.manage-add-form button{padding:10px 16px;background:rgba(93,184,192,0.15);color:var(--teal);border:1px solid rgba(93,184,192,0.4);border-radius:var(--radius-xs);cursor:pointer;font-family:inherit;font-size:13px;font-weight:700;min-height:44px;transition:background 150ms cubic-bezier(0.2,0,0,1),border-color 150ms cubic-bezier(0.2,0,0,1),color 150ms cubic-bezier(0.2,0,0,1)}
.manage-add-form button:hover{background:rgba(93,184,192,0.3);border-color:var(--teal);color:#fff}
.manage-edit-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);display:flex;align-items:center;justify-content:center;z-index:1000;animation:fadeIn .2s ease}
.manage-edit-modal{background:var(--surface-modal);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);border:1px solid var(--border);border-radius:var(--radius);padding:24px;min-width:320px;max-width:90vw;overflow-x:hidden;animation:slideUp 220ms cubic-bezier(0.16,1,0.3,1);box-shadow:0 24px 60px rgba(0,0,0,0.5)}
.so-manual-modal{min-width:380px;max-width:480px}
.manage-edit-modal h3{margin:0 0 16px;color:var(--text-primary);font-size:16px;font-weight:800;line-height:1.3;letter-spacing:-.01em}
.manage-edit-modal label{display:block;margin-bottom:12px;color:var(--text-secondary);font-size:12px;font-weight:600}
.manage-edit-modal input,.manage-edit-modal select{width:100%;padding:10px 12px;border:1px solid var(--border);background:var(--bg);color:var(--text-primary);border-radius:var(--radius-xs);margin-top:4px;box-sizing:border-box;font-family:inherit;font-weight:500;transition:border-color 150ms cubic-bezier(0.2,0,0,1)}
.manage-edit-modal input{font-size:16px}
.manage-edit-modal select{font-size:13px}
#soMfDate{-webkit-appearance:none;appearance:none;display:block;width:100%;min-width:0;max-width:100%;height:40px;min-height:40px;box-sizing:border-box}
#soMfDate::-webkit-calendar-picker-indicator{display:block;cursor:pointer;opacity:1}
#soMfDate::-webkit-date-and-time-value{text-align:left;margin:0}
/* #268 — iOS Safari gives <input type="date"> an intrinsic size that ignores
   width:100%, so the control runs past its box. #soMfDate was given this
   treatment by 45994eb; its sibling in the Sign-Offs filters never was. */
#soDateFilter{
  -webkit-appearance:none;appearance:none;
  display:block;width:100%;min-width:0;max-width:100%;
  height:44px;min-height:44px;box-sizing:border-box;
}
#soDateFilter::-webkit-calendar-picker-indicator{display:block;cursor:pointer;opacity:1}
#soDateFilter::-webkit-date-and-time-value{text-align:left;margin:0}
.manage-edit-modal input:focus,.manage-edit-modal select:focus{outline:none;border-color:var(--teal)}
.manage-edit-modal .modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:20px}
.avail-grid{border-collapse:collapse;width:100%;font-size:12px}
.avail-grid th{padding:4px 6px;text-align:center;color:var(--text-secondary);font-size:11px}
.avail-grid td{padding:4px 6px;text-align:center}
.avail-grid input[type="checkbox"]{width:16px;height:16px;cursor:pointer}
.avail-grid td.disabled-slot input[type="checkbox"]{opacity:0.25;cursor:not-allowed}
.avail-section-heading{font-size:13px;font-weight:700;margin:16px 0 8px;padding-bottom:6px;border-bottom:1px solid var(--border)}
.avail-section-heading.teal{color:var(--teal)}
.avail-section-heading.pink{color:var(--love-pink)}

/* Main content */
.main-content{flex:1;overflow:auto;padding:20px 24px;-webkit-overflow-scrolling:touch}

/* === ROSTER TABLE === */
.grid-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;border-radius:var(--radius);background:#fff;box-shadow:var(--shadow)}
.grid-wrap::-webkit-scrollbar{height:6px}
.grid-wrap::-webkit-scrollbar-track{background:transparent}
.grid-wrap::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.15);border-radius:3px}

.roster-table{border-collapse:collapse;min-width:100%;font-size:11px}
.roster-table thead th{
  padding:10px 6px;text-align:center;font-weight:800;font-size:10px;white-space:nowrap;
  position:sticky;top:0;z-index:10;border-bottom:2px solid #dde2f0;letter-spacing:.03em;
}
.th-date{
  background:var(--navy)!important;color:#fff!important;min-width:110px;
  text-align:left!important;padding-left:14px!important;
  position:sticky!important;left:0!important;z-index:20!important;
}
.th-arkbar{background:var(--teal-light);color:var(--navy);min-width:82px}
.th-hip{background:var(--orange-deep);color:var(--on-orange-deep);min-width:82px}
.th-love{background:var(--love-light);color:var(--love-dark);min-width:82px}
.th-section-label{padding:6px;font-size:11px;font-weight:800;text-align:center;letter-spacing:.05em}

.roster-table tbody tr:nth-child(even){background:#fafbff}
.roster-table tbody tr:hover{background:#f4f5ff}

.td-date{
  padding:8px 14px;font-weight:700;font-size:11px;color:#1c2340;white-space:nowrap;
  border-right:2px solid #dde2f0;position:sticky;left:0;z-index:5;min-width:110px;background:#fff;
}
.roster-table tbody tr:nth-child(even) .td-date{background:#fafbff}
.roster-table tbody tr:hover .td-date{background:#f4f5ff}
.td-date .dow{color:var(--teal);font-size:9px;font-weight:800;text-transform:uppercase;letter-spacing:.1em;display:block}
.td-date.weekend .dow{color:var(--orange)}

  /* Today's date row — highlight in Bangkok timezone */
  .today-row > .td-date { background: #fffbe6; border-left: 3px solid #f0c040; }
  .roster-table tbody .today-row:nth-child(even) > .td-date { background: #fff8d6; }
  .roster-table tbody .today-row:hover > .td-date { background: #fff4c2; }

.td-slot{padding:4px;border:1px solid #eef0f8;min-width:82px;vertical-align:middle;position:relative}
.td-slot.hip-slot{background:rgba(241,90,41,0.04);border-color:rgba(241,90,41,0.15)}
.td-slot.love-slot{background:rgba(233,30,140,0.03);border-color:rgba(233,30,140,0.12)}
.td-slot.empty-slot{background:#f9f9f9}
.slot-select{
  width:100%;padding:5px 3px;border:1.5px solid transparent;border-radius:6px;
  font-family:inherit;font-size:10px;font-weight:700;color:#1c2340;background:transparent;
  cursor:pointer;outline:none;transition:border-color 150ms cubic-bezier(0.2,0,0,1),background 150ms cubic-bezier(0.2,0,0,1);appearance:none;-webkit-appearance:none;
  text-align:center;min-height:28px;
}
.slot-select:hover{border-color:var(--teal);background:var(--teal-light)}
.slot-select:focus{border-color:var(--navy);background:#fff;box-shadow:0 0 0 3px rgba(43,57,144,0.1)}
.slot-select.assigned{color:#fff!important;font-weight:800!important}
.slot-select.unassigned{color:#9aa0b8}
.slot-select.no-avail{background:#f5f5f5;color:#6e6e6e;cursor:not-allowed;font-size:10px}
.slot-select option{background:var(--surface-modal);color:#fff}


/* === STATUS BAR === */
.status-bar{
  background:var(--surface);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border-top:1px solid var(--border);padding:10px 24px;display:flex;align-items:center;gap:20px;
  flex-shrink:0;font-size:11px;font-weight:600;color:var(--text-muted);
}
.status-pill{display:none}
.status-bar-right{margin-left:auto;display:flex;gap:10px}
.btn-sm{
  padding:7px 16px;border-radius:var(--radius-sm);font-family:inherit;font-size:11px;
  font-weight:700;cursor:pointer;transition:background 150ms cubic-bezier(0.2,0,0,1),border-color 150ms cubic-bezier(0.2,0,0,1),color 150ms cubic-bezier(0.2,0,0,1);border:1px solid rgba(255,255,255,0.15);
  background:rgba(255,255,255,0.08);color:#fff;backdrop-filter:blur(10px);
}
.btn-sm:hover{background:rgba(255,255,255,0.15);border-color:rgba(255,255,255,0.3)}
.btn-suggest{background:rgba(93,184,192,0.15);border-color:rgba(93,184,192,0.4);color:var(--teal)}
.btn-suggest:hover{background:rgba(93,184,192,0.3);border-color:var(--teal);color:#fff}
.btn-clear{background:var(--danger-bg);border-color:var(--danger-border);color:var(--danger-text);margin-left:24px}
.btn-clear:hover{background:rgba(198,40,40,0.28);border-color:var(--danger);color:#fff}
.btn-print{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}
.btn-print:hover{background:var(--success-bg-hover);border-color:var(--success-border-hover);color:var(--success-text-hover)}

/* === LOADING & TOAST === */
.loading{text-align:center;padding:80px 40px;color:var(--text-muted);font-weight:600;font-size:13px}
.spinner{
  width:32px;height:32px;border:3px solid rgba(255,255,255,0.1);border-top-color:var(--teal);
  border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 16px;
}
@keyframes spin{to{transform:rotate(360deg)}}

.toast{
  position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(0.9);
  min-width:320px;max-width:480px;text-align:center;
  background:rgba(30,40,80,0.95);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid rgba(255,255,255,0.15);color:#fff;padding:20px 28px;
  border-radius:10px;font-size:14px;font-weight:700;box-shadow:0 16px 48px rgba(0,0,0,0.6);
  opacity:0;transition:transform 300ms cubic-bezier(0.16,1,0.3,1),opacity 200ms ease;z-index:9999;pointer-events:none;
}
.toast.show{transform:translate(-50%,-50%) scale(1);opacity:1;pointer-events:auto}
.toast.success{background:rgba(27,94,32,0.95);border-color:rgba(105,240,174,0.3)}
.toast.error{background:rgba(183,28,28,0.95);border:1px solid rgba(255,100,100,0.6);box-shadow:0 16px 48px rgba(0,0,0,0.6)}

/* === HOURS PAGE === */
.hours-wrap{display:flex;flex-direction:column;gap:20px}
.summary-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:14px}
.summary-card{
  background:var(--surface);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--border);border-radius:var(--radius);padding:18px;
}
.card-label{font-size:10px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}
.card-value{font-size:26px;font-weight:900;color:#fff}
.card-value-sm{font-size:18px}
.card-sub{font-size:11px;font-weight:600;color:var(--text-muted);margin-top:2px}

.hours-table-wrap{
  background:rgba(255,255,255,0.06);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);
  border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;
}
.hours-table-header{padding:14px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.hours-table-header h2{font-size:14px;font-weight:800}
.hours-table{width:100%;border-collapse:collapse}
.hours-table thead th{
  padding:9px 14px;text-align:left;font-size:10px;font-weight:800;color:var(--text-muted);
  text-transform:uppercase;letter-spacing:.08em;border-bottom:1px solid var(--border);background:rgba(255,255,255,0.03);
}
.hours-table thead th.right,.hours-table tbody td.right{text-align:right}
.hours-table tbody tr{border-bottom:1px solid var(--border-subtle);transition:background .1s}
.hours-table tbody tr:last-child{border-bottom:none}
.hours-table tbody tr:hover{background:var(--surface-hover)}
.hours-table tbody td{padding:11px 14px;font-size:12px;font-weight:600;color:var(--text-secondary)}

.dj-name-cell{display:flex;align-items:center;gap:10px}
.dj-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}
.dj-name{font-weight:700;font-size:12px;color:rgba(255,255,255,0.75);transition:color 150ms cubic-bezier(0.2,0,0,1)}
.dj-name-cell:hover .dj-name{color:#fff}
.hours-bar-wrap{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.hours-bar-bg{flex:1;height:7px;background:rgba(255,255,255,0.1);border-radius:4px;min-width:60px}
.hours-bar-fill{height:100%;border-radius:3px;background:var(--teal)}
.hours-bar-fill.resident{background:#7c83ff}
.hours-bar-fill.zero{background:rgba(255,255,255,0.08)}
.hours-val{font-size:12px;font-weight:800;color:#fff;min-width:30px;text-align:right}
.badge{padding:2px 7px;border-radius:20px;font-size:11px;font-weight:800;text-transform:uppercase}
.badge-low{background:rgba(230,81,0,0.25);color:#ffab40;border:1px solid rgba(230,81,0,0.3)}
.badge-ok{background:rgba(46,125,50,0.25);color:#69f0ae;border:1px solid rgba(46,125,50,0.3)}
.badge-needed{padding:2px 8px;border-radius:20px;font-size:11px;font-weight:800;background:#fff8e1;color:#9a4f00;border:1px solid #ffe082;margin-left:6px}
.totals-row{background:rgba(255,255,255,0.05)!important}
.totals-row td{font-weight:800!important;color:#fff!important}


/* === RESIDENT HOURS SIDEBAR === */
.hours-toggle{
  position:fixed;right:0;top:50%;transform:translateY(-50%);
  background:rgba(43,57,144,0.85);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border:1px solid rgba(93,184,192,0.4);border-right:none;border-radius:10px 0 0 10px;
  padding:10px 8px;cursor:pointer;z-index:200;color:#fff;font-size:11px;font-weight:800;
  writing-mode:vertical-rl;text-orientation:mixed;letter-spacing:.08em;
  transition:background var(--transition),padding-right var(--transition);display:none;
}
.hours-toggle:hover{background:rgba(43,57,144,0.95);padding-right:12px}
.app.visible .hours-toggle{display:block}
.app.visible .hours-toggle.hidden{display:none}

.hours-panel-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:250;display:none;cursor:pointer}
.hours-panel-overlay.open{display:block}
.hours-panel{
  position:fixed;right:-280px;top:0;bottom:0;width:270px;
  background:rgba(15,20,45,0.97);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border-left:1px solid rgba(93,184,192,0.3);z-index:300;
  transition:right 220ms cubic-bezier(0.16,1,0.3,1);overflow-y:auto;display:flex;flex-direction:column;
}
.hours-panel.open{right:0}
.hours-panel-header{padding:16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.hours-panel-header h3{font-size:13px;font-weight:800}
.hours-panel-close{background:none;border:none;color:var(--text-muted);font-size:18px;cursor:pointer;padding:4px 8px;border-radius:var(--radius-xs);transition:background 150ms cubic-bezier(0.2,0,0,1),color 150ms cubic-bezier(0.2,0,0,1)}
.hours-panel-close:hover{color:#fff;background:rgba(255,255,255,0.1)}
.hours-panel-body{padding:16px;flex:1}
.hp-row{display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid var(--border-subtle)}
.hp-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.hp-name{flex:1;font-size:11px;font-weight:700;color:var(--text-secondary)}
.hp-hours{font-size:13px;font-weight:900;color:#fff;min-width:36px;text-align:right}
.hp-target{font-size:9px;font-weight:700;color:var(--text-muted);min-width:30px}
.hp-bar{flex:1;height:4px;background:rgba(255,255,255,0.08);border-radius:2px;min-width:40px}
.hp-bar-fill{height:100%;border-radius:2px;transition:width .3s}
.hp-bar-fill.ok{background:var(--teal)}
.hp-bar-fill.warn{background:#ffab40}
.hp-bar-fill.over{background:#ff5252}

/* === DJ DETAIL MODAL === */
.dj-detail-stat-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding:16px 24px;border-bottom:1px solid var(--border-subtle)}
.dj-detail-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.dj-detail-table{width:100%;border-collapse:collapse;min-width:500px}
.dj-slot-cell{display:flex;flex-wrap:wrap;gap:3px;align-items:center}
.dj-slot-tag{background:rgba(255,255,255,0.08);padding:3px 7px;border-radius:var(--radius-xs);font-size:10px;white-space:nowrap;font-weight:600}
/* A rostered slot that was never signed off, in the DJ-detail modal. Reuses the
   amber this file already uses for a partial sign-off count rather than adding
   a colour. Measured against the ACTUAL composite — page #0b1020, overlay
   rgba(0,0,0,0.6), content rgba(15,20,45,0.98), then this 12% fill — where
   #ffb74d reaches 8.41:1 against the 4.5:1 AA floor. No opacity here or on any
   ancestor of the modal: an opacity multiplies the resolved colour, which is
   how two earlier audits were fooled. The opacity:0.45 rule in this file is on
   .signoffs-table tr.so-overridden, a different subtree that this class never
   enters. tests/signed-slots-detail.test.js measures both. */
.dj-slot-tag.missed{background:rgba(255,183,77,0.12);box-shadow:inset 0 0 0 1px rgba(255,183,77,0.3);color:#ffb74d}
@media(max-width:600px){
  .dj-detail-stat-grid{grid-template-columns:1fr 1fr;padding:10px 16px;gap:8px}
}

/* === DESKTOP: >= 1024px === */
@media(min-width:1024px){
  .manage-add-form{flex-direction:column;align-items:stretch}
  .manage-add-form button{align-self:flex-end;width:auto}
}

/* === PRINT HEADER === */
.print-header{display:none;text-align:center;margin-bottom:8px;font-size:14px;font-weight:800;color:var(--navy)}

/* === REDUCED MOTION === */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  .bg-orb{animation:none!important}
  /* #255 — the tab bar's smooth scroll is motion too. Jump instead. */
  .venue-bar{scroll-behavior:auto!important}
}

/* === #252 SIGN-OFFS MOBILE: <= 640px ===
   Scoped to the Sign-Offs tab only. The admin tab bar is deliberately absent
   from this block: its clipping is #255, and the bar is shared by every tab. */
@media (max-width:640px){
  .so-manual{display:block;width:100%;min-height:48px;margin-bottom:14px}
  .signoffs-filters{flex-direction:column;align-items:stretch;gap:0}
  .so-field{display:block;margin-bottom:14px}
  .so-field label{display:block;margin-bottom:6px}
  .so-field input,.so-field select{display:block;width:100%;min-height:44px}
  .so-refresh{display:block;width:100%;min-height:44px}
  .so-cards{display:block}
  .signoffs-table-wrap{display:none}
}

/* === MOBILE FIRST: <= 768px === */
@media(max-width:768px){
  .bg-orb{animation:none;opacity:.3}
  /* Header */
  .app-header{height:auto;padding:10px 14px;flex-wrap:wrap;gap:8px}
  .app-header-left{flex:1;min-width:0}
  .app-header h1{font-size:13px}
  .header-badge{font-size:9px;padding:2px 7px}
  .portal-btn{min-height:44px;display:inline-flex;align-items:center}
  /* Month nav */
  .month-nav{width:100%;justify-content:center}
  .month-label{font-size:14px;min-width:110px}
  .month-nav button{width:44px;height:44px;font-size:16px;min-height:44px}
  /* Venue tabs */
  .venue-bar{padding:0 4px;gap:0}
  .venue-tab{padding:10px 10px;font-size:11px;min-height:44px}
  /* #255 — six tabs need 542px and the bar has 393px, so on a phone it always
     overflows. Left free, it comes to rest anywhere and cuts a label mid-word
     ("Love Beach Club" reading as "ach Club"), with no edge marked as cut.
     Snap stops a tab resting half cut. The mask fades whichever edge IS cut, so
     the bar says it has more to show. scroll-padding-inline-start keeps the
     snapped tab clear of the left fade, so the fade falls on the tail of the
     tab behind it rather than dimming the first letters of a whole one.
     data-edge is set by syncVenueBarEdges(); "none" deliberately has no rule,
     so a bar with room is exactly as it was. */
  .venue-bar{scroll-snap-type:x proximity;scroll-behavior:smooth;scroll-padding-inline-start:26px}
  .venue-tab{scroll-snap-align:start}
  .venue-bar[data-edge="both"]{
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 26px,#000 calc(100% - 26px),transparent 100%);
            mask-image:linear-gradient(90deg,transparent 0,#000 26px,#000 calc(100% - 26px),transparent 100%);
  }
  .venue-bar[data-edge="start"]{
    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 26px);
            mask-image:linear-gradient(90deg,transparent 0,#000 26px);
  }
  .venue-bar[data-edge="end"]{
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent 100%);
            mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent 100%);
  }
  /* Main content */
  .main-content{padding:8px}
  /* Roster grid */
  .td-date{min-width:66px;padding:4px 6px;font-size:10px}
  .td-date .dow{font-size:8px}
  .td-slot{min-width:88px;padding:2px}
  .slot-select{min-height:36px;padding:4px 2px;font-size:11px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}
  .dj-name{font-size:11px}
  .roster-table thead th{font-size:8px;padding:6px 2px}
  .th-section-label{font-size:8px;padding:4px}
  /* Status bar */
  .status-bar{padding:8px 10px;flex-wrap:wrap;gap:8px}
  .status-bar>span{display:none}
  .status-bar-right{width:100%;display:grid;grid-template-columns:1fr 1fr;gap:6px}
  .btn-sm{padding:8px 10px;font-size:11px;min-height:44px;text-align:center;justify-content:center;display:flex;align-items:center}
  .btn-clear{grid-column:auto;margin-left:0}
  /* Hours page */
  .summary-row{grid-template-columns:1fr 1fr}
  .hours-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .hours-table{min-width:520px}
  .hours-table-header{padding:14px 8px}
  .hours-table thead th,.hours-table tbody td{padding:8px 8px;font-size:11px}
  /* Manage DJs tab — actions dropdown */
  .manage-djs-wrap{padding:12px 10px}
  .signoffs-wrap{padding:12px 10px}
  .tpl-wrap{padding:12px 10px}
  .manage-actions-toggle{
    display:inline-flex;align-items:center;justify-content:center;
    padding:8px 14px;min-height:44px;border:1px solid var(--border);
    background:var(--surface);color:var(--text-primary);border-radius:var(--radius-xs);
    cursor:pointer;font-family:inherit;font-size:13px;font-weight:700;
  }
  .manage-actions-toggle:hover{background:rgba(255,255,255,0.1)}
  .manage-btn-desktop{display:none}
  .manage-djs-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .manage-djs-table{min-width:auto}
  .manage-djs-table th,.manage-djs-table td{padding:8px 8px;font-size:12px}
  /* Type and Lockout hide on a phone. The Role column sits third, so they are 4 and 6. */
  .manage-djs-table th:nth-child(4),.manage-djs-table td:nth-child(4),
  .manage-djs-table th:nth-child(6),.manage-djs-table td:nth-child(6){display:none}
  .manage-add-form{flex-direction:column;align-items:stretch;gap:10px}
  .manage-add-form input,.manage-add-form select{width:100%}
  .manage-add-form button{width:100%}
  /* Manage edit modals */
  .manage-edit-modal{min-width:unset;width:calc(100vw - 32px);max-width:400px;padding:20px 16px}
  /* DJ detail modal */
  .dj-detail-stat-grid{grid-template-columns:1fr 1fr;padding:10px 16px;gap:8px}
  /* Availability grid */
  .avail-grid th{padding:3px 3px;font-size:9px}
  .avail-grid td{padding:3px 3px}
  .avail-grid input[type="checkbox"]{width:20px;height:20px}
  /* Manage DJs — venue checkboxes: shrink on mobile. iOS Safari ignores
     width/height on native checkboxes, so scale the rendered control instead. */
  #addDJPlaysArkbar,#addDJPlaysLove,#addDJPlaysHip,
  #editDJPlaysArkbar,#editDJPlaysLove,#editDJPlaysHip{transform:scale(0.8);transform-origin:center}
}

/* === PRINT === */
@media print{
  @page{size:A3 landscape;margin:6mm}
  *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important;color-adjust:exact!important}
  body{background:#fff!important;font-size:9px}
  body::before,body::after{display:none!important}
  .bg-fixed{display:none!important}
  .login-wrap,.app-header,.venue-bar,.status-bar,.hours-toggle,.hours-panel,.hours-panel-overlay,.toast,.dj-detail-actions{display:none!important}
  .print-header{display:block!important;padding:0 0 4px;margin-bottom:4px;border-bottom:2px solid #2B3390;font-size:11px}
  .main-content{padding:0!important;overflow:visible!important;flex:none}
  .app{display:block!important;min-height:unset;background:#fff!important}
  .grid-wrap{box-shadow:none!important;overflow:visible!important;border-radius:0;background:#fff!important}
  .roster-table{font-size:9px;table-layout:fixed;width:100%}
  .roster-table thead th{font-size:8px;padding:3px 2px;position:static;line-height:1.2}
  .th-arkbar{background:#eaf6f7!important;color:#2B3990!important}
  .th-hip{background:var(--orange-deep)!important;color:var(--on-orange-deep)!important}
  .th-love{background:#fce4ec!important;color:#880e4f!important}
  .th-date{background:#2B3990!important;color:#fff!important;width:44px!important;min-width:44px!important;max-width:44px!important;white-space:normal!important}
  .td-date{font-size:8px!important;line-height:1.2;padding:2px 3px;width:44px!important;min-width:44px!important;max-width:44px!important;white-space:normal!important;position:static;border-right:1px solid #ccc}
  .td-date .dow{font-size:7.5px!important;display:block}
  .td-slot{width:60px;min-width:60px;max-width:60px;padding:1px;border:.5px solid #eee}
  .td-slot.hip-slot{background:var(--orange-deep)!important}
  .td-slot.hip-slot .slot-select{color:var(--on-orange-deep)!important;font-weight:700!important}
  .slot-select{font-size:9px;padding:2px 1px;min-height:18px;line-height:1.2;border-radius:3px;border:none;width:100%}
  .slot-select.assigned{color:#fff!important;font-weight:800!important;border-radius:3px!important}
  .slot-select.unassigned,.slot-select.no-avail{display:none}
  .td-slot.empty-slot{background:#fafafa!important}
  .hours-wrap,.summary-row{display:none!important}
  .roster-table tbody tr{height:auto!important}
  .roster-table tbody tr:nth-child(even){background:#fafbff!important}
  body.love-print .roster-table{font-size:9px}
  body.love-print .roster-table thead th{font-size:8px}
  body.love-print .td-slot{width:70px;min-width:70px;max-width:70px}
  body.love-print .slot-select{font-size:9px}
  body.love-print .th-date{width:44px!important;min-width:44px!important;max-width:44px!important;white-space:normal!important}
  body.love-print .td-date{width:44px!important;min-width:44px!important;max-width:44px!important;white-space:normal!important;padding:2px 3px}
}

/* ─── Host role (docs/specs/host-role.md) ─── */
:root{--host:#8e24aa}
/* ARKbar nights are two rows: the DJ line and a slim host line. The date cell spans both. */
.roster-table tr.has-host > td.td-slot{border-bottom-color:transparent}
.roster-table tr.host-row > td{padding:0 4px 4px;border-top:0}
.roster-table tr.host-row .host-select{min-height:20px;padding:2px 3px;font-size:9px;border-radius:5px;border:1px dashed rgba(142,36,170,0.35)}
.roster-table tr.host-row .host-select.assigned{border-style:solid}
.roster-table tr.host-row td.host-none{background:transparent}
/* Stripes follow the night, not the row: two rows per night. */
.roster-table.with-hosts tbody tr:nth-child(even){background:transparent}
.roster-table.with-hosts tbody tr:nth-child(4n+3),.roster-table.with-hosts tbody tr:nth-child(4n+4){background:#fafbff}
.roster-table.with-hosts tbody tr:hover{background:#f4f5ff}
.roster-table.with-hosts tbody tr:nth-child(4n+3) .td-date{background:#fafbff}
.roster-table.with-hosts tbody tr.today-row > .td-date{background:#fffbe6}
.roster-table.with-hosts tbody tr:nth-child(4n+3).today-row > .td-date{background:#fff8d6}
/* An empty host box stays quiet until the manager points at that night. */
.roster-table tr.host-row .host-select.unassigned{color:transparent;border-color:transparent}
.roster-table tr.host-row:hover .host-select.unassigned,
.roster-table tr.has-host:hover + tr.host-row .host-select.unassigned,
.roster-table tr.host-row .host-select.unassigned:focus{color:#8a5fa0;border-color:rgba(142,36,170,0.35)}
.slot-select.saving{opacity:.55;animation:hostPulse 900ms ease-in-out infinite}
@keyframes hostPulse{50%{opacity:.3}}
@media (prefers-reduced-motion:reduce){.slot-select.saving{animation:none}}

.role-tag.host{font-size:9px;color:#d6a4e8;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.split{display:flex;flex-direction:column;align-items:flex-end;gap:2px;line-height:1.2}
.split span{white-space:nowrap}
.split i{font-style:normal;font-size:9px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;margin-right:6px;opacity:.6}
.split .h{color:#d6a4e8}
.hours-bar-fill.host-fill{background:var(--host)}
tr.host-row-hours{background:rgba(142,36,170,0.07)}
tr.totals-row.host-total td:first-child{color:#d6a4e8!important}
tr.totals-row.all-total td{border-top:2px solid rgba(255,255,255,0.25)}

.role-pill{display:inline-block;font-size:10px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:2px 8px;border-radius:10px;background:rgba(255,255,255,0.08);color:var(--text-secondary)}
.role-pill.host{background:rgba(142,36,170,0.25);color:#e3bdf0}
.role-note{font-size:12px;line-height:1.45;color:#e3bdf0;background:rgba(142,36,170,0.12);border:1px solid rgba(142,36,170,0.35);border-radius:8px;padding:8px 10px}
.role-note[hidden]{display:none}

@media(max-width:768px){
  /* Slim, but still a fair tap on a phone. */
  .roster-table tr.host-row .host-select{min-height:28px;font-size:10px}
}
