/* ─── TOKENS ─────────────────────────────────────────────────────── */
:root{
  /* Deep indigo rather than near-black. Every surface below carries a little
     blue so panels separate from the page by hue as well as by brightness --
     the old set differed by about 4% white, which on a dark screen reads as one
     flat sheet. */
  --ink:#0B1022;          /* deepest, behind everything    */
  --depth:#141C36;        /* body base                     */
  --steel:#1A2340;        /* panel base                    */
  --panel:#212C4E;        /* elevated surface              */
  --glass:rgba(255,255,255,0.055);
  --glass-hi:rgba(255,255,255,0.10);
  --line:rgba(160,180,255,0.14);
  --line-hi:rgba(160,180,255,0.26);
  --paper:#EEF1FA;
  --muted:#94A0C0;
  --dim:#5A6890;

  --gold:#F5A623;          /* amber — warmth, food          */
  --coral:#E8533C;         /* energy                        */
  --violet:#7B2FBE;        /* depth accent                  */
  --allow:#00C896;
  --deny:#E8533C;
  --dup:#F5A623;
  --teal:#17C4C4;

  /* the gradient that runs throughout */
  --grad: linear-gradient(135deg, #F5A623 0%, #E8533C 55%, #7B2FBE 100%);
  --grad-h: linear-gradient(135deg, #FFB83A 0%, #F06348 55%, #8E3FD4 100%);

  --display:'Barlow Condensed','Arial Narrow',sans-serif;
  --body:'Inter',system-ui,-apple-system,sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,monospace;

  --r-sm:8px; --r-md:12px; --r-lg:16px;
  --shadow: 0 4px 24px rgba(0,0,0,.45), 0 1px 4px rgba(0,0,0,.3);
  --shadow-lg: 0 12px 48px rgba(0,0,0,.6), 0 2px 8px rgba(0,0,0,.4);
}

/* ─── RESET ──────────────────────────────────────────────────────── */
*{box-sizing:border-box;margin:0}
html{scroll-behavior:smooth}
body{
  background: var(--depth);
  color: var(--paper);
  font-family: var(--body);
  font-size:14px;
  -webkit-font-smoothing:antialiased;
  -webkit-tap-highlight-color:transparent;
  /* ambient depth glows */
  /* The old washes were at 7% over near-black, which is invisible. At these
     strengths the page has a warm top-left and a violet bottom-right, so a long
     screen of tables is not one uniform field. */
  background-image:
    radial-gradient(ellipse 95% 55% at 12% -5%,  rgba(245,166,35,.16) 0%, transparent 62%),
    radial-gradient(ellipse 70% 70% at 88% 104%, rgba(123,47,190,.20) 0%, transparent 60%),
    radial-gradient(ellipse 60% 50% at 55% 45%,  rgba(23,196,196,.07) 0%, transparent 68%);
  background-attachment:fixed;
}
button{font-family:var(--body);cursor:pointer;border:none}

/* ─── TOPBAR ─────────────────────────────────────────────────────── */
.topbar{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:10px 20px;
  background: rgba(7,9,15,0.82);
  border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:40;
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
}
.brand{
  font-family:var(--display);font-size:22px;font-weight:700;
  letter-spacing:.07em;text-transform:uppercase;
  background: var(--grad);
  -webkit-background-clip:text; -webkit-text-fill-color:transparent;
  background-clip:text;
  white-space:nowrap;
}
/* fallback for browsers that don't support text gradient */
@supports not (-webkit-background-clip:text){
  .brand{color:var(--gold)}
}
.brand span{-webkit-text-fill-color:transparent}

/* ─── TABS ───────────────────────────────────────────────────────── */
.tabs{
  display:flex;gap:1px;
  background:rgba(255,255,255,0.05);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:3px;
}
.tabs button{
  background:none;border:0;
  color:var(--muted);
  font-size:11px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;
  padding:6px 11px;border-radius:6px;
  transition:color .15s,background .15s;
  white-space:nowrap;
}
.tabs button:hover{color:var(--paper);background:var(--glass-hi)}
.tabs button[aria-selected="true"]{
  background:var(--grad);color:#fff;
  box-shadow:0 2px 10px rgba(245,166,35,.3);
}
.tabs button[hidden]{display:none}
.spacer{flex:1}

/* ─── META / ROLE CHIPS ──────────────────────────────────────────── */
.meta{font-family:var(--mono);font-size:11px;color:var(--muted)}
.meta b{color:var(--paper);font-weight:500}
.dot{display:inline-block;width:6px;height:6px;border-radius:50%;
  background:var(--muted);margin-right:5px;vertical-align:1px}
.dot.live{background:var(--allow);box-shadow:0 0 6px rgba(0,200,150,.5)}
.dot.off{background:var(--deny)}
.role{
  font-family:var(--display);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  background:var(--grad);-webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;
  border:1px solid rgba(245,166,35,.35);padding:2px 8px;border-radius:4px;
  white-space:nowrap;
}
@supports not (-webkit-background-clip:text){.role{color:var(--gold)}}

/* ─── FORM CONTROLS ──────────────────────────────────────────────── */
select,input{
  background:rgba(255,255,255,0.04);
  color:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  padding:9px 10px;
  font-family:var(--mono);font-size:12px;
  transition:border-color .15s,box-shadow .15s;
}
select:focus,input:focus{
  outline:none;
  border-color:rgba(245,166,35,.6);
  box-shadow:0 0 0 3px rgba(245,166,35,.12);
}
select option{background:var(--panel)}

/* ─── LAYOUT ─────────────────────────────────────────────────────── */
.view{display:none;padding:24px 20px 80px;max-width:1220px;margin:0 auto}
.view.on{display:block}

/* ─── EYEBROW / TYPOGRAPHY ───────────────────────────────────────── */
.eyebrow{
  font-family:var(--display);font-size:12px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;
  background:var(--grad);-webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;
  margin:0 0 10px;display:inline-block;
}
@supports not (-webkit-background-clip:text){.eyebrow{color:var(--gold)}}
.sub{color:var(--muted);font-size:13px;line-height:1.65;margin:0 0 14px}
.hint{font-size:11.5px;color:var(--muted);margin-top:7px;line-height:1.6;opacity:.8}

/* ─── GLASS CARDS ────────────────────────────────────────────────── */
.card{
  background: linear-gradient(150deg, rgba(60,80,150,.30) 0%, rgba(30,40,80,.24) 100%);
  border:1px solid rgba(160,180,255,0.13);
  border-radius:var(--r-lg);
  padding:18px 20px;
  margin-bottom:16px;
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255,255,255,0.06);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  transition:border-color .2s, box-shadow .2s;
}
.card:hover{border-color:rgba(245,166,35,0.30);
  box-shadow:var(--shadow),inset 0 1px 0 rgba(255,255,255,0.10),0 0 26px rgba(245,166,35,0.07)}

/* ─── BUTTONS ────────────────────────────────────────────────────── */
.btn{
  background:rgba(255,255,255,0.06);
  border:1px solid var(--line-hi);
  color:var(--paper);
  font-size:12px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;
  padding:10px 16px;border-radius:var(--r-sm);
  transition:background .15s,border-color .15s,transform .1s,box-shadow .15s;
  white-space:nowrap;
}
.btn:hover{
  background:rgba(255,255,255,0.11);border-color:rgba(255,255,255,.25);
  transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,0,0,.3);
}
.btn:active{transform:translateY(0)}
.btn.primary{
  background:var(--grad);border:none;color:#fff;
  box-shadow:0 4px 16px rgba(245,166,35,.3);
  letter-spacing:.06em;
}
.btn.primary:hover{
  background:var(--grad-h);
  box-shadow:0 6px 24px rgba(245,166,35,.45);transform:translateY(-1px);
}
.btn.sm{padding:6px 11px;font-size:11px}
.btn[aria-pressed="true"]{background:var(--grad);border:none;color:#fff}
.btn:disabled{opacity:.35;cursor:not-allowed;transform:none!important;box-shadow:none!important}

/* ─── FORM LABELS ────────────────────────────────────────────────── */
label.fld{
  display:block;
  font-family:var(--display);font-size:12px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--muted);margin:0 0 6px;
}
.fldwrap{margin-bottom:16px}
.fldwrap input,.fldwrap select{width:100%}

/* ─── ROW UTILITY ────────────────────────────────────────────────── */
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}

/* ─── COUNTER SCREEN ─────────────────────────────────────────────── */
.counter-wrap{display:grid;grid-template-columns:1.35fr .85fr;gap:16px}
@media(max-width:860px){.counter-wrap{grid-template-columns:1fr}}

.verdict{
  border-radius:var(--r-lg);min-height:320px;padding:28px;
  display:flex;flex-direction:column;justify-content:center;
  background:var(--glass);
  border:1px solid var(--line);
  box-shadow:var(--shadow-lg);
  position:relative;overflow:hidden;
  transition:background .3s,border-color .3s;
}
.verdict::before{
  content:'';position:absolute;inset:0;
  background:radial-gradient(ellipse 80% 60% at 50% 0%, rgba(255,255,255,.04) 0%, transparent 60%);
  pointer-events:none;
}
.verdict.allow{
  background:linear-gradient(160deg,rgba(0,200,150,.18) 0%,rgba(0,150,100,.08) 100%);
  border-color:rgba(0,200,150,.4);
  box-shadow:0 0 40px rgba(0,200,150,.15),var(--shadow-lg);
}
.verdict.deny{
  background:linear-gradient(160deg,rgba(232,83,60,.2) 0%,rgba(180,30,20,.08) 100%);
  border-color:rgba(232,83,60,.45);
  box-shadow:0 0 40px rgba(232,83,60,.15),var(--shadow-lg);
}
.verdict.dup{
  background:linear-gradient(160deg,rgba(245,166,35,.2) 0%,rgba(200,120,10,.08) 100%);
  border-color:rgba(245,166,35,.45);
  box-shadow:0 0 40px rgba(245,166,35,.15),var(--shadow-lg);
}
.vword{
  font-family:var(--display);font-weight:700;text-transform:uppercase;
  font-size:clamp(46px,8vw,96px);line-height:.88;margin-bottom:10px;
  letter-spacing:.02em;
}
.verdict.allow .vword{color:#00E6AC}
.verdict.deny .vword{color:#FF6B50}
.verdict.dup .vword{color:#FFB82B}
.vname{font-family:var(--display);font-size:clamp(22px,3vw,34px);font-weight:600;line-height:1.05}
.vsub{font-family:var(--mono);font-size:13px;margin-top:8px;opacity:.8}
.verdict.idle .vword,.verdict.busy .vword{
  font-size:clamp(26px,3.3vw,40px);color:var(--muted);
}
.verdict.idle .vsub,.verdict.busy .vsub{color:var(--muted)}
#scanInput{
  width:100%;font-size:20px;padding:16px;
  letter-spacing:.1em;text-transform:uppercase;
  border-radius:var(--r-md);background:rgba(255,255,255,0.05);
  border:1px solid var(--line-hi);
}
#scanInput:focus{border-color:rgba(245,166,35,.6);box-shadow:0 0 0 3px rgba(245,166,35,.12)}

/* ─── TALLY ──────────────────────────────────────────────────────── */
.tally{
  display:grid;grid-template-columns:repeat(2,1fr);gap:1px;
  background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;
}
.tally div{background:var(--steel);padding:13px 14px}
.tally .k{font-family:var(--display);font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted)}
.tally .v{font-family:var(--mono);font-size:26px;font-weight:600;margin-top:3px;
  font-variant-numeric:tabular-nums}
.v.ok{color:var(--allow)} .v.bad{color:var(--deny)}

.log{
  margin-top:14px;max-height:295px;overflow:auto;
  border:1px solid var(--line);border-radius:var(--r-md);
  scrollbar-width:thin;scrollbar-color:var(--dim) transparent;
}
.log-row{
  display:flex;gap:10px;align-items:baseline;padding:8px 12px;
  border-bottom:1px solid var(--line);
  font-family:var(--mono);font-size:11.5px;
  transition:background .1s;
}
.log-row:hover{background:var(--glass)}
.log-row:last-child{border-bottom:0}
.log-row .t{color:var(--muted);min-width:46px}
.log-row .id{min-width:96px}
.log-row .nm{flex:1;font-family:var(--body);color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ─── PILLS / TAGS ───────────────────────────────────────────────── */
.pill{
  font-family:var(--display);font-size:11px;letter-spacing:.09em;text-transform:uppercase;
  padding:2px 8px;border-radius:4px;font-weight:600;
}
.pill.allow{background:rgba(0,200,150,.15);color:#00E6AC;border:1px solid rgba(0,200,150,.3)}
.pill.deny{background:rgba(232,83,60,.15);color:#FF6B50;border:1px solid rgba(232,83,60,.3)}
.pill.dup{background:rgba(245,166,35,.15);color:#FFB82B;border:1px solid rgba(245,166,35,.3)}

/* ─── TABLES ─────────────────────────────────────────────────────── */
table{width:100%;border-collapse:collapse;font-size:13px}
th{
  font-family:var(--display);font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);text-align:left;padding:10px 11px;
  border-bottom:1px solid var(--line);font-weight:600;
  background:rgba(255,255,255,0.02);
}
td{padding:10px 11px;border-bottom:1px solid var(--line);color:var(--paper)}
tbody tr:last-child td{border-bottom:0}
tbody tr:hover td{background:rgba(255,255,255,0.02)}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums;text-align:right}

/* ─── STATUS COLOURS ─────────────────────────────────────────────── */
.neg{color:#FF6B50} .pos{color:#00C896} .dim{color:var(--muted)} .warn{color:#FFB82B}
.locked{
  display:inline-block;font-family:var(--display);font-size:11px;
  letter-spacing:.12em;text-transform:uppercase;
  color:var(--allow);border:1px solid rgba(0,200,150,.35);
  padding:2px 8px;border-radius:4px;
}

/* ─── ALERTS ─────────────────────────────────────────────────────── */
.err{
  background:rgba(232,83,60,.1);border:1px solid rgba(232,83,60,.3);
  color:#FFB3A4;padding:12px 14px;border-radius:var(--r-sm);
  font-size:13px;margin-bottom:14px;line-height:1.55;
}
.ok-msg{
  background:rgba(0,200,150,.08);border:1px solid rgba(0,200,150,.25);
  color:#A0F0D0;padding:12px 14px;border-radius:var(--r-sm);
  font-size:13px;margin-bottom:14px;
}

/* ─── LOGIN ──────────────────────────────────────────────────────── */
#login{
  position:fixed;inset:0;z-index:200;display:none;
  align-items:center;justify-content:center;padding:20px;
  background: transparent;
}
/* The photograph is the background now; this stays only as the base colour
   behind it while the image decodes. */
#login::before{content:'';position:fixed;inset:0;z-index:-3;background:#07090F}
#login .box{
  width:100%;
  background: linear-gradient(145deg, rgba(12,15,28,0.85) 0%, rgba(7,9,15,0.90) 100%);
  border:1px solid rgba(245,166,35,0.15);
  border-radius:20px;padding:26px 24px 30px;
  box-shadow: 0 0 80px rgba(245,140,30,0.08), 0 0 40px rgba(123,47,190,0.06), var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,0.06);
  backdrop-filter:blur(32px);-webkit-backdrop-filter:blur(32px);
}
#login .brand{font-size:34px;margin-bottom:6px;letter-spacing:.08em}
#login .sub{margin-bottom:24px;font-size:14px}

/* ─── QR CAMERA ──────────────────────────────────────────────────── */
#camWrap{display:none;margin-top:14px;border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden}

/* ─── ATTENDANCE TILES ───────────────────────────────────────────── */
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(108px,1fr));gap:7px}
.tile{
  border:1px solid var(--line);border-radius:var(--r-sm);padding:10px 11px;
  background:var(--glass);
  transition:background .15s,border-color .15s,transform .1s,box-shadow .15s;
  cursor:default;
}
.tile:hover{background:var(--glass-hi);border-color:var(--line-hi);
  transform:translateY(-1px);box-shadow:0 4px 14px rgba(0,0,0,.3)}
.tile .n{
  font-family:var(--display);font-size:14px;font-weight:600;
  letter-spacing:.04em;text-transform:uppercase;line-height:1.15;
}
.tile .c{font-family:var(--mono);font-size:11px;margin-top:4px;opacity:.8}
.tile.good{
  background:rgba(0,200,150,.1);border-color:rgba(0,200,150,.4);
  color:#A0F0D0;box-shadow:0 0 14px rgba(0,200,150,.1);
}
.tile.late{
  background:rgba(245,166,35,.1);border-color:rgba(245,166,35,.4);
  color:#FFD98A;box-shadow:0 0 14px rgba(245,166,35,.1);
}
.tile.missing{
  background:rgba(232,83,60,.1);border-color:rgba(232,83,60,.4);
  color:#FFB3A4;box-shadow:0 0 14px rgba(232,83,60,.1);
}

/* ─── SUMBAR / KPIs ──────────────────────────────────────────────── */
.sumbar{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;margin-bottom:14px;
}
.sumcell{background:var(--glass);padding:13px 14px}
.sumcell .k{
  font-family:var(--display);font-size:12px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted);
}
.sumcell .v{font-family:var(--mono);font-size:23px;font-weight:600;margin-top:3px;
  font-variant-numeric:tabular-nums}

.kpis{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;margin-bottom:16px;
}
.kpi{background:var(--glass);padding:14px 16px}
.kpi .k{
  font-family:var(--display);font-size:12px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted);
}
.kpi .v{
  font-family:var(--mono);font-size:28px;font-weight:600;margin-top:4px;
  font-variant-numeric:tabular-nums;
}
.kpi .n{font-size:11.5px;color:var(--muted);margin-top:4px;line-height:1.45}

/* ─── ROLL CALL ──────────────────────────────────────────────────── */
.tag{
  font-family:var(--display);font-size:11px;letter-spacing:.09em;text-transform:uppercase;
  font-weight:600;padding:2px 8px;border-radius:4px;border:1px solid;white-space:nowrap;
}
.tag.hostel{color:#8FE3E3;border-color:rgba(23,196,196,.4);background:rgba(23,196,196,.1)}
.tag.opted{color:#A0F0D0;border-color:rgba(0,200,150,.4);background:rgba(0,200,150,.1)}
.tag.notopted{color:var(--muted);border-color:var(--line);background:var(--glass)}
.rcrow.absent .tag{opacity:.6}
.rcgrade{font-family:var(--mono);font-size:11px;color:var(--muted);min-width:62px}
.rcrow{
  display:flex;align-items:center;gap:10px;padding:10px 12px;
  border-bottom:1px solid var(--line);cursor:pointer;
  transition:background .1s;
}
.rcrow:last-child{border-bottom:0}
.rcrow:hover{background:rgba(255,255,255,0.03)}
.rcrow-tr:hover td{background:rgba(255,255,255,0.03)}
.rcrow.absent{background:rgba(232,83,60,.08);color:#FFB3A4}
.rcrow .mark{
  font-family:var(--display);font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  border:1px solid var(--line);border-radius:4px;padding:3px 9px;
  min-width:70px;text-align:center;background:var(--glass);
}
.rcrow.absent .mark{border-color:rgba(232,83,60,.5);color:#FF6B50;background:rgba(232,83,60,.08)}
.rcrow .nm{flex:1}
.rcrow .cd{font-family:var(--mono);font-size:11px;color:var(--muted)}

/* ─── HEADCOUNT ENTRY ────────────────────────────────────────────── */
.entry2{display:grid;grid-template-columns:1fr 1fr;gap:20px}
@media(max-width:760px){.entry2{grid-template-columns:1fr}}
.entryList{display:grid;grid-template-columns:1fr auto auto;gap:6px 12px;align-items:center}
.entryList label{font-size:13px;color:var(--paper)}
.entryList input{width:84px;text-align:right;font-size:15px;padding:7px 9px}
.entryList input.over{border-color:rgba(232,83,60,.6);color:#FF6B50}
.roll{font-family:var(--mono);font-size:11.5px;color:var(--muted);min-width:44px;text-align:right}
.roll.none{opacity:.35}
.entryHead{
  font-family:var(--display);font-size:13px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--muted);border-bottom:1px solid var(--line);
  padding-bottom:7px;margin-bottom:10px;
}
.entryTot{
  display:flex;justify-content:space-between;border-top:1px solid var(--line);
  margin-top:10px;padding-top:9px;font-family:var(--mono);font-size:14px;
}
.entryTot b{background:var(--grad);-webkit-background-clip:text;
  -webkit-text-fill-color:transparent;background-clip:text;font-size:18px}

/* ─── ANALYTICS ──────────────────────────────────────────────────── */
.legend{display:flex;gap:14px;flex-wrap:wrap;margin:9px 0 0;font-size:11.5px;color:var(--muted)}
.legend i{display:inline-block;width:10px;height:10px;border-radius:2px;
  margin-right:5px;vertical-align:-1px}

/* ─── RESPONSIVE ─────────────────────────────────────────────────── */
/* ─── RESPONSIVE / MOBILE ───────────────────────────────────────── */

/* Tabs: scrollable on mobile, never wrap */
.tabs{
  overflow-x:auto;overflow-y:hidden;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;flex-wrap:nowrap;
}
.tabs::-webkit-scrollbar{display:none}

@media(max-width:860px){
  .topbar{padding:8px 12px;gap:8px;flex-wrap:nowrap}
  .brand{font-size:17px;flex-shrink:0}
  /* push tabs to their own line below topbar */
  .tabs{order:10;width:100%;border-radius:0;border-left:none;border-right:none;
    border-top:none;background:rgba(7,9,15,0.9);padding:2px 4px}
  .topbar{flex-wrap:wrap}
  .view{padding:16px 10px 80px}
  .card{padding:12px 12px}
  /* filter rows wrap nicely */
  .row{flex-wrap:wrap;gap:8px}
  /* tables scroll horizontally */
  table{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;width:100%;white-space:nowrap}
  @media(max-width:600px){
    .resp-table thead{display:none}
    .resp-table tbody tr{display:flex;flex-direction:column;margin-bottom:12px;border:1px solid var(--line);border-radius:6px;padding:10px 12px}
    .resp-table tbody td{display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px solid var(--line);font-size:13px;white-space:normal}
    .resp-table tbody td:last-child{border-bottom:none}
    .resp-table tbody td:before{content:attr(data-label);font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-right:8px;flex-shrink:0}
    .resp-table input[type=time]{width:90px;font-size:13px}
    .resp-table{display:block;overflow-x:visible;white-space:normal}
  }
  /* entry2 stacks vertically */
  .entry2{grid-template-columns:1fr}
}

@media(max-width:680px){
  .brand{font-size:15px}
  .tabs button{font-size:10px;padding:5px 8px;letter-spacing:.05em}
  .verdict{min-height:200px;padding:16px}
  .vword{font-size:clamp(36px,11vw,60px)}
  /* headcount / kitchen cards - force horizontal scroll */
  #kitchenPanel .card{overflow-x:auto}
  /* analytics table */
  #anBody{overflow-x:auto}
  /* people results table */
  #pplResults{overflow-x:auto}
  /* roll call tiles wrap */
  #rcKindTiles{flex-wrap:wrap}
  /* QR filter panel wraps */
  #qrFilterPanel .row{flex-wrap:wrap}
  /* form rows stack */
  .fld{font-size:11px}
  select,input[type=text],input[type=date],input[type=password],input[type=email]{
    max-width:100%;font-size:14px}
  /* topbar metas hide on very small */
  .meta:not(#sessionChipWrap){display:none}
}

@media(max-width:420px){
  .tabs button{padding:4px 6px;font-size:9px;letter-spacing:.03em}
  .view{padding:12px 8px 80px}
  .card{padding:10px 10px}
  .btn{padding:7px 10px;font-size:12px}
  .btn.sm{padding:5px 8px;font-size:11px}
}

@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

/* --- Settings: sidebar + panes --- */
.stwrap{display:grid;grid-template-columns:196px 1fr;gap:18px;align-items:start}
/* min-width:0 is load-bearing. Grid items default to min-width:auto, so a wide
   table pushes the column wider than the page instead of scrolling within it. */
.stbody{min-width:0}
.stbody table{max-width:100%}
.stnav{display:flex;flex-direction:column;gap:2px;position:sticky;top:12px;
  border:1px solid var(--line);border-radius:var(--r-md);padding:6px;background:var(--panel)}
.stnav-btn{appearance:none;text-align:left;cursor:pointer;font-family:var(--body);
  font-size:12.5px;letter-spacing:.01em;color:var(--muted);background:none;border:none;
  padding:8px 10px;border-radius:4px;line-height:1.3;transition:background .12s,color .12s}
.stnav-btn:hover{background:var(--glass);color:var(--ink-1,#fff)}
.stnav-btn.on{background:var(--glass);color:#fff;font-weight:600;
  box-shadow:inset 2px 0 0 var(--teal)}
.stpane{display:none}
.stpane.on{display:block}
/* The first heading in a pane sits flush; stacked spacing is no longer needed. */
.stpane > .eyebrow:first-child{margin-top:0}
@media (max-width:820px){
  /* A wrapping grid of buttons was the problem: seven long labels made three
     ragged rows that changed height as you moved between sections. One row that
     scrolls sideways keeps the height fixed and the labels whole, and it is the
     pattern people already know from every app's tab strip.
     Applied to .stnav generally, so the roll call kinds and the attendance
     board -- both built from the same class -- get it without extra code. */
  .stwrap{grid-template-columns:1fr;gap:10px}
  .stnav{
    flex-direction:row;flex-wrap:nowrap;gap:6px;
    overflow-x:auto;overflow-y:hidden;
    -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;
    position:sticky;top:0;z-index:5;
    padding:8px;border-radius:0;border-left:none;border-right:none;
    background:rgba(12,15,28,.94);backdrop-filter:blur(12px);
    margin:0 -10px;                 /* bleed to the screen edge */
    scrollbar-width:none;
  }
  .stnav::-webkit-scrollbar{display:none}
  .stnav-btn{
    flex:0 0 auto;scroll-snap-align:start;
    text-align:center;white-space:nowrap;
    padding:8px 14px;border-radius:999px;
    background:var(--glass);border:1px solid var(--line);
  }
  .stnav-btn.on{
    background:var(--grad);color:#fff;border-color:transparent;
    box-shadow:0 2px 10px rgba(245,166,35,.28);
  }
  /* status lines under a label (due time, outstanding count) stay on one line */
  .stnav-btn > div{white-space:nowrap}

  /* Roll call pills carry a second line -- "due 13:00 - feeds Sun 30 Aug" --
     which makes them twice as tall as every other nav and turns the strip into
     a row of lozenges. Drop it here: the same cutoff and fed date are printed
     in full on the line directly beneath the strip for whichever kind is
     selected, so nothing is lost. The status dot stays. */
  #rcKindTiles .stnav-btn > div{display:none}
  #rcKindTiles .stnav-btn{padding:8px 13px;line-height:1.2}
  #rcKindTiles .stnav-btn .rcdot{margin-left:5px}
  /* the cutoff line is the replacement, so give it room to breathe */
  #rcCut{margin-top:2px;font-size:12px;line-height:1.4}
}

/* --- Extra class groups --- */
.sgcols{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:start}
@media (max-width:820px){ .sgcols{grid-template-columns:1fr} }
.sgrow{display:flex;align-items:center;gap:8px;padding:6px 8px;border-bottom:1px solid var(--line)}
.sgrow:last-child{border-bottom:none}
.sgrow .nm{flex:1;font-size:12.5px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sgrow .ct{font-size:10px;color:var(--muted);white-space:nowrap}
.sgbox{border:1px solid var(--line);border-radius:4px}
.sgoff{opacity:.5}
.sgwin{display:flex;align-items:center;gap:8px;padding:5px 8px;font-size:11.5px;
  border-top:1px dashed var(--line)}
.sglink{background:none;border:none;cursor:pointer;font-size:11px;padding:2px 4px;
  color:var(--muted);font-family:var(--body)}
.sglink:hover{color:#fff;text-decoration:underline}
.sglink.neg{color:var(--neg)}

/* --- Print QR pane --- */
.qrgrid,.fgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:12px 14px}
.qrfield,.ffield{min-width:0;display:flex;flex-direction:column;gap:4px}
.qrfield select,.ffield select{width:100%}
.qrfield .fld,.ffield .fld{margin:0}
/* contents makes the group's children members of the parent grid, so Branch and
   the student-only filters line up as one set while still toggling together */
#pplStuFilters{display:contents}
.srchbar{display:flex;gap:8px;align-items:center;flex-wrap:nowrap}
@media(max-width:600px){
  .srchbar{flex-wrap:wrap}
  .srchbar #pplQ{flex:1 1 100%}
  .srchbar .btn{flex:1 1 auto}
}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:6px;overflow:hidden;
  background:var(--glass);width:fit-content;max-width:100%;flex-wrap:wrap}
.seg button{appearance:none;background:none;border:none;cursor:pointer;
  font-family:var(--body);font-size:12.5px;color:var(--muted);padding:7px 16px;
  border-right:1px solid var(--line);transition:background .12s,color .12s}
.seg button:last-child{border-right:none}
.seg button:hover{color:#fff}
.seg button[aria-pressed="true"]{background:var(--grad);color:#fff;font-weight:600}
.qrbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:16px;
  padding-top:14px;border-top:1px solid var(--line)}
@media(max-width:600px){
  .qrbar .btn{flex:1 1 auto}
  .seg{width:100%}
  .seg button{flex:1 1 auto;padding:7px 8px}
}

/* --- result tables --- */
/* min-width:0 lets the wrapper scroll instead of stretching its grid column. */
.tblwrap{min-width:0;overflow-x:auto;border:1px solid var(--line);border-radius:4px}
/* Result tables stay tables on every screen and scroll sideways. The card
   layout that .resp-table gives is right for a 3-column settings grid and wrong
   for a 15-column record list, where one student becomes a full screen of
   labels and scanning down the list stops being possible. */
.tblwrap{-webkit-overflow-scrolling:touch}
@media(max-width:820px){
  /* The global mobile rule turns every table into a scrolling block. Inside a
     wrapper that already scrolls that gives two scrollbars fighting each other,
     so the inner table goes back to being a table and the wrapper does the
     scrolling. min-width keeps it filling the pane when the columns are narrow. */
  .tblwrap table{display:table;overflow-x:visible;width:auto;min-width:100%}
}

/* --- Roll call: kinds as nav entries --- */
/* loadKindTileColors still sets good/late/missing; in the nav that reads as a
   status dot rather than a filled tile, so the selected item stays legible. */
.stnav-btn .rcdot{display:inline-block;width:7px;height:7px;border-radius:50%;
  background:var(--line);margin-left:6px;vertical-align:middle}
.stnav-btn.good .rcdot{background:#00C896}
.stnav-btn.late .rcdot{background:#F5A623}
.stnav-btn.missing .rcdot{background:#E8533C}
.stnav-btn[disabled]{pointer-events:none}

/* --- Login backdrop --- */
/* ============ LOGIN =============================================
   Wide: the artwork carries the lockup and the copy on its left, so the
   only thing added is the card on the right.
   Phone: the artwork is behind everything, so the lockup, the card and the
   copy are stacked as three sections. */
#login{
  position:fixed;inset:0;z-index:200;display:none;
  align-items:center;justify-content:flex-end;
  padding:24px clamp(20px,5vw,80px);
  background:#0B1220;
}
#login::before{content:none}

.loginbg{
  position:absolute;inset:0;z-index:0;
  background-image:url("../backdrop.webp");
  background-size:cover;background-position:center;background-repeat:no-repeat;
}
.loginveil{display:none}
.logintag{display:none}

/* Both hidden on a wide screen: the artwork already says them. */
.loginlogo{display:none}
.logincopy{display:none}

.loginpanel{position:relative;z-index:2;width:100%;max-width:370px}

#login .box{
  background:#fff;border:none;border-radius:16px;
  padding:30px 28px;font-size:14px;display:block;
  box-shadow:0 20px 50px rgba(6,12,24,.30);
  backdrop-filter:none;-webkit-backdrop-filter:none;
}
#login .box .loginmark{
  display:block;width:62%;max-width:190px;height:auto;margin:0 auto 20px;
}
#login .loginhd{
  background:none;font-family:var(--display);font-weight:700;
  font-size:19px;line-height:1.3;color:#14203A;
  margin:0 0 18px;letter-spacing:0;text-transform:none;
}
#login .box .fldwrap{margin-bottom:14px}
#login .box .fld{
  display:block;font-family:var(--body);font-size:13px;font-weight:500;
  color:#41506E;margin-bottom:6px;letter-spacing:0;text-transform:none;
}
#login .box input{
  width:100%;font-family:var(--body);font-size:14px;padding:11px 12px;
  background:#fff;border:1px solid #CFD9E8;border-radius:8px;color:#14203A;
}
#login .box input:focus{
  border-color:#2B7FE0;outline:none;box-shadow:0 0 0 3px rgba(43,127,224,.16);
}
#login .box .btn{
  display:block;width:100%;margin-top:6px;padding:13px;
  font-family:var(--body);font-size:14px;font-weight:600;
  letter-spacing:0;text-transform:none;
  background:#1B72E8;border:none;border-radius:8px;color:#fff;box-shadow:none;
}
#login .box .btn:hover{background:#1461CE}
#login #loginMsg:empty{display:none}
#login #loginMsg{font-size:13px;margin-bottom:10px}
#login #cfgToggle{color:#2B7FE0}

/* ---- Phone: lockup, card, copy ---- */
@media(max-width:860px){
  #login{
    display:none;
    flex-direction:column;align-items:center;justify-content:center;
    gap:22px;padding:28px 20px;
    /* The stack can be taller than the screen on a small phone, so it
       scrolls rather than being cut off at the bottom. */
    overflow-y:auto;
  }
  /* Behind everything and well back, so white text reads over it. */
  .loginbg{position:fixed;opacity:.30}
  .loginlogo{
    display:block;width:62%;max-width:230px;height:auto;
    flex:0 0 auto;
  }
  .loginpanel{max-width:390px;flex:0 0 auto}
  #login .box{padding:24px 22px}
  /* The card carries the lockup on a wide screen; here the one above it
     does that job, so showing both would be the logo twice. */
  #login .box .loginmark{display:none}
  .logincopy{
    display:block;text-align:center;flex:0 0 auto;max-width:390px;
  }
  .lc-head{
    font-family:var(--display);font-weight:700;font-size:18px;
    line-height:1.3;color:#fff;margin:0 0 8px;
  }
  .lc-head span{color:#3FC6F0}
  .lc-sub{
    font-size:12.5px;line-height:1.6;color:rgba(237,242,247,.80);margin:0;
  }
}
@media(max-width:380px){
  .loginlogo{width:56%}
  .lc-head{font-size:16px}
  .lc-sub{font-size:11.5px}
}

/* --- Counter on a phone, camera live --- */
/* The verdict is the only thing that matters at the moment of a scan, so while
   the camera is open it stays pinned at the top and the preview is capped so
   both fit on one screen without scrolling. */
@media(max-width:860px){
  #view-counter.cam-on #verdict{
    position:sticky;top:6px;z-index:6;
    min-height:0;padding:12px 14px;
    box-shadow:0 10px 24px rgba(0,0,0,.55);
  }
  #view-counter.cam-on .vword{font-size:clamp(26px,7.5vw,40px);line-height:1.05}
  #view-counter.cam-on .vname{font-size:15px}
  #view-counter.cam-on .vsub{font-size:12px}
  /* the scanner-focused helper text is noise once the camera is the input */
  #view-counter.cam-on #scanInput,
  #view-counter.cam-on label[for="scanInput"],
  #view-counter.cam-on #scanInput + .hint{display:none}
  #camWrap{max-height:52vh;overflow:hidden}
  #camWrap #reader video{width:100% !important;height:auto !important}
}

/* --- Top bar on a phone --- */
@media(max-width:860px){
  /* The chip says LUNCH on its own; the word SESSION in front of it was the
     difference between Sign out fitting on the row and wrapping to a new one. */
  .sesslabel{display:none}
  #sessionChipWrap{gap:0}
}

/* --- Roll call roster on a phone --- */
/* Six items on one line -- mark, name, plan, food pref, class, code -- fits a
   desktop and crushes a phone. Split into two: the name and the Present/Absent
   control on top, everything descriptive underneath. Ordering is done in CSS so
   the markup, and every handler bound to it, is untouched. */
@media(max-width:600px){
  .rcrow{flex-wrap:wrap;gap:6px 8px;padding:11px 12px;align-items:center}
  .rcrow .nm{order:1;flex:1 1 auto;min-width:0;font-size:14px;overflow-wrap:anywhere}
  .rcrow .mark{order:2;flex:0 0 auto;min-width:74px;padding:5px 9px}
  /* zero-height spacer that forces everything after it onto a second line */
  .rcrow::after{content:'';order:3;flex-basis:100%;height:0}
  .rcrow .tag{order:4}
  .rcrow .rcgrade{order:5;min-width:0}
  .rcrow .cd{order:6}
  /* the food pref toggle is the other thing being tapped, so it gets the
     opposite corner from the mark rather than being buried mid-row */
  .rcrow .fpbtn{order:7;margin-left:auto;padding:4px 10px !important;font-size:11px !important}
}

/* --- Roll call header --- */
/* Date and branch live on the title row rather than in a card of their own.
   On a phone that reclaims a whole block of vertical space, so the roll call
   tiles and the roster start higher up the screen. */
.rchead{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}
.rchead #rcDate{width:150px;font-size:12px;padding:6px 8px}
.rchead #rcBranch{display:none;font-size:12px;padding:6px 8px;min-width:120px}
@media(max-width:600px){
  .rchead{gap:6px}
  .rchead .eyebrow{flex:0 0 auto}
  .rchead #rcDate{flex:1 1 130px;width:auto}
  .rchead #rcBranch{flex:1 1 110px;min-width:0}
  #view-rollcall .sub{margin:2px 0 8px;font-size:12px}
}

/* --- Date inputs --- */
/* The browser draws its calendar glyph in near-black, which is invisible on a
   dark field. Inverting it is the only hook the shadow DOM exposes. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator{
  filter:invert(85%) sepia(12%) saturate(500%) hue-rotate(2deg);
  opacity:.85;cursor:pointer;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="month"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover{opacity:1}

/* --- Calendar grid --- */
/* Flat month grid: hairline rules instead of a box per cell, the date number
   reading first, and the day type as a quiet caption under it. Colour is
   carried by a left edge rather than coloured text, so a full month reads as a
   pattern rather than a wall of words. */
.cal-grid{
  display:grid;grid-template-columns:repeat(7,1fr);
  gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;
}
.cal-hd{
  background:var(--panel);text-align:center;
  font-family:var(--display);font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);font-weight:600;padding:8px 0;
}
.cal-pad{background:rgba(255,255,255,.012)}
.cal-day{
  background:var(--depth);cursor:pointer;position:relative;
  min-height:74px;padding:6px 8px 8px;
  border-left:3px solid transparent;
  transition:background .12s,border-color .12s;
}
.cal-day:hover{background:rgba(255,255,255,.05)}
.cal-top{display:flex;justify-content:space-between;align-items:center;height:22px}
.cal-num{
  font-family:var(--body);font-size:13px;font-weight:500;color:#fff;
  width:22px;height:22px;line-height:22px;text-align:center;border-radius:50%;
}
/* today gets the filled disc, the way every calendar marks it */
.cal-day.today .cal-num{background:var(--grad);color:#fff;font-weight:700}
.cal-dot{
  width:6px;height:6px;border-radius:50%;background:var(--teal);
  box-shadow:0 0 6px rgba(23,196,196,.8);
}
.cal-type{font-size:10px;line-height:1.3;color:var(--muted);margin-top:2px}
.cal-note{font-size:9px;color:var(--muted);opacity:.75;line-height:1.3}
.cal-label{color:#F5A623;opacity:.9;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* day type reads from the left edge and the caption colour */
.cal-day.t-WORKING  .cal-type{color:var(--muted)}
.cal-day.t-HALF_DAY {border-left-color:#F5A623}
.cal-day.t-HALF_DAY .cal-type{color:#F5A623}
.cal-day.t-SUNDAY   {border-left-color:rgba(255,255,255,.22)}
.cal-day.t-HOLIDAY  {border-left-color:#E8533C;background:rgba(232,83,60,.06)}
.cal-day.t-HOLIDAY .cal-type{color:#E8533C}
/* selection is a ring inside the cell, so it never shifts the grid */
.cal-day.sel{background:rgba(23,196,196,.12)}
.cal-day.sel:after{
  content:'';position:absolute;inset:0;pointer-events:none;
  box-shadow:inset 0 0 0 2px var(--teal);
}
@media(max-width:600px){
  .cal-day{min-height:58px;padding:4px 5px 6px;border-left-width:2px}
  .cal-num{font-size:12px;width:20px;height:20px;line-height:20px}
  .cal-top{height:20px}
  .cal-type{font-size:9px}
  .cal-note{display:none}          /* labels return on a wider screen */
  .cal-hd{font-size:9px;padding:6px 0;letter-spacing:.06em}
}

/* --- Login on a phone --- */
/* The backdrop carries the logo and the strapline, but on a narrow screen both
   sit behind the card. So they are repeated as real elements -- mark above the
   fields, words below -- and the photograph drops back to a texture. */
/* The old login block lived here. It set .loginmark and .logintag again
   at the foot of the file, after the rules above, so the tagline came back
   on a phone and appeared twice. Removed rather than overridden: two sets
   of rules for one screen is how this went wrong three times. */

/* --- Icon-only buttons --- */
.iconbtn{min-width:34px;padding:6px 9px;font-size:15px;line-height:1}

/* --- Compact filter bars --- */
/* One row of controls with no labels above them: a date field, a branch picker
   and a refresh icon explain themselves, and the labels were costing a whole
   line of height on a phone for no information. */
.filterbar{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:8px 10px;margin-bottom:10px;
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--panel);
}
.filterbar input[type="date"]{width:150px;font-size:12px;padding:6px 8px}
.filterbar select{font-size:12px;padding:6px 8px;min-width:130px}
.filterbar .fbsep{font-size:12px;color:var(--muted)}
.filterbar #abDayType,.filterbar #exDayType{font-size:12px;white-space:nowrap}
@media(max-width:600px){
  .filterbar{gap:6px;padding:7px 8px}
  /* the date shares its row with the day type, so both are visible at once */
  .filterbar input[type="date"]{flex:1 1 128px;width:auto;min-width:0;font-size:12px}
  .filterbar select{flex:1 1 110px;min-width:0}
  .filterbar .spacer{display:none}
  .filterbar #abDayType,.filterbar #exDayType{flex:0 0 auto;font-size:11px}
  .filterbar .btn{flex:0 0 auto}
  /* Everything on one line. nowrap stops any item escaping to a second row;
     the two that can absorb the squeeze -- branch and day type -- are allowed
     to shrink and ellipsise, while the date and the icon hold their size. */
  #view-board .filterbar{flex-wrap:nowrap;gap:5px}
  #view-board .filterbar #abDate{flex:0 0 116px;min-width:0;padding:6px 4px}
  #view-board .filterbar #abBranch{flex:1 1 60px;min-width:0;padding:6px 4px}
  #view-board .filterbar #abRefresh{flex:0 0 auto}
  #view-board .filterbar #abDayType{
    flex:0 1 auto;min-width:0;font-size:10.5px;text-align:right;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  }
}

/* --- Timings --- */
.tmrow{
  display:flex;align-items:center;gap:12px;
  padding:10px 12px;border-bottom:1px solid var(--line);
}
.tmrow:last-child{border-bottom:0}
.tmname{flex:1;min-width:0;font-size:13px}
.tmname .hint{margin-top:2px;font-size:11px}
.tmtime input[type="time"]{width:104px;font-size:13px;padding:6px 8px}
.tmfixed{font-family:var(--mono);font-size:13px}
@media(max-width:600px){
  .tmrow{padding:9px 10px;gap:8px}
  .tmname{font-size:12.5px}
  .tmname .hint{font-size:10px;line-height:1.35}
  .tmtime input[type="time"]{width:92px;font-size:12px;padding:5px 6px}
}

/* --- Settings panes on a phone --- */
@media(max-width:600px){
  /* Branch / counter / block pickers and their action buttons were laid out as
     a single flex row that overflowed. Let every control take a full line and
     the buttons share one. */
  #stpane-branches .row > div,
  #stpane-branches .row > select,
  #stpane-branches .row > input{flex:1 1 100%;min-width:0}
  #stpane-branches select,
  #stpane-branches input[type="text"]{width:100%}
  #stpane-branches .row{gap:8px}
  #stpane-branches .row > .btn{flex:1 1 auto}
  #stpane-branches table{font-size:12px}
  #stpane-assign .ffield select{width:100%}
  /* the roles matrix is genuinely wide; let it scroll rather than squash */
  #rpGrid{overflow-x:auto}
  #rolesPanel{padding:12px}
  #rolesPanel .row .btn{flex:1 1 auto}
  .srchbar #usrQ{flex:1 1 100%}
  .srchbar #usrSearch{flex:1 1 auto}
}

/* --- Counter action buttons --- */
/* Full-width word buttons ate the row on a phone. On mobile they shrink to an
   icon with a one-word caption under it -- a caption rather than a tooltip,
   because there is no hover on a touch screen and an unlabelled glyph would
   leave the operator guessing. */
.cbtn .bico{display:none}
.cbtn .bmini{display:none}
.cbtn .bico svg{width:19px;height:19px;display:block}
@media(max-width:600px){
  .cbtns{gap:6px;flex-wrap:nowrap}
  .cbtn{
    flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:3px;
    min-width:56px;padding:7px 6px;line-height:1;
  }
  .cbtn .bico{display:block}
  .cbtn .btxt{display:none}
  .cbtn .bmini{
    display:block;font-size:8.5px;letter-spacing:.06em;text-transform:uppercase;
    font-family:var(--display);opacity:.85;
  }
  .cbtn.live{border-color:var(--neg);color:var(--neg)}
  /* the code field takes whatever is left */
  #scanInput{font-size:15px;padding:9px 10px}
}

/* --- Counter scan line --- */
/* Desktop keeps the field on its own line with the buttons beneath. On a phone
   they share one row: the field takes what is left after three 54px tiles,
   which is enough for an enrolment code and saves a whole block of height. */
.scanline > .cbtns{margin-top:11px}
@media(max-width:600px){
  .scanline{display:flex;align-items:stretch;gap:6px}
  .scanline #scanInput{flex:1 1 auto;min-width:0;font-size:15px;padding:9px 10px}
  .scanline > .cbtns{margin-top:0;flex:0 0 auto;align-items:stretch}
  .cbtn{min-width:54px;justify-content:center}
  /* the scanner hint belongs to the field, so it sits under the whole line */
  .scanline + .hint{margin-top:6px}
}

/* --- Kitchen filter bar on a phone --- */
/* Today / Tomorrow take the first line, then the date and the refresh icon
   share the second. The date field was 148px wide and pushed the icon over on
   its own; letting it flex to whatever is left keeps them together. */
@media(max-width:600px){
  .kcbar{gap:6px}
  .kcbar .btn.sm:not(.iconbtn){flex:1 1 auto;min-width:0}
  .kcbar input[type="date"]{flex:1 1 120px;min-width:0;width:auto}
  .kcbar .iconbtn{flex:0 0 auto}
}

/* --- Reports --- */
/* Every size and colour was set inline, so the panes drifted apart as they were
   built. One set of classes now drives all of them. */
.extiles{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:10px}
.extile{
  cursor:pointer;padding:10px 12px;border-radius:var(--r-md);
  border:1px solid var(--line);background:var(--panel);
  transition:border-color .12s,background .12s;
}
.extile:hover{background:var(--glass)}
.extile .n{font-size:12px;font-weight:600;line-height:1.25}
.extile .c{font-size:10.5px;color:var(--muted);margin-top:3px}
.extile.t0 .n{color:var(--teal)}
.extile.t1 .n{color:var(--dup)}
.extile.t2 .n{color:var(--coral)}
.extile.sel{background:var(--glass)}
.extile.t0.sel{border-color:var(--teal)}
.extile.t1.sel{border-color:var(--dup)}
.extile.t2.sel{border-color:var(--coral)}
.extablewrap{padding:0;margin-bottom:12px;overflow-x:auto}
.extablewrap table{min-width:100%;white-space:nowrap}
.exdethead{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.exdethead #exDetailTitle{font-weight:600;font-size:14px;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.exdetseg{margin-bottom:10px}
@media(max-width:600px){
  /* three tiles side by side become unreadable slivers; one per row reads
     cleanly and the tap targets get bigger, not smaller */
  .extiles{grid-template-columns:1fr;gap:6px}
  .extile{display:flex;align-items:baseline;gap:8px;padding:9px 11px}
  .extile .n{font-size:12.5px}
  .extile .c{margin-top:0;margin-left:auto;white-space:nowrap}
  .extablewrap table{font-size:12px}
  .extablewrap th,.extablewrap td{padding:7px 8px !important}
  .exdethead #exDetailTitle{font-size:13px}
  .exdetseg{width:100%}
  .exdetseg button{flex:1 1 auto}
  /* collections and coupon tables scroll rather than shrink to nothing */
  #clBody table,#ocBody table,#cpnBody table{font-size:12px;white-space:nowrap}
  #clBody,#ocBody,#cpnBody{overflow-x:auto}
  #clSummary .sumbar{grid-template-columns:1fr 1fr}
  #cpnOutstanding .btn{flex:1 1 auto;text-align:left}
}

/* --- Top bar brand --- */
/* Desktop keeps the emblem alone: the tabs sit on the same row and a wordmark
   would crowd them. On a phone the tabs drop to their own line, so the full
   lockup fits and the app is properly named. */
.brandlock{display:none}
@media(max-width:860px){
  .brandmark{display:none !important}
  .brandlock{display:block;height:26px;width:auto;max-width:44vw;object-fit:contain}
}
@media(max-width:380px){
  .brandlock{height:22px;max-width:40vw}
}

/* --- Filter bar extras --- */
.fbchk{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);
  white-space:nowrap}
.fbchk input{width:auto;margin:0}
@media(max-width:600px){
  /* two dates and a separator take the first line, the rest follow, and every
     control is the same height so nothing looks pasted on */
  .filterbar input[type="date"]{flex:1 1 40%;font-size:12px}
  .filterbar .fbsep{flex:0 0 auto}
  .filterbar select{flex:1 1 100%}
  .filterbar .fbchk{flex:1 1 auto}
  .filterbar .btn.sm{flex:0 0 auto;padding:7px 12px}
}

/* --- Attendance board on a phone --- */
/* The board was built for a wide screen: a 23px display heading, a four-cell
   summary bar and unit tiles at 108px minimum. On a 360px screen that gives a
   heading that wraps to three lines and tiles two-across with clipped labels.
   Everything scales down together so the proportions still hold. */
@media(max-width:600px){
  #abBody .card{padding:12px 12px}
  /* heading and the due line stack instead of fighting for one row */
  #abBody .card > .row:first-child{flex-direction:column;align-items:flex-start;gap:4px}
  #abBody .card > .row:first-child > div:first-child{font-size:16px !important;line-height:1.2}
  #abBody .card .meta{font-size:11px;line-height:1.4}
  /* one row of four small cells: the four numbers are read together, so
     splitting them over two rows made the comparison harder, not easier */
  #abBody .sumbar{grid-template-columns:repeat(4,1fr);gap:5px}
  #abBody .sumcell{padding:7px 5px;text-align:center;min-width:0}
  #abBody .sumcell .k{font-size:8.5px;letter-spacing:.04em;line-height:1.2;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #abBody .sumcell .v{font-size:17px;line-height:1.15;margin-top:2px}
  #abBody .sumcell .hint{font-size:8.5px;line-height:1.2;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* unit tiles: smaller minimum so three fit, and no hover lift on touch */
  #abBody .tiles{grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:6px}
  #abBody .tile{padding:7px 8px}
  #abBody .tile:hover{transform:none;box-shadow:none}
  #abBody .tile .n{font-size:11.5px;letter-spacing:.02em;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #abBody .tile .c{font-size:9.5px;margin-top:2px}
}
@media(max-width:380px){
  #abBody .tiles{grid-template-columns:repeat(auto-fill,minmax(74px,1fr))}
  #abBody .sumbar{gap:4px}
  #abBody .sumcell{padding:6px 3px}
  #abBody .sumcell .v{font-size:15px}
  #abBody .sumcell .k{font-size:8px}
}

/* --- Top bar chips on a phone --- */
/* Sign out was bare text beside a gradient role chip and a bordered session
   chip, so it read as a stray link rather than a control. Everything on that
   row now shares one pill shape and one height. */
@media(max-width:860px){
  .topbar #btnLogout{
    font-family:var(--display);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
    padding:4px 10px;border-radius:999px;
    border:1px solid rgba(232,83,60,.4);color:#FF8A73;background:rgba(232,83,60,.10);
  }
  .topbar #btnLogout:hover{background:rgba(232,83,60,.18)}
  .topbar .role{font-size:10px;padding:3px 9px;border-radius:999px}
  .topbar #sessionChip{border-radius:999px;padding:3px 9px;font-size:10px}
  .topbar .meta{font-size:10px}

  /* Main menu: 11px uppercase inside a hairline strip read as a caption, not
     navigation. Bigger type, real padding, and a scrolling row of pills so the
     current tab is unmistakable. */
  .tabs{
    gap:5px;padding:6px 8px;background:rgba(7,9,15,.92);
    overflow-x:auto;scrollbar-width:none;scroll-snap-type:x proximity;
  }
  .tabs::-webkit-scrollbar{display:none}
  .tabs button{
    flex:0 0 auto;scroll-snap-align:start;
    font-size:12px;letter-spacing:.05em;padding:8px 14px;border-radius:999px;
    border:1px solid var(--line);background:var(--glass);color:var(--muted);
  }
  .tabs button[aria-selected="true"]{
    border-color:transparent;color:#fff;
    box-shadow:0 2px 12px rgba(245,166,35,.32);
  }
}

/* --- Lookup scan line --- */
@media(max-width:600px){
  .lkline{flex-wrap:nowrap;align-items:stretch;gap:6px}
  .lkline #lkCode{flex:1 1 auto;min-width:0;font-size:15px;padding:9px 10px}
  .lkline .btn{flex:0 0 auto}
  .lkline #lkGo{padding:9px 13px;font-size:12px}
}

/* --- Colour on the inner pages --- */
/* The chrome was already colourful; the content area was not. These give the
   working surfaces some of the same warmth without touching legibility. */
.topbar{
  background:linear-gradient(180deg, rgba(33,44,78,.96) 0%, rgba(20,28,54,.94) 100%);
  border-bottom:1px solid rgba(160,180,255,.16);
}
/* a hairline of the brand gradient under the bar ties the page to the logo */
.topbar:after{
  content:'';position:absolute;left:0;right:0;bottom:-1px;height:2px;
  background:var(--grad);opacity:.55;
}
.topbar{position:relative}
/* tables: banded rows so a long list is readable without squinting */
tbody tr:nth-child(even) td{background:rgba(160,180,255,.035)}
tbody tr:hover td{background:rgba(245,166,35,.07)}
thead th{
  background:rgba(160,180,255,.07);
  border-bottom:1px solid rgba(160,180,255,.18);
}
/* section headings get a small gradient rule under them */
.eyebrow{position:relative;padding-bottom:5px}
.eyebrow:after{
  content:'';position:absolute;left:0;bottom:0;width:34px;height:2px;
  background:var(--grad);border-radius:2px;opacity:.85;
}
/* nav strips and filter bars sit on a tinted surface, not a black one */
.stnav,.filterbar{
  background:linear-gradient(160deg, rgba(60,80,150,.26) 0%, rgba(30,40,80,.20) 100%);
}
.tile{background:linear-gradient(150deg, rgba(60,80,150,.22) 0%, rgba(30,40,80,.16) 100%)}
/* inputs read as wells cut into the surface rather than black holes */
input,select,textarea{
  background:rgba(11,16,34,.55);
  border:1px solid rgba(160,180,255,.16);
}
input:focus,select:focus,textarea:focus{
  border-color:rgba(245,166,35,.55);
  box-shadow:0 0 0 3px rgba(245,166,35,.12);
}
@media(max-width:860px){
  .tabs{background:linear-gradient(180deg, rgba(26,35,64,.96), rgba(20,28,54,.96))}
}

/* ============================================================
   LIGHT THEME
   Applied by setting data-theme="light" on <html>. Everything below
   overrides the dark defaults; nothing here changes layout, so the two
   themes stay in step as the app grows.

   Not white: a warm paper base (#F7F5F1) with cool white cards on top.
   Pure white on a bright kitchen tablet is as tiring as pure black in a
   dark hall, and the warm tint keeps the amber brand colour from looking
   dirty against it.
   ============================================================ */
html[data-theme="light"]{
  --ink:#FFFFFF;
  --depth:#FBF6EE;        /* warm cream, the page itself     */
  --steel:#FFFDF9;
  --panel:#FFFDF9;        /* cards: warm white, not flat     */
  --glass:rgba(180,120,30,0.055);
  --glass-hi:rgba(180,120,30,0.11);
  --line:rgba(150,110,60,0.20);
  --line-hi:rgba(150,110,60,0.34);
  --paper:#3A2F1E;        /* warm brown-ink, not blue-black  */
  --muted:#7C6A52;        /* warm secondary, not grey        */
  --dim:#B4A48C;

  --gold:#C77A0A;         /* darkened so it passes on white  */
  --coral:#C63A24;
  --violet:#6D28B4;
  --allow:#00845F;
  --deny:#C63A24;
  --dup:#B37209;
  --teal:#0E8C8C;

  --grad: linear-gradient(135deg, #E08A0C 0%, #D2452C 55%, #7B2FBE 100%);
  --grad-h: linear-gradient(135deg, #F09A18 0%, #E2543A 55%, #8E3FD4 100%);

  --shadow: 0 1px 3px rgba(25,35,70,.10), 0 6px 18px rgba(25,35,70,.07);
  --shadow-lg: 0 10px 34px rgba(25,35,70,.14), 0 2px 8px rgba(25,35,70,.08);
}
html[data-theme="light"] body{
  background:var(--depth);color:var(--paper);
  /* Three soft washes -- apricot, rose, mint -- so the page reads as colour
     rather than as white with grey furniture on it. */
  background-image:
    radial-gradient(ellipse 85% 55% at 8% -8%,   rgba(255,176,60,.30) 0%, transparent 60%),
    radial-gradient(ellipse 75% 60% at 96% 8%,   rgba(255,138,160,.20) 0%, transparent 58%),
    radial-gradient(ellipse 90% 70% at 50% 108%, rgba(90,205,175,.22) 0%, transparent 62%);
}
html[data-theme="light"] .card{
  background:linear-gradient(160deg,#FFFDF9 0%,#FFF8ED 100%);
  border:1px solid var(--line);
  box-shadow:var(--shadow);backdrop-filter:none;-webkit-backdrop-filter:none;
}
html[data-theme="light"] .card:hover{
  border-color:rgba(199,122,10,.35);box-shadow:var(--shadow-lg);
}
html[data-theme="light"] .topbar{
  background:linear-gradient(180deg,#FFF6E6 0%,#FFEFD8 100%);
  border-bottom:1px solid var(--line);
}
html[data-theme="light"] .topbar:after{opacity:.9}
html[data-theme="light"] .stnav,
html[data-theme="light"] .filterbar{
  background:linear-gradient(160deg,#FFF7EA 0%,#FFF1DC 100%);border-color:var(--line);
}
html[data-theme="light"] .tile{
  background:linear-gradient(160deg,#FFFDF9 0%,#FFF4E3 100%);border-color:var(--line);
}
html[data-theme="light"] .stnav-btn{color:var(--muted)}
html[data-theme="light"] .stnav-btn:hover{background:var(--glass);color:var(--paper)}
html[data-theme="light"] .stnav-btn.on{background:var(--glass);color:#12193A}
html[data-theme="light"] thead th{
  background:linear-gradient(180deg,#FFEBCB 0%,#FFE3B8 100%);
  border-bottom:1px solid var(--line-hi);color:#7A5A22;
}
html[data-theme="light"] tbody tr:nth-child(even) td{background:rgba(255,176,60,.07)}
html[data-theme="light"] tbody tr:hover td{background:rgba(90,205,175,.16)}
html[data-theme="light"] td{border-bottom:1px solid var(--line)}
html[data-theme="light"] input,
html[data-theme="light"] select,
html[data-theme="light"] textarea{
  background:#FFFFFF;border:1px solid var(--line);color:var(--paper);
}
html[data-theme="light"] input:focus,
html[data-theme="light"] select:focus,
html[data-theme="light"] textarea:focus{
  border-color:rgba(199,122,10,.65);box-shadow:0 0 0 3px rgba(199,122,10,.15);
}
html[data-theme="light"] input::placeholder{color:var(--dim)}
/* the browser's date glyph is dark, which is correct here */
html[data-theme="light"] input[type="date"]::-webkit-calendar-picker-indicator,
html[data-theme="light"] input[type="month"]::-webkit-calendar-picker-indicator,
html[data-theme="light"] input[type="time"]::-webkit-calendar-picker-indicator{filter:none}
html[data-theme="light"] .btn{
  background:linear-gradient(180deg,#FFFDF9 0%,#FFF3E1 100%);
  border:1px solid var(--line);color:var(--paper);
}
html[data-theme="light"] .btn:hover{
  background:linear-gradient(180deg,#FFF6E8 0%,#FFEBCF 100%);border-color:var(--line-hi);
}
/* html[data-theme="light"] .btn is more specific than .btn.primary, so the
   cream background above was overriding the gradient and leaving white text on
   a cream button -- invisible. This puts the gradient back with enough
   specificity to hold, for every primary state. */
html[data-theme="light"] .btn.primary,
html[data-theme="light"] .btn.primary:hover,
html[data-theme="light"] button.btn.primary{
  background:var(--grad);color:#fff;border:none;
  box-shadow:0 3px 12px rgba(199,122,10,.32);
}
html[data-theme="light"] .btn.primary:hover{background:var(--grad-h)}
html[data-theme="light"] .btn.primary:disabled{opacity:.55}
/* the same trap catches any other coloured button variants */
html[data-theme="light"] .btn.danger{background:var(--deny);color:#fff;border:none}

/* The logo is gold and white artwork drawn for a dark ground: on cream the
   white wordmark disappears. Give it back the dark ground it was drawn on,
   as a small plate rather than recolouring the mark. */
html[data-theme="light"] .brandmark,
html[data-theme="light"] .brandlock{
  background:linear-gradient(160deg,#141C36 0%,#0B1022 100%);
  border-radius:8px;padding:4px 8px;box-sizing:content-box;
  box-shadow:0 1px 4px rgba(60,40,10,.22);
}
html[data-theme="light"] .brandmark{padding:4px;border-radius:9px}
html[data-theme="light"] .tabs{
  background:linear-gradient(180deg,#FFF3E0 0%,#FFE8C9 100%);border-color:var(--line);
}
html[data-theme="light"] .tabs button{color:var(--muted)}
html[data-theme="light"] .tabs button[aria-selected="true"]{color:#fff}
@media(max-width:860px){
  html[data-theme="light"] .tabs{background:linear-gradient(180deg,#FFFFFF,#F7F5F1)}
  html[data-theme="light"] .tabs button{background:#FFFFFF;border-color:var(--line)}
  /* The rule above outranks .tabs button[aria-selected] on specificity, so the
     selected pill lost its gradient and kept its white text -- white on white.
     Restored here with a selector that wins. */
  html[data-theme="light"] .tabs button[aria-selected="true"]{
    background:var(--grad);color:#fff;border-color:transparent;
    box-shadow:0 2px 12px rgba(199,122,10,.30);
  }
  html[data-theme="light"] .tabs button:hover{background:#FFF6E8;color:var(--paper)}
  html[data-theme="light"] .tabs button[aria-selected="true"]:hover{
    background:var(--grad-h);color:#fff;
  }
  html[data-theme="light"] .topbar #btnLogout{
    color:#B33A24;border-color:rgba(198,58,36,.35);background:rgba(198,58,36,.07);
  }
}
/* Tile states were pastel text on a 10% tint -- built to glow on a dark
   ground. On cream that is nearly white text on nearly white: the roll call and
   attendance tiles disappeared. Light mode uses a stronger tint, a solid
   border and dark ink of the same hue. */
html[data-theme="light"] .tile.good{
  background:rgba(0,132,95,.13);border-color:rgba(0,132,95,.50);
  color:#065F46;box-shadow:none;
}
html[data-theme="light"] .tile.late{
  background:rgba(199,122,10,.15);border-color:rgba(199,122,10,.55);
  color:#7A4A05;box-shadow:none;
}
html[data-theme="light"] .tile.missing{
  background:rgba(198,58,36,.13);border-color:rgba(198,58,36,.50);
  color:#8E2A18;box-shadow:none;
}
html[data-theme="light"] .tile.good .c,
html[data-theme="light"] .tile.late .c,
html[data-theme="light"] .tile.missing .c{opacity:.9}
html[data-theme="light"] .tile:hover{filter:brightness(.97)}
/* the same pastels are used as plain text through both modules */
html[data-theme="light"] .pos{color:#06795A}
html[data-theme="light"] .neg{color:#B33A24}
html[data-theme="light"] .warn{color:#8A5A06}
html[data-theme="light"] .ok-msg{color:#06795A}
/* roll call status dots and the selected kind pill */
html[data-theme="light"] .stnav-btn.on{color:#3A2F1E}
html[data-theme="light"] .sumcell{background:rgba(255,176,60,.10)}

/* calendar */
html[data-theme="light"] .cal-grid{background:var(--line);border-color:var(--line)}
html[data-theme="light"] .cal-hd{background:linear-gradient(180deg,#FFEBCB,#FFE3B8);color:#7A5A22}
html[data-theme="light"] .cal-day{background:#FFFDF9}
html[data-theme="light"] .cal-day:hover{background:#FFF4E3}
html[data-theme="light"] .cal-pad{background:#FBF3E6}
html[data-theme="light"] .cal-num{color:#3A2F1E}
html[data-theme="light"] .cal-day.t-HOLIDAY{background:rgba(198,58,36,.06)}
/* roll call roster */
html[data-theme="light"] .rcrow .mark{background:linear-gradient(180deg,#FFF6E8,#FFEBCF)}
html[data-theme="light"] .rcrow:hover{background:rgba(90,205,175,.14)}
html[data-theme="light"] .rcrow.absent{background:rgba(198,58,36,.07);color:#8E2A18}
/* verdict panel keeps its strong colours; only the idle state changes */
html[data-theme="light"] .verdict.idle{background:#FFFFFF;border-color:var(--line)}
/* the login screen stays dark: it is a photograph on a dark backdrop */
/* The light theme used to force the login card DARK -- from the days when
   the sign-in screen was dark whatever the theme. html[data-theme] beats a
   plain #login selector on specificity, so these won however they were
   ordered, and the card came out black with pale labels on it. The card is
   white in both themes now, so there is nothing left to override. */

/* --- Modal --- */
.modal-ov{
  position:fixed;inset:0;z-index:300;display:flex;align-items:center;justify-content:center;
  background:rgba(8,10,20,.55);backdrop-filter:blur(3px);padding:18px;
}
.modal-bx{
  width:100%;max-width:460px;max-height:86vh;overflow:auto;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:16px 18px;box-shadow:var(--shadow-lg);
}
.modal-hd{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.modal-ft{display:flex;align-items:center;gap:8px;margin-top:14px}
.dstable{width:100%;font-size:13px}
.dstable th,.dstable td{padding:6px 6px}
.dstable input[type="number"]{width:74px;text-align:right;font-size:13px;padding:6px 8px}
.dstable tfoot td{border-top:1px solid var(--line-hi);padding-top:9px}
.dstot{font-family:var(--mono)}
@media(max-width:600px){
  .modal-bx{padding:14px;max-height:92vh}
  .dstable{font-size:12px}
  .dstable th,.dstable td{padding:5px 4px}
  .dstable input[type="number"]{width:60px;padding:6px 6px}
}
html[data-theme="light"] .modal-ov{background:rgba(60,45,20,.35)}
html[data-theme="light"] .modal-bx{background:#FFFDF9}

@media(max-width:600px){
  /* The heading takes the first line on its own; the two day buttons and the
     refresh icon share the second. nowrap stops the icon escaping again, and
     only the segmented control is allowed to shrink. */
  .livehead{gap:6px;flex-wrap:wrap;align-items:center}
  .livehead .eyebrow{flex:1 1 100%;margin-bottom:2px}
  .livehead .spacer{display:none}
  /* the global mobile rule sets .seg{width:100%}, which leaves no room on the
     row for the icon. width:auto lets flex-basis do the sizing instead. */
  .livehead .seg{width:auto;flex:1 1 auto;min-width:0;display:flex;flex-wrap:nowrap}
  .livehead .seg button{flex:1 1 auto;min-width:0;padding:7px 10px;font-size:12px;
    white-space:nowrap}
  .livehead .iconbtn{flex:0 0 auto;margin-left:auto}
}

/* --- Counter device line --- */
#devLine{
  display:flex;align-items:center;gap:10px;margin-bottom:12px;
  font-size:12px;
}
#devLine #devLineTxt{
  font-family:var(--display);letter-spacing:.08em;text-transform:uppercase;font-size:11px;
}
#devLine .btn.sm{padding:4px 10px;font-size:11px}
@media(max-width:600px){
  #devLine{margin-bottom:8px}
}

/* --- Photographs --- */
.photobox{
  width:72px;height:72px;flex-shrink:0;border-radius:var(--r-md);
  border:1px dashed var(--line);background:var(--panel);
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  font-family:var(--display);font-size:11px;color:var(--muted);letter-spacing:.06em;
}
.photobox img{width:100%;height:100%;object-fit:cover;display:block}
.phrow{display:flex;align-items:center;gap:10px;padding:6px 0;
  border-bottom:1px solid var(--line);font-size:12px}
.phrow:last-child{border-bottom:0}
.phthumb{width:34px;height:34px;border-radius:4px;object-fit:cover;flex-shrink:0;
  border:1px solid var(--line)}
.phname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* --- Profile photo --- */
.pfphoto{
  position:relative;width:88px;height:88px;flex-shrink:0;overflow:hidden;
  border:1px dashed var(--line);border-radius:var(--r-md);background:var(--panel);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-size:26px;color:var(--muted);
}
.pfphoto img{width:100%;height:100%;object-fit:cover;display:block}
.pfphoto.can-edit{cursor:pointer;border-style:solid}
.pfphoto-hint{
  position:absolute;left:0;right:0;bottom:0;padding:3px 0;
  background:rgba(8,10,20,.72);color:#fff;
  font-family:var(--display);font-size:9px;letter-spacing:.1em;text-transform:uppercase;
  text-align:center;opacity:0;transition:opacity .12s;
}
.pfphoto.can-edit:hover .pfphoto-hint{opacity:1}
/* no hover on a touch screen, so the label stays visible there */
@media(hover:none){ .pfphoto.can-edit .pfphoto-hint{opacity:1} }

/* --- Cafeteria --- */
.cafewrap{display:grid;grid-template-columns:1fr 320px;gap:14px;align-items:start}
.cftiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(124px,1fr));gap:8px}
/* A <button> with no colour set falls back to the browser's own buttontext,
   which is near-black -- invisible on a dark panel. The font falls back too, so
   the tiles were rendering in the UA's default face as well. */
.cftile{
  cursor:pointer;padding:12px 10px;border-radius:var(--r-md);
  border:1px solid var(--line);background:var(--panel);text-align:left;
  color:var(--paper);font:inherit;
  transition:transform .08s,border-color .12s;
}
.cftile:hover{border-color:rgba(245,166,35,.45)}
.cftile:active{transform:scale(.97)}
.cftile .nm{font-size:12.5px;font-weight:600;line-height:1.25}
.cftile .pr{font-family:var(--mono);font-size:13px;margin-top:4px;color:var(--paper)}
.cftile .ct{font-size:9.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
.cfbill{
  position:sticky;top:8px;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--panel);padding:12px;
}
.cflines{max-height:38vh;overflow:auto;margin-bottom:8px}
.cfline{display:flex;align-items:center;gap:8px;padding:5px 0;
  border-bottom:1px solid var(--line);font-size:12.5px}
.cfline:last-child{border-bottom:0}
.cfline .n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cfline .q{font-family:var(--mono)}
.cfline button{padding:2px 7px;font-size:12px;line-height:1}
.cftotal{display:flex;justify-content:space-between;align-items:baseline;
  border-top:1px solid var(--line-hi);padding-top:8px;
  font-family:var(--display);font-size:13px;letter-spacing:.08em;text-transform:uppercase}
.cftotal b{font-family:var(--mono);font-size:22px;letter-spacing:0}
.cfwho{margin-top:8px;padding:8px 10px;border-radius:var(--r-md);
  border:1px solid var(--line);font-size:12.5px}
.cfwho .bal{font-family:var(--mono);font-size:18px}
.cfwho.low{border-color:var(--neg)}
@media(max-width:860px){
  .cafewrap{grid-template-columns:1fr}
  .cfbill{position:static}
  .cftiles{grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:6px}
  .cflines{max-height:30vh}
}

/* --- Cafeteria till --- */
.cftile{position:relative}
.cftile.picked{border-color:var(--gold);background:var(--glass);color:var(--paper)}
/* the picked tile in light mode needs the same treatment the other themed
   surfaces got: a tint, not a dark fill */
html[data-theme="light"] .cftile{background:linear-gradient(160deg,#FFFDF9,#FFF4E3)}
html[data-theme="light"] .cftile.picked{background:rgba(199,122,10,.12)}
.cftile .badge{
  position:absolute;top:6px;right:6px;min-width:22px;height:22px;padding:0 6px;
  border-radius:999px;background:var(--grad);color:#fff;
  font-family:var(--mono);font-size:11px;line-height:22px;text-align:center;
}
/* Clear and Charge sit together at the foot of the bill panel, in normal flow.
   The fixed-to-the-bottom version and the grid placement that replaced it both
   put the button somewhere the eye had to hunt for it. */
.cfbar{display:flex;gap:8px;align-items:center;margin-top:10px}
.cfbar .cfbar-go{
  background:var(--allow);color:#fff;border:none;
  font-family:var(--display);letter-spacing:.04em;padding:10px 18px;
}
.cfbar .cfbar-go:disabled{opacity:.45}
@media(max-width:860px){
  /* One column. The bill follows the items and the buttons stay with it, so
     there is nothing floating and nothing to toggle between. */
  .cafewrap{grid-template-columns:1fr}
  .cfbar-go{flex:1;padding:12px;font-size:15px}
}
/* receipt list */
.rcpt{display:flex;align-items:center;gap:10px;padding:10px 12px;
  border:1px solid var(--line);border-radius:var(--r-md);margin-bottom:6px;
  background:var(--panel);cursor:pointer}
.rcpt:hover{border-color:rgba(245,166,35,.4)}
.rcpt .no{font-family:var(--mono);font-size:12.5px}
.rcpt .who{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px}
.rcpt .amt{font-family:var(--mono);font-size:14px}
.rcpt.void{opacity:.55}
.rcpt.void .amt{text-decoration:line-through}
/* thermal receipt: 80mm roll */
@media print{
  body * {visibility:hidden !important}
  #rcptPrint, #rcptPrint * {visibility:visible !important}
  #rcptPrint{position:absolute;left:0;top:0;width:72mm;color:#000;background:#fff}
  @page{size:80mm auto;margin:3mm}
}
#rcptPrint{display:none}
#rcptPrint.on{display:block}
.rcline{display:flex;justify-content:space-between;gap:8px;font-size:12px}

/* --- Cafeteria categories --- */
.cfcats{
  display:flex;flex-direction:column;gap:4px;align-self:start;
  position:sticky;top:8px;max-height:calc(100vh - 120px);overflow:auto;
  padding-right:2px;
}
.cfcat{
  display:flex;align-items:center;gap:6px;width:100%;text-align:left;
  padding:8px 10px;border-radius:var(--r-md);cursor:pointer;
  border:1px solid transparent;background:transparent;color:var(--muted);
  font-family:var(--body);font-size:12.5px;line-height:1.25;
}
.cfcat:hover{background:var(--glass);color:var(--paper)}
.cfcat.on{background:var(--glass);color:var(--paper);border-color:var(--line-hi);font-weight:600}
.cfcat .n{
  margin-left:auto;font-family:var(--mono);font-size:10.5px;opacity:.7;
  background:var(--glass);border-radius:999px;padding:1px 6px;
}
/* three columns: categories, tiles, bill */
.cafewrap{grid-template-columns:168px 1fr 320px}
/* a till operator has no section nav, so the panes get the full width */
#view-cafe .stwrap.nonav{grid-template-columns:1fr}
#view-cafe .stwrap.nonav .stnav{display:none}
@media(max-width:1100px){
  .cafewrap{grid-template-columns:150px 1fr 280px}
}
@media(max-width:860px){
  /* The rail becomes a scrolling chip strip above the tiles -- the same shape
     as the section navs, so it needs no learning. */
  .cafewrap{grid-template-columns:1fr}
  .cfcats{
    flex-direction:row;flex-wrap:nowrap;position:static;max-height:none;
    overflow-x:auto;gap:6px;padding-bottom:2px;margin-bottom:8px;
    scrollbar-width:none;scroll-snap-type:x proximity;
  }
  .cfcats::-webkit-scrollbar{display:none}
  .cfcat{
    width:auto;flex:0 0 auto;scroll-snap-align:start;white-space:nowrap;
    border:1px solid var(--line);border-radius:999px;padding:7px 12px;
    background:var(--glass);
  }
  .cfcat.on{background:var(--grad);color:#fff;border-color:transparent}
  .cfcat.on .n{background:rgba(255,255,255,.22)}
}

/* --- Top bar connection --- */
.conn{display:flex;align-items:center;gap:6px;flex:0 0 auto}
.conn #connTxt:empty{display:none}
@media(max-width:860px){
  /* Every chip on this row keeps its own width so Sign out cannot be pushed
     onto a second line; the branch name is the one thing allowed to shorten. */
  .topbar{flex-wrap:wrap;row-gap:6px}
  /* The first row has to hold: logo, connection dot, session, role, and two
     icons. At 44vw for the logo and 38vw for the role chip it was over budget
     before the icons were counted, so Sign out wrapped. Both now shrink, and
     the role chip is the one allowed to lose characters -- it is the longest
     and the least load-bearing. */
  .topbar .role{
    flex:0 1 auto;min-width:0;max-width:30vw;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  }
  .brandlock{max-width:32vw}
  .topbar{column-gap:6px}
  .topbar .conn{flex:0 0 auto}
  .conn #connTxt{font-size:10px;max-width:30vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
}

/* --- Cafeteria takings split --- */
.splittbl{width:100%;margin:10px 0 4px;font-size:12.5px}
.splittbl th,.splittbl td{padding:6px 8px}
.splittbl tfoot td{border-top:1px solid var(--line-hi)}
.splittbl td:first-child{font-family:var(--display);font-size:11px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
@media(max-width:600px){
  .splittbl{font-size:11.5px}
  .splittbl th,.splittbl td{padding:5px 4px}
}

/* --- Cafeteria till --- */
/* One markup for both. .cfhead is display:contents on a desktop, so the search
   bar and the chips are placed into the grid individually; on a phone it is a
   real sticky box so they pin together with no offset arithmetic. */
.cfhead{display:contents;min-width:0}
/* The till's own filter row carries the .filterbar class for its spacing, but
   the bordered, tinted box that class draws made a panel behind the branch and
   search controls that belonged to nothing. Spacing without the chrome. */
.cffilter{border:none;background:transparent;padding:0;margin:0 0 10px}
.cffilter #cfSearch{min-width:0}
/* Clear and Charge are the two ways off the bill, so they are a pair at every
   width: same height, same shape, same weight. Clear stays neutral because it
   is the destructive one and should not compete for the eye. */
.cfbar{gap:8px}
.cfbar #cfClear,.cfbar-go{
  flex:1 1 0;padding:11px;font-size:14px;border-radius:var(--r-md);
  font-family:var(--display);letter-spacing:.04em;
}
.cfbar #cfClear{
  background:var(--glass);border:1px solid var(--line-hi);color:var(--paper);
}
.cfbar .spacer{display:none}
/* A grid item defaults to min-width:auto, which lets its content widen the
   column past the screen -- the tiles were doing exactly that and the page
   scrolled sideways. Every item here is told it may be narrower than its
   contents. */
.cafewrap{grid-template-columns:168px 1fr 320px;min-width:0}
.cafewrap > *{min-width:0}
.cftiles{min-width:0}
.cftile{min-width:0;overflow:hidden}
.cftile .nm{overflow-wrap:anywhere}
.cafewrap > .cffilter{grid-column:1 / -1}
.cafewrap .cfcats{grid-column:1}
.cafewrap > #cfItemsCol{grid-column:2}
.cafewrap > .cfbill{grid-column:3}
/* The store till reuses this grid. The placement rules were keyed to
   #cfItemsCol by id, so a second till with its own id got no column at all
   and every child stacked. The hook is a class now; the id rule stays so
   nothing about the cafeteria changes. */
.cafewrap > .cfitems{grid-column:2}
/* A picker with no bill: two columns, rail and tiles. */
.cafewrap-2{grid-template-columns:168px minmax(0,1fr)}
.cafewrap-2 > .cfitems{grid-column:2}
/* A horizontal tab row, which is NOT what .cfcats is. That rail is a sticky
   vertical sidebar and only turns into a chip strip on a phone; used outside
   a .cafewrap column it stacks and pushes the content below it. The store's
   reports needed a row of tabs, so they get one. Same chip look at every
   width, scrolls sideways when there are more than fit. */
.sttabs{
  display:flex;flex-direction:row;flex-wrap:nowrap;gap:6px;
  overflow-x:auto;scrollbar-width:none;margin:0 0 10px;
  scroll-snap-type:x proximity;
}
.sttabs::-webkit-scrollbar{display:none}
.sttabs .cfcat{
  width:auto;flex:0 0 auto;scroll-snap-align:start;white-space:nowrap;
  border:1px solid var(--line);border-radius:999px;padding:7px 12px;
  background:var(--glass);
}
.sttabs .cfcat.on{background:var(--grad);color:#fff;border-color:transparent}

/* --- Purchase order builder --- */
/* Four panes: categories, items, that item's variants with a quantity box
   on each row, and the order itself. Its own grid rather than .cafewrap,
   whose columns are keyed to the till and whose third column is a bill. */
.pobuild{
  display:grid;grid-template-columns:150px 190px minmax(0,1fr) 300px;
  gap:12px;align-items:start;min-width:0;
}
.pobuild > *{min-width:0}
.pocol{
  border:1px solid var(--line);border-radius:var(--r-md);padding:8px;
  max-height:calc(100vh - 240px);overflow:auto;
}
.pocol h4{
  margin:0 0 6px;font-family:var(--display);font-size:11px;
  letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
}
/* A row in the first two panes: a whole-width button, like the till rail. */
.porow{
  display:flex;align-items:center;gap:6px;width:100%;text-align:left;
  padding:7px 8px;border-radius:var(--r-md);cursor:pointer;
  border:1px solid transparent;background:transparent;color:var(--muted);
  font-family:var(--body);font-size:12.5px;line-height:1.25;
}
.porow:hover{background:var(--glass);color:var(--paper)}
.porow.on{background:var(--glass);color:var(--paper);
  border-color:var(--line-hi);font-weight:600}
.porow .n{margin-left:auto;font-family:var(--mono);font-size:10.5px;opacity:.7}
/* The variant row carries its own quantity box, so a line is one
   interaction rather than a click across and then a type. */
.povar{
  display:flex;align-items:center;gap:8px;padding:6px 4px;
  border-bottom:1px solid var(--line);
}
.povar:last-child{border-bottom:0}
.povar .v{flex:1;min-width:0;font-size:13px}
.povar .s{font-family:var(--mono);font-size:11px;color:var(--muted)}
.povar input{width:64px;text-align:right;font-family:var(--mono)}
.povar.has{background:var(--glass)}

@media(max-width:1200px){
  .pobuild{grid-template-columns:130px 160px minmax(0,1fr) 260px}
}
@media(max-width:980px){
  /* Stacked, with the order last: on a narrow screen the buyer scrolls to
     it once at the end rather than past it on every category. */
  .pobuild{grid-template-columns:minmax(0,1fr)}
  .pocol{max-height:none}
  .pocol.ponav{max-height:220px}
}

/* The printed order. White paper, black text, no chrome -- it leaves the
   building and a vendor reads it. */
.podoc{
  background:#fff;color:#111;padding:22px;border-radius:var(--r-md);
  font-family:var(--body);max-width:820px;
}
.podoc h2{margin:0 0 2px;font-size:19px;color:#111}
.podoc .muted{color:#666;font-size:12px}
.podoc table{width:100%;border-collapse:collapse;margin-top:14px}
.podoc th,.podoc td{
  border:1px solid #ccc;padding:6px 8px;font-size:12.5px;text-align:left;
}
.podoc th{background:#f2f2f2;color:#111}
.podoc td.num,.podoc th.num{text-align:right;font-family:var(--mono)}
.podoc .sign{margin-top:34px;display:flex;justify-content:space-between}

/* The bundle picker reuses the till grid. .cfbill is sized for a receipt
   and a total; here it holds a plain list, so it keeps the column and
   drops the sticky footer the till needs. */
#storepane-entitle .cfbill{position:static;max-height:none}
#storepane-entitle .cftiles .cftile.on{border-color:var(--accent)}

/* --- Welcome bundle class picker --- */
/* A wrapping grid of tick boxes rather than a tall list: a school has
   thirty-odd classes and the whole set has to be readable at once for
   somebody to see what a bundle covers. */
.encls{
  display:grid;gap:6px;
  grid-template-columns:repeat(auto-fill,minmax(170px,1fr));
}
.encls label{
  display:flex;align-items:center;gap:8px;
  border:1px solid var(--line);border-radius:var(--r-md);
  padding:8px 10px;cursor:pointer;font-size:13px;
}
.encls label.on{border-color:var(--accent);background:var(--accent-soft,transparent)}
.encls label .n{margin-left:auto;font-size:11px;opacity:.6}

/* --- Returns basket --- */
/* The controls were a .qrbar of mixed-height labels, so they wrapped into
   a zig-zag and the written-off note added a stray line. A fixed grid
   instead: name across the top, then quantity, condition and swap on one
   row that lines up down the list. */
.rtrow{
  border:1px solid var(--line);border-radius:var(--r-md);
  padding:10px 12px;margin-bottom:8px;
}
.rtrow .hd{display:flex;align-items:baseline;gap:8px;margin-bottom:8px}
.rtrow .hd .nm{font-weight:600}
.rtrow .ct{
  display:grid;grid-template-columns:auto 1fr auto;gap:10px;
  align-items:center;
}
.rtrow .ct .qty{display:flex;align-items:center;gap:6px;white-space:nowrap}
.rtrow .ct .qty input{width:58px;text-align:right;font-family:var(--mono)}
.rtrow .ct .cond{display:flex;align-items:center;gap:6px;min-width:0}
.rtrow .note{margin-top:8px;font-size:12px;line-height:1.4}
@media(max-width:640px){
  .rtrow .ct{grid-template-columns:1fr;gap:8px}
  .rtrow .ct > *{justify-self:start}
}
@media print{
  body *{visibility:hidden}
  .podoc,.podoc *{visibility:visible}
  .podoc{position:absolute;left:0;top:0;width:100%;max-width:none;padding:0}
  .noprint{display:none!important}
}
.cffoot{display:none}

@media(max-width:1100px){
  .cafewrap{grid-template-columns:150px 1fr 280px}
}

@media(max-width:860px){
  /* overflow-x:hidden on an ancestor turns it into a scroll container, and a
     sticky child then sticks to that container instead of the page -- which is
     why the header scrolled away with the items. So it goes only on the tiles,
     which is where the wide content actually is, and never on anything that
     contains .cfhead. */
  #view-cafe,#cfpane-till,#view-store,#storepane-till,
  .cafewrap,.cfhead{max-width:100%}
  #cfItemsCol,.cfitems,.cftiles{max-width:100%;overflow-x:hidden}
  .cfcats{overflow-x:auto}          /* the chips are meant to scroll sideways */
  .cafewrap{grid-template-columns:minmax(0,1fr)}
  .cafewrap > .cffilter,
  .cafewrap .cfcats,
  .cafewrap > #cfItemsCol,
  .cafewrap > .cfbill{grid-column:1}
  .cafewrap > .cfitems{grid-column:1}
  .cafewrap-2{grid-template-columns:minmax(0,1fr)}
  #view-cafe .stwrap{grid-template-columns:1fr}
  #view-cafe .stwrap .stnav{display:none}
  #view-cafe .stwrap.onecafe #cfBranch{display:none}

  .cfhead{
    display:block;position:sticky;top:0;z-index:40;
    background:var(--depth);padding:6px 0 4px;margin:0 0 6px;
  }
  .cffilter{gap:6px;padding:0;margin:0 0 6px;border:none;background:transparent}
  .cffilter #cfSearch{flex:1 1 100%;font-size:13px}
  .cffilter #cfMenuMsg{flex:1 1 100%;order:9}
  .cfcats{margin:0}

  #cfItemsCol{
    padding-top:0;
    padding-bottom:calc(124px + env(safe-area-inset-bottom,0px));
  }

  .cffoot{
    display:block;position:fixed;left:0;right:0;bottom:0;z-index:80;
    background:var(--panel);border-top:1px solid var(--line-hi);
    padding:8px 10px calc(6px + env(safe-area-inset-bottom,0px));
  }
  /* hidden unless the till is the pane in view */
  .cffoot:not(.tillonly) .cffoot-go{display:none}
  .cffoot-go{
    display:flex;align-items:center;justify-content:center;gap:10px;
    width:100%;padding:13px;border:0;border-radius:var(--r-md);cursor:pointer;
    background:var(--allow);color:#fff;font:inherit;
    font-family:var(--display);font-size:14px;letter-spacing:.04em;
    margin-bottom:8px;
  }
  .cffoot-go b{font-family:var(--mono);letter-spacing:0}
  .cffoot-tabs{display:flex;align-items:stretch;gap:2px}
  .cffoot-tabs button{
    flex:1 1 0;min-width:0;padding:7px 2px 5px;border:0;background:transparent;
    color:var(--muted);font:inherit;font-size:10.5px;cursor:pointer;
    display:flex;flex-direction:column;align-items:center;gap:3px;
    border-radius:var(--r-md);position:relative;
  }
  .cffoot-tabs button i{display:block;width:22px;height:22px}
  .cffoot-tabs button i svg{width:100%;height:100%;display:block}
  .cffoot-tabs button.on{color:var(--paper);font-weight:600;background:var(--glass)}
  .cfbadge{
    position:absolute;top:2px;right:calc(50% - 20px);
    min-width:16px;height:16px;line-height:16px;border-radius:999px;
    background:var(--grad);color:#fff;font-family:var(--mono);font-size:9.5px;
    padding:0 4px;display:none;
  }

  #cfpane-till.billmode .cfhead,
  #cfpane-till.billmode #cfItemsCol{display:none}
  #cfpane-till:not(.billmode) .cfbill{display:none}
  #cfpane-till.billmode .cfbill{
    position:static;max-height:none;overflow:visible;
    margin:0 0 calc(124px + env(safe-area-inset-bottom,0px));
    border-radius:var(--r-md);box-shadow:none;transform:none;padding:12px;
  }
  .cflines{max-height:none}
}
@media(min-width:861px){
  .cfburger{display:none}
}

/* --- Cafeteria report quick ranges --- */
.csquick{margin-top:-4px;padding-top:6px}
.csquick .fbsep{font-family:var(--display);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase}
@media(max-width:600px){
  .csquick{gap:6px}
  .csquick .btn{flex:1 1 auto;padding:7px 8px;font-size:11.5px}
  .csquick .fbsep{flex:1 1 100%}
}

/* --- Cafeteria insights --- */
.insgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:12px;margin-top:12px}
.insgrid .card{margin:0}
.insgrid .sub{margin:2px 0 10px;font-size:11.5px}
.bars{display:flex;flex-direction:column;gap:5px}
.bar{display:flex;align-items:center;gap:8px;font-size:12px}
.bar .bl{flex:0 0 62px;color:var(--muted);font-family:var(--mono);font-size:11px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bar .bt{flex:1;height:14px;background:var(--glass);border-radius:3px;overflow:hidden}
.bar .bt i{display:block;height:100%;background:var(--grad);border-radius:3px}
.bar .bv{flex:0 0 auto;font-family:var(--mono);font-size:11.5px;text-align:right;min-width:88px}
@media(max-width:860px){
  /* One panel per row. minmax(320px,1fr) does not fit a 360px phone once the
     page padding is taken off, so the grid was overflowing rather than
     stacking -- which is what made every panel look cut off. */
  .insgrid{grid-template-columns:minmax(0,1fr);gap:10px}
  .insgrid .card{padding:12px 12px;min-width:0;overflow:hidden}
  .insgrid .eyebrow{font-size:10px}
  .insgrid .sub{font-size:11px;line-height:1.45;margin:2px 0 8px}

  /* Bars: the label column was a fixed 62px of monospace, which left almost
     nothing for the bar itself on a narrow screen. */
  .bar{gap:6px;font-size:11.5px}
  .bar .bl{flex:0 0 46px;font-size:10px}
  .bar .bt{height:12px}
  .bar .bv{min-width:0;flex:0 0 auto;font-size:10.5px;white-space:nowrap}
  .bar .bv .dim{display:none}      /* the secondary figure is the first to go */

  /* Tables inside a panel: smaller type, tighter cells, and the columns that
     are nice-to-have rather than the point are dropped. */
  .insgrid table{font-size:11.5px;white-space:normal}
  .insgrid th,.insgrid td{padding:6px 5px}
  .insgrid .scrollbox{max-height:200px}

  /* Top spenders: Visits goes, the name and what they spent stay. */
  #inTop table th:nth-child(2),#inTop table td:nth-child(2){display:none}
  /* Sold nothing: Category goes; the item and its price are the decision. */
  #inDead table th:nth-child(2),#inDead table td:nth-child(2){display:none}
  /* Who served: Sales count goes, takings and cash stay. */
  #inOps table th:nth-child(2),#inOps table td:nth-child(2){display:none}
  /* Running low: the date goes, the balance is the point. */
  #inLow table th:nth-child(3),#inLow table td:nth-child(3){display:none}

  /* Long names wrap rather than widening the panel. */
  .insgrid td{overflow-wrap:anywhere}
  .insgrid td .dim{display:block;font-size:10px}

  /* The period table above is wide by nature: let it scroll sideways rather
     than squeeze eight columns into 360px. */
  #csBody table{white-space:nowrap;font-size:11.5px}
  #csBody{overflow-x:auto}
  .splittbl{font-size:11px}
  .splittbl th,.splittbl td{padding:5px 3px}
}
@media(max-width:600px){
  .insgrid .sub{font-size:10.5px}
  .bar .bl{flex-basis:42px}
}

/* --- Long insight lists --- */
/* Five rows and then scroll: these lists run to a hundred entries, and a panel
   that long pushes every panel after it off the screen. The header stays put so
   the columns are still labelled halfway down. */
.scrollbox{max-height:212px;overflow-y:auto;overscroll-behavior:contain}
.scrollbox table{margin:0}
.scrollbox thead th{
  position:sticky;top:0;z-index:1;
  background:var(--panel);
}
.scrollbox::-webkit-scrollbar{width:8px}
.scrollbox::-webkit-scrollbar-thumb{background:var(--line-hi);border-radius:4px}
html[data-theme="light"] .scrollbox thead th{background:#FFEBCB}
@media(max-width:600px){
  .scrollbox{max-height:190px}
}

@media(max-width:860px){
  .inwhoseg{width:100%;display:flex}
  .inwhoseg button{flex:1 1 0;min-width:0;font-size:11px;padding:7px 4px}
  /* the quick-range row and the filter bar in this pane wrap cleanly */
  #cfpane-reports .filterbar{gap:6px}
  #cfpane-reports .filterbar input[type="date"]{flex:1 1 42%}
  #cfpane-reports .filterbar select{flex:1 1 100%}
  #cfpane-reports .filterbar .btn{flex:1 1 auto}
}

/* --- Every insight panel is bounded --- */
/* A month of data fits in any of these; a year does not. Bounding them all
   keeps the grid of panels a fixed shape however much history builds up, so
   the page never becomes one panel followed by a mile of scrolling.
   Bar lists get a slightly taller box than tables because a bar row is
   shorter than a table row. */
.scrollbox{max-height:212px;overflow-y:auto;overscroll-behavior:contain}
#inHours.scrollbox,#inDow.scrollbox,#inCats.scrollbox{max-height:190px}
/* the bars have no header to pin, so a little breathing room instead */
#inHours .bars,#inDow .bars,#inCats .bars{padding-right:4px}
/* a scrolled panel says so, rather than looking like it simply ends */
.scrollbox{
  background:
    linear-gradient(var(--panel) 30%, rgba(0,0,0,0)) top / 100% 18px no-repeat,
    linear-gradient(rgba(0,0,0,0), var(--panel) 70%) bottom / 100% 18px no-repeat;
  background-attachment:local, local;
}
@media(max-width:860px){
  .scrollbox{max-height:200px}
  #inHours.scrollbox,#inDow.scrollbox,#inCats.scrollbox{max-height:178px}
}

/* --- Top-up slabs --- */
.slabs{display:flex;gap:6px;flex-wrap:wrap}
.slabs .slab{flex:1 1 auto;min-width:64px;font-family:var(--mono)}
.linkrow input{width:100%;font-family:var(--mono);font-size:11.5px;padding:7px 8px}

/* --- Modules and their screens --- */
/* The top bar carries modules; the left panel carries the screens inside the
   one you are in. Both are built from the access matrix, so a role that can
   reach nothing in a module never sees it. */
.applayout{display:grid;grid-template-columns:186px minmax(0,1fr);gap:0;align-items:start}
.applayout.nosub{grid-template-columns:minmax(0,1fr)}
.applayout.nosub .subnav{display:none}
.subnav{
  position:sticky;top:0;align-self:start;
  display:flex;flex-direction:column;gap:2px;
  padding:16px 10px;min-height:calc(100vh - 56px);
  border-right:1px solid var(--line);
}
.subnav-btn{
  text-align:left;padding:9px 12px;border-radius:var(--r-md);
  border:1px solid transparent;background:transparent;color:var(--muted);
  font:inherit;font-size:13px;cursor:pointer;
}
.subnav-btn:hover{background:var(--glass);color:var(--paper)}
.subnav-btn.on{background:var(--glass);color:var(--paper);font-weight:600;
  border-color:var(--line-hi)}
.viewhost{min-width:0}
@media(max-width:860px){
  /* No room for a left panel beside the content. The screens become the same
     scrolling pill strip used elsewhere in the app, above the view. */
  .applayout{grid-template-columns:minmax(0,1fr)}
  .subnav{
    position:sticky;top:0;z-index:35;
    flex-direction:row;flex-wrap:nowrap;overflow-x:auto;gap:5px;
    min-height:0;padding:8px 10px;border-right:none;
    border-bottom:1px solid var(--line);background:var(--depth);
    scrollbar-width:none;
  }
  .subnav::-webkit-scrollbar{display:none}
  .subnav-btn{
    flex:0 0 auto;white-space:nowrap;border-radius:999px;
    border:1px solid var(--line);background:var(--glass);padding:7px 13px;font-size:12px;
  }
  .subnav-btn.on{background:var(--grad);color:#fff;border-color:transparent}
  #modTabs button{font-size:12px;padding:8px 14px}
}

/* --- Top bar icon buttons --- */
/* Account and Sign out as glyphs: two words were the difference between this
   row fitting and wrapping once modules arrived. Both keep a title and an
   aria-label, so the meaning survives for a screen reader and on hover. */
.topicon{
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;padding:0;flex:0 0 auto;
}
.topicon svg{width:17px;height:17px;display:block}
.topicon.danger{color:#FF8A73;border-color:rgba(232,83,60,.35)}
.topicon.danger:hover{background:rgba(232,83,60,.14);color:#FF8A73}
html[data-theme="light"] .topicon.danger{
  color:#B33A24;border-color:rgba(198,58,36,.35);background:rgba(198,58,36,.06);
}
@media(max-width:860px){
  .topicon{width:30px;height:30px}
  .topicon svg{width:16px;height:16px}
  /* the pill styling written for the old text button no longer applies */
  .topbar #btnLogout{padding:0;border-radius:var(--r-md);font-size:0}
}

/* --- Top bar on a small phone --- */
@media(max-width:430px){
  /* Something has to go, and the meal is on the Counter screen anyway. */
  #sessionChipWrap{display:none}
  .brandlock{max-width:34vw}
  .topbar .role{max-width:36vw}
}
@media(max-width:360px){
  /* At this width the role chip is three letters of ellipsis. The role is on
     the Account screen; being able to leave is not. */
  .topbar .role{display:none}
  .brandlock{max-width:46vw}
}

/* --- Kitchen filter bar --- */
/* A later generic rule gives every filterbar date a 40% basis that grows.
   Same specificity as the kitchen rule written before it, so it won, the date
   took the row and the refresh icon wrapped. Scoped to the view here so it
   outranks both, and the icon is pinned to the end of the line it shares. */
@media(max-width:600px){
  /* One row: Today, Tomorrow, the date and the refresh icon. nowrap means
     nothing can escape to a second line, and the two things that can absorb
     the squeeze -- the buttons and the date -- shrink instead. */
  #view-kitchen .filterbar.kcbar{flex-wrap:nowrap;gap:5px;padding:7px 8px}
  #view-kitchen .filterbar.kcbar .btn.sm:not(.iconbtn){
    flex:0 1 auto;min-width:0;padding:7px 8px;font-size:11.5px;white-space:nowrap;
  }
  #view-kitchen .filterbar.kcbar input[type="date"]{
    flex:1 1 96px;width:auto;min-width:0;font-size:11.5px;padding:6px 4px;
  }
  #view-kitchen .filterbar.kcbar .iconbtn{flex:0 0 auto}
  #view-kitchen .filterbar.kcbar .spacer{display:none}
}

/* --- Takings split table --- */
/* The global mobile rule turns every table into display:block so wide ones can
   scroll. A block box sizes its inner table to the content, so width:100% stops
   stretching and this five-column table rendered narrower than the screen with
   a gap down the right. It is narrow by nature and needs no scrolling, so it
   goes back to being a table and fills the width. */
@media(max-width:860px){
  #cfpane-reports .splittbl{
    display:table;width:100%;table-layout:fixed;white-space:normal;overflow:visible;
  }
  #cfpane-reports .splittbl th,
  #cfpane-reports .splittbl td{padding:6px 4px;overflow:hidden;text-overflow:ellipsis}
  /* the label column needs least; the four money columns share the rest */
  #cfpane-reports .splittbl th:first-child,
  #cfpane-reports .splittbl td:first-child{width:19%}
  #cfpane-reports .splittbl th:not(:first-child),
  #cfpane-reports .splittbl td:not(:first-child){width:20.25%}
}

/* --- Pharmacy tiles --- */
.cftile .stk{font-size:10px;margin-top:3px;color:var(--muted)}
.cftile.outofstock{opacity:.5;cursor:not-allowed}
.cftile.outofstock:hover{border-color:var(--line)}

/* --- Purchase order document --- */
.podoc{background:#fff;color:#111;padding:22px;font-family:var(--body);font-size:12px}
.podoc-hd{display:flex;justify-content:space-between;gap:20px;
  border-bottom:2px solid #111;padding-bottom:10px;margin-bottom:12px}
.podoc-org{font-size:17px;font-weight:700}
.podoc-title{font-size:15px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.podoc-sm{font-size:11px;color:#444;line-height:1.5}
.podoc-lbl{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:#666;
  margin-bottom:3px}
.podoc-cols{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:14px}
.podoc-tbl{width:100%;border-collapse:collapse;font-size:12px}
.podoc-tbl th,.podoc-tbl td{border:1px solid #bbb;padding:6px 8px;text-align:left;color:#111}
.podoc-tbl th{background:#f2f2f2;font-size:11px;text-transform:uppercase;letter-spacing:.04em}
.podoc-tbl .num{text-align:right}
.podoc-foot{display:flex;justify-content:space-between;margin-top:34px;
  font-size:11px;color:#444}
/* the preview inside the dialog scrolls; the print version does not */
.podoc-preview{max-height:52vh;overflow:auto;border:1px solid var(--line);
  border-radius:var(--r-md)}
.podoc-preview .podoc{padding:16px}
#poPrint{display:none}
#poPrint.on{display:block}
@media print{
  body.po-printing *{visibility:hidden !important}
  #poPrint.on,#poPrint.on *{visibility:visible !important}
  #poPrint.on{position:absolute;left:0;top:0;width:100%}
  @page{size:A4;margin:14mm}
}

/* --- Roles --- */
.roleswrap{display:grid;grid-template-columns:220px minmax(0,1fr);gap:14px;align-items:start}
.rolelist{border:1px solid var(--line);border-radius:var(--r-md);padding:10px}
.rlrow{
  display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  padding:8px 10px;border-radius:var(--r-md);border:1px solid transparent;
  background:transparent;color:var(--muted);font:inherit;font-size:12.5px;cursor:pointer;
}
.rlrow:hover{background:var(--glass);color:var(--paper)}
.rlrow.on{background:var(--glass);color:var(--paper);font-weight:600;border-color:var(--line-hi)}
.rlrow .nm{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rlrow .n{font-family:var(--mono);font-size:10.5px;opacity:.7;
  background:var(--glass);border-radius:999px;padding:1px 7px}
.roleedit{border:1px solid var(--line);border-radius:var(--r-md);padding:14px;min-width:0}
.rlgroup{margin-top:12px}
.rlgroup-hd{
  display:flex;align-items:center;gap:8px;margin-bottom:6px;
  font-family:var(--display);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);
}
.rlgroup-hd span{flex:1}
.rlperm{
  display:inline-flex;align-items:center;gap:7px;margin:0 6px 6px 0;
  padding:7px 11px;border-radius:999px;border:1px solid var(--line);
  background:var(--glass);font-size:12.5px;cursor:pointer;color:var(--muted);
}
.rlperm.on{border-color:var(--gold);color:var(--paper)}
.rlperm input{width:auto;margin:0}
/* the comparison grid: narrow columns, and a header that stays put */
.rlmx{width:100%;font-size:11.5px;border-collapse:collapse}
.rlmx th,.rlmx td{padding:5px 6px;white-space:nowrap}
.rlmx thead th{
  position:sticky;top:0;background:var(--panel);z-index:1;
  font-size:10px;letter-spacing:.04em;
}
.rlmx thead th:first-child,.rlmx tbody td:first-child{
  position:sticky;left:0;background:var(--panel);z-index:2;text-align:left;
}
.rlmx thead th:first-child{z-index:3}
.rlmx-grp td{
  font-family:var(--display);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);padding-top:10px;
}
html[data-theme="light"] .rlmx thead th,
html[data-theme="light"] .rlmx tbody td:first-child{background:#FFF8ED}
@media(max-width:860px){
  .roleswrap{grid-template-columns:minmax(0,1fr)}
  .rolelist{max-height:220px;overflow:auto}
}

/* --- Module panes live in the left navigation --- */
/* These rows are written into index.html, so they list every pane whether or
   not the person may reach it. The left panel is built from the access matrix
   and shows only what they hold.
   The rule used to hide the static rows above 860px only, on the reasoning
   that phones had no left panel. Phones DO have it -- it becomes a scrolling
   strip -- so on mobile both appeared, and the unfiltered one showed every
   pane in the module to everybody. Hidden at every width now: one navigation,
   built from permissions. */
#view-cafe .stwrap > .stnav,
#view-pharmacy .stwrap > .stnav,
#view-expenses .stwrap > .stnav,
#view-gate .stwrap > .stnav{display:none !important}
#view-cafe .stwrap,
#view-pharmacy .stwrap,
#view-expenses .stwrap,
#view-gate .stwrap{grid-template-columns:minmax(0,1fr)}

/* --- Which counter a purchase came from --- */
.tagsrc{
  font-family:var(--display);font-size:9px;letter-spacing:.08em;text-transform:uppercase;
  border:1px solid var(--line);border-radius:999px;padding:1px 6px;color:var(--muted);
}

/* --- Wallet movements that are not purchases --- */
.rcpt.movement{cursor:default}
.rcpt.movement:hover{border-color:var(--line)}

/* --- Running low --- */
/* Five rows and then scroll. With a hundred items and a reorder level on most
   of them, this list can be longer than the item list it sits above. */
.lowbox{max-height:212px}
.lowbox table{margin:0}
.lowbox thead th{position:sticky;top:0;z-index:1;background:var(--panel)}
html[data-theme="light"] .lowbox thead th{background:#FFEBCB}
@media(max-width:860px){
  .lowbox{max-height:190px}
  /* The reorder level is reference, not a decision: the stock and the quantity
     to order are what matter, and dropping it keeps five columns off a phone. */
  #phLowBody table th:nth-child(4),
  #phLowBody table td:nth-child(4){display:none}
  #phLowBody table{font-size:11.5px}
  #phLowBody th,#phLowBody td{padding:6px 4px}
  #phLowBody input[type="number"]{width:56px;font-size:11.5px;padding:5px 4px}
  #phLowBody td:nth-child(2){overflow-wrap:anywhere}
}
@media(max-width:600px){
  /* Tighter still: the checkbox column shrinks to the box itself. */
  #phLowBody table th:first-child,
  #phLowBody table td:first-child{width:26px;padding-left:2px;padding-right:2px}
  #phLowBody input[type="number"]{width:50px}
}

/* --- Dialogs on a phone --- */
/* The order builder is the widest of them: supplier, search, and a table of
   every item with stock, reorder level, rate and quantity. At 620px wide with
   four numeric columns it ran off the screen. */
@media(max-width:860px){
  .modal-ov{align-items:flex-end;padding:0}
  .modal-bx{
    max-width:100% !important;width:100%;
    max-height:92vh;border-radius:14px 14px 0 0;
    padding:14px 12px calc(14px + env(safe-area-inset-bottom,0px));
  }
  .modal-hd .eyebrow{font-size:11px}
  /* Actions go full width and stack, so no button is half off the edge. */
  .modal-ft{flex-wrap:wrap;gap:6px;margin-top:12px}
  .modal-ft .spacer{display:none}
  .modal-ft .btn{flex:1 1 46%;min-width:0;padding:11px 8px}
  .modal-ft .btn.primary{flex:1 1 100%;order:-1}   /* the main action, first and wide */
  .modal-bx .fgrid{grid-template-columns:minmax(0,1fr)}
  .modal-bx .scrollbox{max-height:46vh}
  .modal-bx table{font-size:11.5px}
  .modal-bx th,.modal-bx td{padding:6px 4px}
  .modal-bx input[type="number"]{width:60px !important;font-size:11.5px;padding:6px 4px}
  .modal-bx td{overflow-wrap:anywhere}
}

/* The order builder: on a phone the decision is what to order and how much.
   Stock stays, because it is the reason for the number. */
@media(max-width:860px){
  #obTable th:nth-child(3),#obTable td:nth-child(3){display:none}  /* reorder at */
}
@media(max-width:600px){
  #obTable th:nth-child(4),#obTable td:nth-child(4){display:none}  /* rate */
  .modal-ft .btn{flex:1 1 100%}
}

/* --- Cafeteria item and category lists --- */
/* A hundred and twenty items and twenty categories, one above the other, made
   the pane longer than anything else in the app. Both scroll now, with the
   header pinned so the columns stay labelled. */
#miList,#mcList{max-height:260px}
#miList thead th,#mcList thead th{
  position:sticky;top:0;z-index:1;background:var(--panel);
}
html[data-theme="light"] #miList thead th,
html[data-theme="light"] #mcList thead th{background:#FFEBCB}
@media(max-width:860px){
  #miList,#mcList{max-height:220px}
  #miList table,#mcList table{font-size:11.5px}
  #miList th,#miList td,#mcList th,#mcList td{padding:6px 4px}
  #miList td,#mcList td{overflow-wrap:anywhere}
  /* Type is the least load-bearing column: it is Unit on every row of the
     uploaded list, so it distinguishes nothing at this width. */
  #miList table th:nth-child(2),#miList table td:nth-child(2){display:none}
  /* The order number is reference; the item and its count are the decision. */
  #mcList table th:nth-child(3),#mcList table td:nth-child(3){display:none}
  #miList .btn.sm,#mcList .btn.sm{padding:5px 7px;font-size:11px}
}
@media(max-width:600px){
  /* Category goes too: it is a heading in the list you are already looking at. */
  #miList table th:nth-child(3),#miList table td:nth-child(3){display:none}
}

/* --- Cafeteria lists on a phone: fit the screen, wrap the names --- */
@media(max-width:860px){
  /* The global mobile rule makes every table display:block with nowrap so wide
     ones can scroll. Both of these are narrow enough to fit once names wrap,
     so they go back to being tables -- a block box also sizes itself to its
     content, which is why the category table stopped filling the width. */
  #miList table,#mcList table{
    display:table;width:100%;table-layout:fixed;white-space:normal;
  }
  #miList,#mcList{overflow-x:hidden}

  /* One long product name was widening the whole table. Fixed layout plus
     wrapping keeps it to two or three lines in its own column instead. */
  #miList td:first-child,#mcList td:first-child{
    white-space:normal;overflow-wrap:anywhere;word-break:break-word;line-height:1.35;
  }

  /* Items: name takes what is left, the numbers get just enough. */
  #miList table th:first-child,#miList table td:first-child{width:auto}
  #miList table th:nth-child(4),#miList table td:nth-child(4){width:62px}  /* price */
  #miList table th:last-child,#miList table td:last-child{width:96px}      /* actions */

  /* Categories: name, count, actions. */
  #mcList table th:first-child,#mcList table td:first-child{width:auto}
  #mcList table th:nth-child(2),#mcList table td:nth-child(2){width:48px}
  #mcList table th:last-child,#mcList table td:last-child{width:104px}

  /* Buttons stack rather than forcing the column wider. */
  #miList td:last-child,#mcList td:last-child{white-space:normal}
  #miList td:last-child .btn,#mcList td:last-child .btn{margin:1px 0}
}
@media(max-width:600px){
  #miList table th:nth-child(4),#miList table td:nth-child(4){width:56px}
  #miList table th:last-child,#miList table td:last-child{width:80px}
  #mcList table th:last-child,#mcList table td:last-child{width:88px}
  #miList .btn.sm,#mcList .btn.sm{padding:5px 6px;font-size:10.5px}
}

/* --- Expenses --- */
/* A claim reads as a line: what it is, who gets paid, how much. The amount is
   the thing an approver scans for, so it sits alone on the right. */
.exrow{
  display:flex;align-items:center;gap:12px;
  padding:11px 12px;margin-bottom:6px;
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--glass);cursor:pointer;
}
.exrow:hover{border-color:var(--line-hi)}
.exrow-main{flex:1;min-width:0}
.exrow-top{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:3px}
.exno{font-family:var(--mono);font-size:10.5px;color:var(--muted)}
.exrow-desc{font-size:13.5px;line-height:1.35;overflow-wrap:anywhere}
.exrow-sub{font-size:11px;margin-top:2px;overflow-wrap:anywhere}
.exrow-amt{
  font-family:var(--display);font-size:16px;white-space:nowrap;flex:0 0 auto;
}
.exchip{
  font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;
  border:1px solid var(--line);border-radius:999px;padding:2px 8px;white-space:nowrap;
}
.exchip.pos{border-color:rgba(46,160,67,.4)}
.exchip.warn{border-color:rgba(245,166,35,.45)}
.exchip.neg{border-color:rgba(232,83,60,.45)}

.exhead{display:flex;align-items:center;gap:12px;margin-bottom:4px}
.examt{font-family:var(--display);font-size:26px;flex:1}
.exdesc{font-size:14px;line-height:1.45}
.exbank{width:100%;font-size:13px}
.exbank td{padding:4px 6px}
.exbank td:first-child{width:96px}

/* Attachments. A bill is looked at, not read in place, so the tile is a
   thumbnail that opens full size in a new tab. */
.exfiles{display:flex;flex-wrap:wrap;gap:8px}
.exfile{
  width:110px;border:1px solid var(--line);border-radius:var(--r-md);
  padding:6px;text-align:center;
}
.exfile img{width:100%;height:78px;object-fit:cover;border-radius:4px;display:block}
.exfile-doc{
  display:flex;align-items:center;justify-content:center;height:78px;
  background:var(--glass);border-radius:4px;font-family:var(--display);font-size:13px;
}
.exfile-cap{font-size:9.5px;color:var(--muted);margin:4px 0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.extrail{border-left:2px solid var(--line);padding-left:10px}
.extrail-row{display:flex;gap:10px;font-size:11.5px;margin-bottom:5px}
.extrail-row span:first-child{flex:0 0 96px;font-family:var(--mono);font-size:10.5px}

/* Counts. On the module tab so work waiting is visible without opening it,
   and beside a pane name so it is clear which queue it is in. */
.modbadge{
  display:inline-block;margin-left:6px;min-width:17px;padding:0 5px;
  border-radius:999px;background:var(--grad);color:#fff;
  font-family:var(--mono);font-size:10px;line-height:17px;text-align:center;
}
.panecount{
  display:inline-block;margin-left:6px;min-width:16px;padding:0 5px;
  border-radius:999px;background:var(--glass);border:1px solid var(--line-hi);
  font-family:var(--mono);font-size:10px;line-height:15px;
}

@media(max-width:860px){
  .exrow{padding:10px;gap:8px}
  .exrow-amt{font-size:14px}
  .examt{font-size:22px}
  .exfile{width:calc(33.333% - 6px)}
  .extrail-row{flex-direction:column;gap:1px}
  .extrail-row span:first-child{flex:none}
  .exbank td:first-child{width:80px}
}

/* --- UPI QR --- */
/* Shown, not linked: it is the thing the money is sent against, and an
   accountant should not have to open a tab to check it. */
.qrbox{display:inline-flex;flex-direction:column;align-items:flex-start;gap:6px}
.qrbox img{
  width:150px;height:150px;object-fit:contain;background:#fff;
  border:1px solid var(--line);border-radius:var(--r-md);padding:6px;
}
@media(max-width:860px){ .qrbox img{width:128px;height:128px} }

/* --- Badges: a task has a number, news has a dot --- */
.navbadge{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:18px;height:18px;padding:0 5px;margin-left:auto;
  border-radius:999px;background:rgba(232,83,60,.92);color:#fff;
  font-family:var(--mono);font-size:10px;font-weight:700;
}
.navdot,.moddot{
  display:inline-block;width:7px;height:7px;border-radius:50%;
  background:var(--gold);margin-left:7px;vertical-align:middle;
}
.subnav-btn{display:flex;align-items:center;gap:6px}
@media(max-width:860px){
  .subnav-btn{display:inline-flex}
  .navbadge{margin-left:6px;min-width:16px;height:16px;font-size:9.5px}
}

/* --- Payment details, locked until deliberately edited --- */
.exbankwrap.locked input{
  background:transparent;border-color:var(--line);color:var(--muted);cursor:default;
}
.exbankwrap.locked input:focus{outline:none;border-color:var(--line)}

/* --- Expense analytics --- */
/* A bar against the largest row reads faster than a column of numbers, and
   needs no charting library. */
.exbar{display:grid;grid-template-columns:minmax(0,1fr) 1fr auto;gap:8px;
  align-items:center;margin-bottom:6px;font-size:12px}
.exbar-l{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.exbar-t{height:7px;border-radius:999px;background:var(--glass);overflow:hidden}
.exbar-f{height:100%;background:var(--grad);border-radius:999px}
.exbar-v{font-family:var(--mono);font-size:11px;white-space:nowrap}
.exagerow{cursor:pointer}
.exagerow:hover td{background:var(--glass)}
@media(max-width:860px){
  .exbar{grid-template-columns:minmax(0,1fr) auto;gap:6px}
  .exbar-t{grid-column:1/-1;order:3}
  .exbar-v{font-size:10.5px}
}

/* --- Expenses on a phone --- */
/* Six screens, each with its own shape. The left panel is not shown at this
   width, so the pane row inside the view is the navigation -- it has to scroll
   sideways rather than wrap into three ragged lines. */
@media(max-width:860px){
  #view-expenses .stwrap{grid-template-columns:minmax(0,1fr);gap:0}

  /* Cards were sized for two columns and left half the screen empty. */
  #view-expenses .insgrid{grid-template-columns:minmax(0,1fr);gap:10px}
  #view-expenses .card{padding:12px 11px}
  #view-expenses .sumbar{gap:8px}
  #view-expenses .sumcell{flex:1 1 44%;min-width:0}
  #view-expenses .sumcell .v{font-size:16px}

  /* A claim row: description takes the width, amount and status stack right. */
  .exrow{padding:10px;gap:9px}
  .exdesc{font-size:12.5px}
  .exmeta{font-size:10.5px}
  .examt{font-size:13px}
  .exchip{font-size:9.5px;padding:2px 7px}

  /* Tables inside the panes: fit rather than scroll sideways. */
  #view-expenses table{display:table;width:100%;table-layout:fixed;white-space:normal}
  #view-expenses .scrollbox{overflow-x:hidden;max-height:none}
  #view-expenses th,#view-expenses td{padding:6px 4px;font-size:11.5px;
    overflow-wrap:anywhere}
  #view-expenses td .btn.sm{padding:5px 7px;font-size:10.5px;margin:1px 0}

  /* Vendors: name and the buttons, nothing else earns the width. */
  #exVendBody table th:nth-child(2),#exVendBody table td:nth-child(2),
  #exVendBody table th:nth-child(4),#exVendBody table td:nth-child(4){display:none}
  #exVendBody table th:last-child,#exVendBody table td:last-child{width:104px}

  /* Categories: name, count, actions. */
  #exCatBody table th:last-child,#exCatBody table td:last-child{width:112px}
  #exCatBody table th:nth-child(2),#exCatBody table td:nth-child(2){width:52px}

  /* Waiting longest: the payee is the least useful column on a phone. */
  #exrAgeing table th:nth-child(2),#exrAgeing table td:nth-child(2){display:none}
  #exrAgeing table th:last-child,#exrAgeing table td:last-child{width:44px}

  /* The date filters and the quick ranges. */
  #view-expenses .filterbar{flex-wrap:wrap;gap:6px;padding:8px}
  #view-expenses .filterbar input[type="date"]{flex:1 1 40%;min-width:0;font-size:12px}
  #view-expenses .csquick{overflow-x:auto;flex-wrap:nowrap;scrollbar-width:none}
  #view-expenses .csquick::-webkit-scrollbar{display:none}
  #view-expenses .csquick .btn{flex:0 0 auto;font-size:11.5px;padding:6px 10px}

  /* Vendor history opens as a sheet like every other dialog. */
  #vhBody{max-height:52vh}
  #vhBody table th:nth-child(2),#vhBody table td:nth-child(2){display:none}
}

@media(max-width:600px){
  #view-expenses .sumcell{flex:1 1 100%}
  #view-expenses .exbar{grid-template-columns:minmax(0,1fr) auto}
  /* Claim number and branch are enough; the status word repeats the colour. */
  #exrAgeing table th:nth-child(3),#exrAgeing table td:nth-child(3){display:none}
}

/* --- The claim, read top down --- */
.exhead{display:flex;align-items:center;gap:10px;justify-content:space-between}
.exdesc2{font-size:15px;line-height:1.4;margin-top:2px;overflow-wrap:anywhere}
.exkv{width:100%;font-size:13px}
.exkv td{padding:4px 6px;vertical-align:top}
.exkv td:first-child{color:var(--muted);width:96px}

/* --- Claim detail: the upload row and what follows it --- */
.exuprow{display:block;margin-top:6px}
.exuprow input[type="file"]{display:block;width:100%}
/* Nothing may sit beside the history: it is the last thing read and belongs
   on its own line whatever came before it. */
.exclear{clear:both;display:block;width:100%}
.extrail{clear:both;display:block;margin-top:4px}
.extrail-row{display:flex;gap:8px;font-size:11.5px;margin-bottom:5px;
  align-items:baseline;flex-wrap:wrap}
.extrail-row .dim{flex:0 0 auto;font-family:var(--mono)}

/* --- Branch strip above a queue --- */
/* Each button carries what is waiting on that branch: how many, how much, and
   a mark when the oldest has been sitting too long. The point is to be able to
   choose where to start without opening anything. */
.exstrip{
  display:flex;gap:8px;overflow-x:auto;padding:2px 2px 10px;
  scrollbar-width:none;
}
.exstrip::-webkit-scrollbar{display:none}
.exchipb{
  flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-start;gap:2px;
  min-width:104px;padding:9px 13px;cursor:pointer;text-align:left;
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--glass);color:var(--muted);font:inherit;
  transition:border-color .12s ease, transform .12s ease;
}
.exchipb:hover{border-color:var(--line-hi);transform:translateY(-1px)}
.exchipb .nm{
  font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  font-family:var(--display);white-space:nowrap;
}
.exchipb .ct{font-family:var(--mono);font-size:19px;font-weight:700;color:var(--paper);line-height:1.05}
.exchipb .amt{font-family:var(--mono);font-size:10.5px;opacity:.75}
.exchipb .old{
  font-family:var(--mono);font-size:9.5px;padding:1px 6px;border-radius:999px;
  background:rgba(232,83,60,.18);color:#FF8A73;margin-top:2px;
}
/* Selected: the gradient does the work, so no second signal is needed. */
.exchipb.on{
  border-color:transparent;color:#fff;
  background:var(--grad);
}
.exchipb.on .ct,.exchipb.on .amt{color:#fff}
.exchipb.on .old{background:rgba(255,255,255,.22);color:#fff}
/* Nothing waiting: still selectable, just not asking for attention. */
.exchipb.empty{opacity:.45}
.exchipb.empty .nm{font-size:10.5px}
@media(max-width:860px){
  .exchipb{min-width:88px;padding:8px 11px}
  .exchipb .ct{font-size:16px}
  .exchipb .nm{font-size:10px}
}

/* --- Report filter row --- */
#expane-reports .filterbar select{
  font-size:12px;padding:6px 8px;min-width:120px;max-width:190px;
}
@media(max-width:860px){
  #expane-reports .filterbar{flex-wrap:wrap}
  #expane-reports .filterbar select{flex:1 1 46%;min-width:0;max-width:none}
  #expane-reports .filterbar input[type="date"]{flex:1 1 46%}
}

/* --- The gate desk --- */
/* Used standing up, on a phone, with a queue waiting. Big targets, one thing
   at a time, and a result visible from arm's length. */
.gtempty{padding:26px 14px;text-align:center}
.gtempty-t{font-family:var(--display);font-size:17px;margin-bottom:4px}
.gtempty-s{font-size:12.5px;color:var(--muted)}

.gtperson{display:flex;gap:12px;align-items:center;margin-bottom:10px}
.gtface{width:64px;height:64px;border-radius:12px;object-fit:cover;flex:0 0 auto;
  border:1px solid var(--line)}
.gtface-none{display:flex;align-items:center;justify-content:center;
  background:var(--glass);font-family:var(--display);font-size:24px;color:var(--muted)}
.gtwho{min-width:0}
.gtwho .nm{font-size:17px;font-family:var(--display);overflow-wrap:anywhere}
.gttags{display:flex;flex-wrap:wrap;gap:5px;margin-top:5px}
.gttag{font-size:10px;letter-spacing:.05em;text-transform:uppercase;
  padding:2px 8px;border-radius:999px;border:1px solid var(--line);color:var(--muted)}
.gttag.ok{border-color:rgba(74,163,106,.5);color:#7BC894}
.gttag.out{border-color:rgba(232,83,60,.5);color:#FF8A73}
.gttag.in{border-color:rgba(74,163,106,.35)}

.gtnote{font-size:12.5px;color:var(--muted);border-left:2px solid var(--line);
  padding:6px 10px;margin:10px 0;line-height:1.5}
.gtnote.ok{border-color:rgba(74,163,106,.6)}
.gtnote.warn{border-color:rgba(212,160,23,.7);color:var(--paper)}

/* --- A row of exclusive choices --- */
/* The same shape as .gtpass, which the gate has used for a year, but not
   scoped to the gate. Two answers to one question read as radios on
   sight; a segmented control reads as a filter, which is why the pay
   cycle was being missed on the expense form. */
.radrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}
.rad{
  display:flex;gap:8px;align-items:center;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-md);
  padding:9px 12px;font-size:13px;flex:1 1 220px;
}
.rad input{width:auto;margin:0;flex:0 0 auto}
.rad:has(input:checked){border-color:var(--gold);background:var(--glass)}
@media(max-width:520px){ .rad{flex:1 1 100%} }

.gtpasses{display:flex;flex-direction:column;gap:6px;margin-top:10px}
.gtpass{display:flex;flex-wrap:wrap;gap:8px;align-items:center;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-md);padding:9px 11px;font-size:12.5px}
.gtpass.on{border-color:var(--gold);background:var(--glass)}
.gtpass input{width:auto;margin:0}
.gtpass .pr{flex:1;min-width:0}
.gtpass .pc,.gtpass .pb{font-size:11px;color:var(--muted)}

.gtphoto{display:flex;gap:12px;align-items:center;margin-top:12px}
.gtphoto-box{width:96px;height:96px;border-radius:10px;border:1px dashed var(--line);
  display:flex;align-items:center;justify-content:center;overflow:hidden;flex:0 0 auto;
  font-size:11px;text-align:center;padding:6px}
.gtphoto-box img{width:100%;height:100%;object-fit:cover}
.gtsnap{cursor:pointer}

.gtact{display:flex;gap:8px;margin-top:14px}
.gtbig{flex:1;padding:15px;font-size:15px}

/* The result. Large enough to read without leaning in, gone in four seconds. */
.gtdone{padding:30px 16px;text-align:center;border-radius:var(--r-lg)}
.gtdone.out{background:rgba(232,83,60,.12);border:1px solid rgba(232,83,60,.35)}
.gtdone.in{background:rgba(74,163,106,.12);border:1px solid rgba(74,163,106,.35)}
.gtdone-t{font-family:var(--display);font-size:13px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted)}
.gtdone-n{font-size:24px;font-family:var(--display);margin:6px 0 2px;overflow-wrap:anywhere}
.gtdone-s{font-size:12px;color:var(--muted)}

.gtresults{display:flex;flex-direction:column;gap:5px;margin-top:8px}
.gtres{display:flex;flex-direction:column;align-items:flex-start;gap:1px;
  text-align:left;padding:9px 11px;border-radius:var(--r-md);
  border:1px solid var(--line);background:var(--glass);cursor:pointer;font:inherit}
.gtres:hover{border-color:var(--line-hi)}
.gtres .nm{font-size:13px;color:var(--paper)}
.gtres .dim{font-size:11px}

.gtslot{border:1px solid var(--line);border-radius:var(--r-md);padding:11px;margin-bottom:9px}
.gtslot-h{font-family:var(--display);font-size:13px;margin-bottom:8px}
.gtdecide{display:flex;gap:6px}
.gtlate{border-color:rgba(232,83,60,.5)}

@media(max-width:860px){
  .gtface{width:56px;height:56px}
  .gtwho .nm{font-size:15px}
  .gtbig{padding:14px;font-size:14px}
  .gtdone-n{font-size:20px}
  .gtact{flex-direction:column-reverse}
  .gtact .btn{width:100%}
  .gtphoto-box{width:82px;height:82px}
  #view-gate .stwrap{grid-template-columns:minmax(0,1fr)}
}

/* --- Gatepass: the person chosen --- */
.gtpicked{
  margin-top:6px;padding:8px 11px;border-radius:var(--r-md);
  border:1px solid var(--gold);background:var(--glass);font-size:12.5px;
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
}
.gtpicked b{overflow-wrap:anywhere}
#npHits{max-height:200px;overflow-y:auto}

/* --- Camera --- */
.camstage{
  position:relative;background:#000;border-radius:var(--r-md);overflow:hidden;
  aspect-ratio:4/3;display:flex;align-items:center;justify-content:center;
}
.camstage video,.camstage img{width:100%;height:100%;object-fit:cover;display:block}
.camwrap .modal-ft{gap:6px}
.gtphoto-btns{display:flex;flex-direction:column;gap:6px}
.gtphoto-btns .btn{white-space:nowrap}
@media(max-width:860px){
  .camstage{aspect-ratio:3/4}
  .gtphoto{flex-direction:column;align-items:stretch}
  .gtphoto-box{width:100%;height:170px}
  .gtphoto-btns{flex-direction:row}
  .gtphoto-btns .btn{flex:1;text-align:center}
}

/* --- Security bar: one box, the buttons beside it --- */
.gtbar{display:flex;gap:6px;align-items:center;margin-bottom:10px;flex-wrap:wrap}
.gtbar input{flex:1 1 200px;min-width:0}
.gtbar .btn[aria-pressed="true"]{background:var(--grad);color:#fff;border-color:transparent}

/* --- Gatepass history --- */
.gthist{width:100%;font-size:12.5px;margin-bottom:16px}
.gthist th,.gthist td{padding:6px 8px}
.gthist-m{
  font-family:var(--display);font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);text-align:left;padding-top:2px;
}
.gthist tfoot td{border-top:1px solid var(--line-hi)}
/* A number that opens something should look like it does. */
.gtnum{
  font-family:var(--mono);font-size:13px;font-weight:600;color:var(--paper);
  background:none;border:none;border-bottom:1px dashed var(--line-hi);
  padding:1px 2px;cursor:pointer;
}
.gtnum:hover{color:var(--gold);border-bottom-color:var(--gold)}
.gthlist{width:100%;font-size:12px}
.gthlist th,.gthlist td{padding:6px 7px;vertical-align:top}
.gthlist tr.haspic{cursor:pointer}
.gthlist tr.haspic:hover td{background:var(--glass)}

@media(max-width:860px){
  .gtbar input{flex:1 1 100%}
  .gtbar .btn{flex:1}
  .gthist{font-size:11.5px}
  .gthist th,.gthist td{padding:5px 4px}
  /* No pass needed is a detail; the three that matter stay. */
  .gthist th:nth-child(4),.gthist td:nth-child(4){display:none}
  .gthlist{display:table;width:100%;table-layout:fixed;white-space:normal}
  .gthlist th,.gthlist td{padding:5px 4px;font-size:11px;overflow-wrap:anywhere}
  /* Transport and reason go; who took them and when does not. */
  .gthlist th:nth-child(4),.gthlist td:nth-child(4),
  .gthlist th:nth-child(7),.gthlist td:nth-child(7){display:none}
}

/* --- Permanent pass picker --- */
.gptbl{width:100%;font-size:12.5px}
.gptbl th,.gptbl td{padding:6px 8px;vertical-align:top}
.gptbl th:first-child,.gptbl td:first-child{width:32px}
#gpBody{max-height:340px}
@media(max-width:860px){
  .gptbl{display:table;width:100%;table-layout:fixed;white-space:normal}
  .gptbl th,.gptbl td{padding:5px 4px;font-size:11.5px;overflow-wrap:anywhere}
  .gptbl th:first-child,.gptbl td:first-child{width:26px;padding-left:2px}
  #gpBody{max-height:300px}
  #gtpane-slots .filterbar select,#gtpane-slots .filterbar input{flex:1 1 46%;min-width:0}
  #gtpane-slots .qrbar .btn{flex:1 1 46%}
}

/* --- The gate, in read-only --- */
/* Security compares two faces and taps once. Both photographs are large enough
   to compare at a glance, because that is the entire job. */
.gtcollect{display:flex;gap:12px;align-items:flex-start;margin:10px 0}
.gtcol-pic{flex:0 0 auto;width:104px;height:104px;border-radius:10px;overflow:hidden;
  border:1px solid var(--line);background:var(--glass)}
.gtcol-pic img{width:100%;height:100%;object-fit:cover;display:block}
.gtcol-none{width:100%;height:100%;display:flex;align-items:center;justify-content:center;
  text-align:center;font-size:11px;color:var(--muted);line-height:1.4}
.gtcol-who{min-width:0}
.gtcol-who .lbl{font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);margin-bottom:2px}
.gtcol-who .nm{font-size:16px;font-family:var(--display);overflow-wrap:anywhere}
@media(max-width:860px){
  .gtcol-pic{width:88px;height:88px}
  .gtcol-who .nm{font-size:15px}
}

/* --- Security desk --- */
/* A guard matches a face on a screen to a face in front of them, at arm's
   length, in daylight. The photographs are the screen; the words are a caption. */
.sdhead{display:flex;gap:14px;align-items:center;margin-bottom:12px}
.sdface{
  width:108px;height:108px;border-radius:14px;object-fit:cover;flex:0 0 auto;
  border:1px solid var(--line);background:var(--glass);display:block;
}
.sdface.none{
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-size:38px;color:var(--muted);text-align:center;
}
.sdface.nophoto{font-size:12px;line-height:1.4}
.sdwho{min-width:0}
.sdname{font-family:var(--display);font-size:22px;line-height:1.15;overflow-wrap:anywhere}
.sdmeta{font-size:13px;color:var(--paper);margin-top:3px}
.sdchips{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}

/* Two faces, the same size, side by side. */
.sdpair{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.sdcard{flex:1;min-width:0;margin:0;text-align:center;
  border:1px solid var(--line);border-radius:var(--r-md);padding:10px}
.sdcard.warn{border-color:rgba(212,160,23,.6)}
.sdcard .sdface{width:100%;height:auto;aspect-ratio:1;margin:0 auto}
.sdcard figcaption{font-size:12.5px;margin-top:8px;overflow-wrap:anywhere;line-height:1.4}
.sdarrow{flex:0 0 auto;color:var(--muted);font-size:20px}

.sdstate{
  font-family:var(--display);font-size:13px;letter-spacing:.04em;
  padding:9px 12px;border-radius:var(--r-md);margin-bottom:10px;
  border:1px solid var(--line);color:var(--muted);
}
.sdstate.ok{border-color:rgba(74,163,106,.5);color:#7BC894}
.sdstate.in{border-color:rgba(212,160,23,.5);color:#E8C46A}
.sdstate.no{border-color:rgba(232,83,60,.5);color:#FF8A73}

.sdreason{border-left:2px solid var(--line);padding:6px 12px;margin:10px 0;font-size:13px}
.sdreason .lbl{font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);margin-bottom:2px}

/* --- History rows --- */
.gtclick{cursor:pointer}
.gtclick:hover td{background:var(--glass)}
/* A permanent pass explains why a row may have no collector against it. */
.gtpassname{color:var(--gold);font-weight:600}

/* --- One record, in full --- */
.mvfaces{display:flex;gap:10px;margin-bottom:12px}
.mvfaces figure{flex:1;min-width:0;margin:0;text-align:center}
.mvface{width:100%;height:auto;aspect-ratio:1;object-fit:cover;border-radius:12px;
  border:1px solid var(--line);background:var(--glass);display:block}
.mvface.none{display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-size:30px;color:var(--muted)}
.mvfaces figcaption{font-size:12px;margin-top:6px;color:var(--muted);
  overflow-wrap:anywhere}

@media(max-width:860px){
  .sdface{width:88px;height:88px}
  .sdname{font-size:19px}
  .sdpair{gap:6px}
  .sdarrow{display:none}
  .sdcard{padding:8px}
  .sdcard figcaption{font-size:11.5px}
  /* Category and transport are on the record dialog; the list keeps the
     columns a guard or an office actually scans. */
  .gthlist th:nth-child(3),.gthlist td:nth-child(3){display:none}
}

/* --- One gate table, two halves --- */
/* The halves count different events, so a rule between them says so without a
   sentence: passes on the left, what happened at the gate on the right. */
.gthist2 .gthist-grp th{
  font-family:var(--display);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);padding-bottom:2px;text-align:center;font-weight:400;
}
.gthist2 .gthist-grp th:nth-child(3){border-left:1px solid var(--line-hi)}
.gthist2 th.sep,.gthist2 td.sep{border-left:1px solid var(--line-hi)}
@media(max-width:860px){
  /* Waiting and No pass are the two that can go: both are visible in the
     lists behind the numbers that remain. */
  .gthist2 th:nth-child(4),.gthist2 td:nth-child(4),
  .gthist2 th:nth-child(7),.gthist2 td:nth-child(7){display:none}
  .gthist2 .gthist-grp th:nth-child(2){display:none}
}

/* --- Approved and waiting --- */
/* Two columns, because day scholars leave all at once and a single hosteller
   going to a doctor should not be lost in the middle of that. */
.gtready{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.gtready-col{border:1px solid var(--line);border-radius:var(--r-md);padding:10px;min-width:0}
.gtready-h{
  font-family:var(--display);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);margin-bottom:8px;
}
.rdy{
  display:flex;flex-direction:column;align-items:flex-start;gap:1px;width:100%;
  text-align:left;padding:9px 11px;margin-bottom:6px;cursor:pointer;font:inherit;
  border:1px solid var(--line);border-radius:var(--r-md);background:var(--glass);
}
.rdy:hover{border-color:var(--gold)}
.rdy-n{font-size:13.5px;color:var(--paper);overflow-wrap:anywhere}
.rdy-m{font-size:11px;color:var(--muted);overflow-wrap:anywhere}
.rdy-r{font-size:11px;color:var(--muted);overflow-wrap:anywhere}
/* Approved hours ago and still not used. */
.rdy.stale{border-left:2px solid rgba(212,160,23,.8)}
@media(max-width:860px){
  .gtready{grid-template-columns:minmax(0,1fr);gap:10px}
  .rdy{padding:10px}
  .rdy-n{font-size:13px}
}

/* --- Two faces, at a size that fits a screen --- */
/* aspect-ratio:1 on a full-width card made these enormous on a laptop: half a
   1000px dialog is a 500px photograph. Capped, and centred in the card. */
.sdcard .sdface{
  width:100%;max-width:180px;height:auto;aspect-ratio:1;margin:0 auto;
}
.sdpair{align-items:stretch}
.sdcard{display:flex;flex-direction:column;justify-content:flex-start}
.mvface{max-width:190px;margin:0 auto}
@media(max-width:860px){
  .sdcard .sdface{max-width:140px}
  .mvface{max-width:150px}
}
@media(max-width:430px){
  .sdcard .sdface{max-width:118px}
}

/* --- A face on the waiting card --- */
.rdy-top{display:flex;align-items:center;gap:8px;width:100%;min-width:0}
.rdy-face{width:34px;height:34px;border-radius:8px;object-fit:cover;flex:0 0 auto;
  border:1px solid var(--line);background:var(--glass)}
.rdy-face.none{display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-size:15px;color:var(--muted)}
.rdy-top .rdy-n{min-width:0;overflow-wrap:anywhere}

/* --- Entry attendance --- */
/* A row nobody scanned in is the point of the screen, so it is marked rather
   than left to be found by reading the In column. */
.gtmissing td{background:rgba(212,160,23,.07)}
.gtmissing td:first-child{box-shadow:inset 2px 0 0 rgba(212,160,23,.7)}
@media(max-width:860px){
  #gtpane-entry .filterbar select,#gtpane-entry .filterbar input{flex:1 1 46%;min-width:0}
  /* Branch and transport go; who arrived and when does not. */
  #gaBody table th:nth-child(3),#gaBody table td:nth-child(3),
  #gaBody table th:nth-child(4),#gaBody table td:nth-child(4){display:none}
}

/* --- The gate's own session --- */
.gtsess{
  font-family:var(--display);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  padding:5px 12px;border-radius:999px;border:1px solid var(--line);color:var(--muted);
}
.gtsess.on{background:var(--grad);color:#fff;border-color:transparent}
/* A scan that meant nothing: neither a success nor an error, and it should
   not look like either. */
.gtdone.none{background:var(--glass);border:1px solid var(--line)}

/* --- Lookup: where they are --- */
/* The one line somebody scanning a card at a gate actually wants, so it sits
   above the details rather than inside the table with them. */
.lkwhere{
  display:inline-block;margin-top:6px;padding:5px 11px;border-radius:999px;
  font-size:12px;border:1px solid var(--line);color:var(--muted);
}
.lkwhere.in{border-color:rgba(74,163,106,.5);color:#7BC894}
.lkwhere.out{border-color:rgba(232,83,60,.5);color:#FF8A73}
.lkpass{
  margin-top:10px;padding:8px 11px;border-left:2px solid var(--gold);
  background:var(--glass);border-radius:0 var(--r-md) var(--r-md) 0;font-size:12.5px;
}
.lkpass .lbl{font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);margin-bottom:3px}

/* --- One navigation, on every screen size --- */
/* On a phone the left panel becomes a scrolling strip of pills. It is built
   from the access matrix, so it shows only the panes somebody actually holds
   -- which the static rows inside each view never did. */
@media(max-width:860px){
  .subnav{
    display:flex;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;
    gap:5px;padding:8px 0 10px;margin:0;border:none;min-height:0;
    scrollbar-width:none;position:sticky;top:0;z-index:20;background:var(--depth);
  }
  .subnav::-webkit-scrollbar{display:none}
  .subnav-btn{
    flex:0 0 auto;white-space:nowrap;border-radius:999px;
    border:1px solid var(--line);background:var(--glass);
    padding:7px 13px;font-size:12px;
  }
  .subnav-btn.on{background:var(--grad);color:#fff;border-color:transparent}
  .subnav-btn .navbadge{margin-left:6px;min-width:16px;height:16px;font-size:9.5px}
}