/* ============================================================
   DITUCALAN MERCHANDISING CORP. — HRMS v2
   Enterprise UI · Brand #030083
   ============================================================ */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  /* Brand */
  --c-brand:        #030083;
  --c-brand-hover:  #0400a0;
  --c-brand-active: #020062;
  --c-brand-10:     rgba(3,0,131,0.08);
  --c-brand-20:     rgba(3,0,131,0.15);

  /* Surfaces */
  --c-white:        #ffffff;
  --c-bg:           #eef0f7;
  --c-surface:      #ffffff;
  --c-surface-2:    #f5f6fb;

  /* Borders */
  --c-border:       #dde0ef;
  --c-border-2:     #c8cce0;

  /* Text */
  --c-text:         #080920;
  --c-text-2:       #363856;
  --c-text-3:       #686b8c;   /* was #7a7d9e: 3.5:1 on --c-bg, under AA for 11-12px text; now 4.5 / 5.2 on white */
  --c-text-inv:     #ffffff;

  /* State */
  --c-success:      #0f7b41;
  --c-success-bg:   #e6f4ed;
  --c-warning:      #b45309;
  --c-warning-bg:   #fef3e2;
  --c-danger:       #c0202c;
  --c-danger-bg:    #fbeaea;
  --c-info:         #1d5fad;
  --c-info-bg:      #e8f0fb;

  /* Layout */
  --sidebar-w:       252px;
  --sidebar-w-icon:  64px;
  --topbar-h:        58px;

  /* Misc */
  --radius-sm:  4px;
  --radius:     8px;
  --radius-lg:  12px;
  --shadow-sm:  0 1px 3px rgba(3,0,131,0.07), 0 1px 2px rgba(3,0,131,0.04);
  --shadow:     0 4px 12px rgba(3,0,131,0.08), 0 1px 3px rgba(3,0,131,0.05);
  --shadow-lg:  0 12px 40px rgba(3,0,131,0.12), 0 4px 12px rgba(3,0,131,0.06);
  --transition: 150ms ease;
}

/* ── BASE ───────────────────────────────────────────── */
html { font-size: 14px; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }

body {
  font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
  background: var(--c-bg);
  color: var(--c-text);
  line-height: 1.5;
  min-height: 100vh;
}

body.auth-mode { background: var(--c-white); }

/* ── TYPOGRAPHY ─────────────────────────────────────── */
.t-display {
  font-family: 'Plus Jakarta Sans', 'DM Sans', sans-serif;
  font-weight: 800;
  letter-spacing: -0.025em;
}
.t-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--c-text-3);
}
.t-mono {
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: 12px;
}

/* ── SCROLLBAR ──────────────────────────────────────── */
::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--c-border-2); border-radius: 2px; }
::-webkit-scrollbar-thumb:hover { background: var(--c-text-3); }

/* ============================================================
   LOGIN — SPLIT PANEL
   ============================================================ */
.login-root {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100vh;
}

/* LEFT — brand panel */
.login-brand-panel {
  background: var(--c-brand);
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 52px 60px;
}

/* D-mark watermark pattern — signature element */
.login-brand-panel::before {
  content: '';
  position: absolute;
  bottom: -100px;
  right: -120px;
  width: 520px;
  height: 520px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M10 10h28c22.09 0 40 17.91 40 40s-17.91 40-40 40H10V10z' fill='none' stroke='rgba(255,255,255,0.04)' stroke-width='2'/%3E%3Crect x='10' y='38' width='22' height='24' rx='2' fill='none' stroke='rgba(255,255,255,0.06)' stroke-width='2'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  pointer-events: none;
}
.login-brand-panel::after {
  content: '';
  position: absolute;
  top: -80px;
  left: -100px;
  width: 360px;
  height: 360px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath d='M10 10h28c22.09 0 40 17.91 40 40s-17.91 40-40 40H10V10z' fill='none' stroke='rgba(255,255,255,0.03)' stroke-width='2'/%3E%3Crect x='10' y='38' width='22' height='24' rx='2' fill='none' stroke='rgba(255,255,255,0.04)' stroke-width='2'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  pointer-events: none;
}


.login-brand-logo {
  position: relative;
  z-index: 1;
}

/* Horizontal white logo on dark panel */
.login-brand-logo-img {
  height: 36px;
  width: auto;
  display: block;
  filter: brightness(0) invert(1);
  opacity: 0.92;
  margin-bottom: 0;
}

/* Hide the old text fallback — replaced by real logo img */
.login-corp-name { display: none; }
.login-corp-sub  { margin-top: 14px; font-size: 13.5px; font-weight: 500;
                   color: rgba(255,255,255,0.62); letter-spacing: .01em; }
.login-brand-mark { display: none; }



/* RIGHT — form panel */
.login-form-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 52px 60px;
  background: var(--c-white);
}

.login-form-inner { width: 100%; max-width: 360px; }

/* Small stacked logo on form side */
.login-form-logo-wrap {
  margin-bottom: 36px;
}
.login-form-logo-img {
  height: 52px;
  width: auto;
  display: block;
}

.login-form-title {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 800;
  font-size: 26px;
  color: var(--c-text);
  letter-spacing: -0.025em;
  margin-bottom: 4px;
}

/* ── FORM ELEMENTS ──────────────────────────────────── */
.field, .form-group { margin-bottom: 18px; }
.field-label, .form-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--c-text-2);
  margin-bottom: 6px;
  letter-spacing: 0.01em;
}
/* Add Employee has 35 fields and 7 must be filled; nothing said which until
   Save refused. Any form field marked required gets the mark, so a new form
   needs nothing extra. Scoped to pop-ups: the sign-in form's two fields are
   obviously both needed. */
.modal .field:has([required]) > .field-label::after { content: " *"; color: var(--c-danger); font-weight: 700; }
.field-ctrl {
  position: relative;
  display: flex;
  align-items: center;
}
.field-ctrl i {
  position: absolute;
  left: 12px;
  font-size: 15px;
  color: var(--c-text-3);
  pointer-events: none;
}
.field-input {
  width: 100%;
  height: 40px;
  border: 1.5px solid var(--c-border);
  border-radius: var(--radius);
  padding: 0 12px 0 38px;
  font-size: 13px;
  font-family: inherit;
  color: var(--c-text);
  background: var(--c-white);
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}
.field-input.no-icon { padding-left: 12px; }
.field-input:focus {
  border-color: var(--c-brand);
  box-shadow: 0 0 0 3px var(--c-brand-10);
}
.field-input::placeholder { color: var(--c-text-3); }
textarea.field-input {
  height: 80px;
  padding-top: 10px;
  padding-left: 12px;   /* no textarea carries a leading icon; the 38px icon inset pushed every placeholder in */
  resize: vertical;
}
select.field-input {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237c7fa0' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
  cursor: pointer;
}

/* ── BUTTONS ────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 0 16px;
  height: 36px;
  box-sizing: border-box;   /* padding must not add to the width */
  max-width: 100%;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  border: none;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
  white-space: nowrap;
  text-decoration: none;
}
.btn i { font-size: 14px; }
.btn-sm { height: 30px; padding: 0 12px; font-size: 12px; }
.btn-lg { height: 44px; padding: 0 20px; font-size: 14px; font-weight: 700; }
.btn-full { display: flex; width: 100%; box-sizing: border-box; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }

.btn-primary { background: var(--c-brand); color: #fff; }
.btn-primary:hover { background: var(--c-brand-hover); box-shadow: 0 4px 14px rgba(3,0,131,0.28); }
.btn-primary:active { background: var(--c-brand-active); box-shadow: none; }

/* Secondary buttons: a white face and a visible edge. Transparent with the
   #dde0ef border, they all but vanished on the #eef0f7 page. */
.btn-ghost, .btn-secondary {
  background: var(--c-surface);
  color: var(--c-text);
  border: 1.5px solid var(--c-border-2);
  box-shadow: 0 1px 2px rgba(8,9,32,0.05);
}
.btn-ghost:hover, .btn-secondary:hover { background: var(--c-surface-2); border-color: var(--c-text-3); color: var(--c-text); }

.btn-danger { background: var(--c-danger); color: #fff; }
.btn-danger:hover { background: #a01822; }

.btn-success { background: var(--c-success); color: #fff; }
.btn-success:hover { background: #0a6535; }

.btn-text {
  background: transparent;
  color: var(--c-brand);
  border: none;
  padding: 0 6px;
  height: auto;
  font-weight: 600;
  font-size: 12px;
}
.btn-text:hover { text-decoration: underline; }

/* ── ALERT ──────────────────────────────────────────── */
.alert {
  padding: 10px 14px;
  border-radius: var(--radius);
  font-size: 13px;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 16px;
  border-left: 3px solid;
}
.alert i { font-size: 14px; flex-shrink: 0; margin-top: 1px; }
.alert-danger  { background: var(--c-danger-bg);  color: var(--c-danger);  border-color: var(--c-danger); }
.alert-success { background: var(--c-success-bg); color: var(--c-success); border-color: var(--c-success); }
.alert-warning { background: var(--c-warning-bg); color: var(--c-warning); border-color: var(--c-warning); }
.alert-info    { background: var(--c-info-bg);    color: var(--c-info);    border-color: var(--c-info); }

/* ============================================================
   APP SHELL
   ============================================================ */
#app {
  display: flex;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* ── SIDEBAR ────────────────────────────────────────── */
.sidebar {
  width: var(--sidebar-w);
  background: var(--c-brand);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  height: 100vh;
  overflow: hidden;
  position: relative;
  z-index: 100;
  transition: width var(--transition);
}

/* sidebar top bar */
.sidebar-header {
  padding: 0 18px;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}
.sidebar-close { display: none; }
.sidebar-brand-mark {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
/* Real logo in sidebar — hide the old SVG box */
.sidebar-logo-box { display: none; }

/* Horizontal logo (white) for sidebar */
.sidebar-logo-img {
  height: 22px;
  width: auto;
  display: block;
  filter: brightness(0) invert(1);
  opacity: 0.88;
}

.sidebar-brand-text { display: none; }

/* user row */
.sidebar-user {
  padding: 14px 18px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}
.sidebar-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(255,255,255,0.15);
  border: 1.5px solid rgba(255,255,255,0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 700;
  font-size: 12px;
  color: #fff;
  flex-shrink: 0;
}
.sidebar-user-info { overflow: hidden; min-width: 0; }
.sidebar-user-name {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-user-role {
  font-size: 11.5px;
  color: rgba(255,255,255,0.4);
}

/* nav */
.sidebar-nav {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 12px 10px;
}
.sidebar-nav::-webkit-scrollbar { width: 2px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); }

.nav-group-label {
  font-size: 11px;
  font-weight: 700;
  color: rgba(255,255,255,0.25);
  padding: 14px 10px 5px;
}
.nav-group-label:first-child { padding-top: 4px; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: rgba(255,255,255,0.55);
  font-size: 13px;
  font-weight: 500;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
  border: none;
  background: transparent;
  width: 100%;
  text-align: left;
  font-family: inherit;
  white-space: nowrap;
  position: relative;
}
.nav-item i {
  font-size: 16px;
  width: 18px;
  flex-shrink: 0;
  text-align: center;
}
.nav-item:hover {
  background: rgba(255,255,255,0.07);
  color: rgba(255,255,255,0.9);
}
.nav-item.active {
  background: rgba(255,255,255,0.13);
  color: #fff;
  font-weight: 600;
}
.nav-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 2.5px;
  background: #fff;
  border-radius: 0 2px 2px 0;
}
.nav-badge {
  margin-left: auto;
  background: #fff;
  color: var(--c-brand);
  font-size: 10px;
  font-weight: 700;
  font-family: 'Plus Jakarta Sans', sans-serif;
  padding: 1px 6px;
  border-radius: 10px;
  flex-shrink: 0;
}

/* sidebar footer */
.sidebar-footer {
  padding: 12px 10px;
  border-top: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}
.sidebar-logout {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: rgba(255,255,255,0.4);
  font-size: 13px;
  font-weight: 500;
  border: none;
  background: transparent;
  width: 100%;
  font-family: inherit;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
.sidebar-logout i { font-size: 16px; width: 18px; text-align: center; }
.sidebar-logout:hover {
  background: rgba(192,32,44,0.2);
  color: #ff6b78;
}

/* ── TOPBAR ─────────────────────────────────────────── */
.app-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }

.topbar {
  height: var(--topbar-h);
  background: var(--c-white);
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 16px;
  flex-shrink: 0;
  z-index: 10;
}
.topbar-hamburger {
  display: none;
  width: 34px;
  height: 34px;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--c-text-3);
  cursor: pointer;
  border-radius: var(--radius-sm);
  font-size: 18px;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
.topbar-hamburger:hover { background: var(--c-surface-2); color: var(--c-text); }
/* Unread HR message (Contact HR badge) while the menu is closed on a phone (__571). */
.topbar-hamburger.has-badge { position: relative; }
/* New message from HR (__572): stays until opened or closed, unlike a toast. */
.msg-banner { position: fixed; top: calc(var(--topbar-h) + 12px); right: 24px; width: 340px; z-index: 900;
  display: flex; gap: 10px; align-items: flex-start; padding: 12px 10px 12px 14px; cursor: pointer;
  background: var(--c-surface); color: var(--c-text); border: 1px solid var(--c-border); border-left: 4px solid var(--c-brand);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); animation: slideUp 200ms ease; }
.msg-banner-main { flex: 1; min-width: 0; }
.msg-banner-who { font-size: 12px; color: var(--c-text-3); margin-bottom: 2px; }
.msg-banner-who b { color: var(--c-brand); }
.msg-banner-text { font-size: 13px; line-height: 1.4; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; word-break: break-word; }
.msg-banner-x { flex: 0 0 auto; border: 0; background: none; color: var(--c-text-3); padding: 2px; cursor: pointer; font-size: 16px; line-height: 1; }
@media (max-width: 640px) { .msg-banner { left: 12px; right: 12px; width: auto; top: calc(var(--topbar-h) + 8px); } }
.topbar-hamburger.has-badge::after { content: ''; position: absolute; top: 5px; right: 5px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--c-danger); box-shadow: 0 0 0 2px var(--c-white); }
.topbar-title {
  margin: 0;
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 700;
  font-size: 15px;
  color: var(--c-text);
  letter-spacing: -0.01em;
}
.topbar-breadcrumb {
  font-size: 11px;
  color: var(--c-text-3);
  margin-top: 1px;
}
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.topbar-time {
  font-size: 12px;
  font-weight: 500;
  color: var(--c-text-3);
  font-family: 'JetBrains Mono', monospace;
  padding-right: 8px;
  border-right: 1px solid var(--c-border);
}
.topbar-action {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--c-border);
  background: var(--c-white);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--c-text-3);
  font-size: 15px;
  position: relative;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
.topbar-action:hover { background: var(--c-surface-2); color: var(--c-text); border-color: var(--c-border-2); }
.notif-pip {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-danger);
  border: 1.5px solid #fff;
}

/* ── CONTENT ────────────────────────────────────────── */
.content {
  flex: 1;
  overflow-y: auto;
  --content-pad: 24px;
  padding: var(--content-pad);
  background: var(--c-bg);
}

/* ── PAGE HEADER ────────────────────────────────────── */
.page-hdr, .panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 20px;
  gap: 12px;
  flex-wrap: wrap;
}
.page-title, .panel-head h1 {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 800;
  font-size: 20px;
  color: var(--c-text);
  letter-spacing: -0.02em;
}
.page-sub, .panel-sub {
  font-size: 12px;
  color: var(--c-text-3);
  margin-top: 2px;
}
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* The top bar names the tab (it is the page's h1), so the in-page heading only
   repeated it. The line under it (a count, the date, a note) and the actions
   stay; a header left with neither is dropped so it leaves no gap. */
.page-title, .panel-head h1 { display: none; }
.page-hdr, .panel-head { align-items: center; }
.page-hdr:not(:has(.page-sub, .panel-sub, button, a, select, input, label)) { display: none !important; }   /* the roster's header sets display inline */

/* ── STAT CARDS ─────────────────────────────────────── */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 20px; }
.stat-card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
  display: flex;
  align-items: flex-start;   /* labels line up across a row; a tile with no sub-line no longer sits lower */
  gap: 12px;
}
.stat-card-header { display: flex; align-items: center; justify-content: space-between; }
.stat-card-label, .stat-label { font-size: 12.5px; font-weight: 600; color: var(--c-text-3); }
.stat-card-value, .stat-value {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 800;
  font-size: 26px;
  color: var(--c-text);
  letter-spacing: -0.03em;
  line-height: 1;
}
.stat-card-trend, .stat-sub { font-size: 11px; color: var(--c-text-3); }
.stat-card-trend strong { color: var(--c-success); }
.stat-card-trend.down strong { color: var(--c-danger); }

/* ── CARD ───────────────────────────────────────────── */
.card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.card-header {
  padding: 14px 20px;
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.card-title {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 700;
  font-size: 14px;
  color: var(--c-text);
  letter-spacing: -0.01em;
}
.card-body { padding: 20px; }
/* Figures in a table read right-aligned, so their digits line up. */
.data-tbl th.t-num, .data-tbl td.t-num { text-align: right; white-space: nowrap; }
/* An empty register sits in the middle of its card, not stuck to the top. */
#payroll-result:has(> .empty-panel:only-child) { display: flex; align-items: center; justify-content: center; min-height: 240px; }
@media (max-width: 480px) {
  /* The register's four totals fit two across; stacked, they took a whole screen. */
  #payroll-result .stat-grid[style*="grid-template-columns"] { grid-template-columns: 1fr 1fr !important; gap: 10px; }
}
.card-body-sm { padding: 14px 20px; }

/* ── BAR CHART (branch headcount, employment type, analytics) ── */
.bar-chart { padding: 18px 20px 20px; display: flex; flex-direction: column; gap: 13px; }

/* ── CARD CONTENT GUTTERS ──────────────────────────────────────────────
   Content dropped straight into a .card (without a .card-body wrapper) still
   needs side gutters so text never touches the frame. Self-padded / full-bleed
   children (header, body, list rows, charts, tables, centered states) are
   excluded so they keep their existing layout. */
.card > :not(.card-header):not(.card-body):not(.card-body-sm):not(.ann-item):not(.bar-chart):not(.tbl-wrap):not(table):not(.empty-panel):not(.loading-panel):not(.btn):not(.myps-item) {
  padding-left: 20px;
  padding-right: 20px;
}
.card-header + :not(.card-body):not(.card-body-sm):not(.tbl-wrap):not(table):not(.myps-item) {
  padding-top: 16px;
}
.card > :last-child:not(.card-header):not(.card-body):not(.card-body-sm):not(.ann-item):not(.bar-chart):not(.tbl-wrap):not(table):not(.empty-panel):not(.loading-panel):not(.btn):not(.myps-item) {
  padding-bottom: 18px;
}

/* ── CARD CONTENT INSET ──────────────────────────────────────
   Cards render content directly (no padded body wrapper). Inset any
   non-structural content block so text/fields never touch the frame.
   Full-bleed blocks (header, tables, filter bars, bar charts, list rows)
   are excluded — they carry their own padding. */
.card > *:not(.card-header):not(.card-body):not(.card-body-sm):not(.tbl-wrap):not(.filters):not(.bar-chart):not(.ann-item):not(.myps-item):not(.btn) {
  margin-left: 20px;
  margin-right: 20px;
}
.card > .card-header + *:not(.card-body):not(.card-body-sm):not(.tbl-wrap):not(.filters):not(.bar-chart):not(.ann-item):not(.myps-item):not(.btn) {
  margin-top: 16px;
}
.card > *:not(.card-header):not(.card-body):not(.card-body-sm):not(.tbl-wrap):not(.filters):not(.bar-chart):not(.ann-item):not(.myps-item):not(.btn):last-child {
  margin-bottom: 18px;
}

/* ── TABLE ──────────────────────────────────────────── */
.tbl-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
thead th {
  text-align: left;
  font-size: 12px;
  font-weight: 700;
  color: var(--c-text-3);
  padding: 10px 12px;
  border-bottom: 1.5px solid var(--c-border);
  white-space: nowrap;
  background: var(--c-surface-2);
}
tbody tr {
  border-bottom: 1px solid var(--c-border);
  transition: background var(--transition);
}
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--c-surface-2); }

/* ── Employee attendance month calendar (roster-style, view-only) ── */
.att-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.att-cal-hdr {
  text-align: center; font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--c-text-3); padding: 2px 0 6px;
}
.att-cell {
  min-height: clamp(48px, 6.5vw, 72px); border: 1px solid var(--c-border); border-radius: 8px;
  padding: 5px 6px; display: flex; flex-direction: column; gap: 3px;
  background: var(--c-surface); font-size: 11px; overflow: hidden;
  cursor: pointer; transition: background var(--transition), border-color var(--transition);
}
.att-cell:not(.att-empty):hover { background: var(--c-surface-2); border-color: var(--c-brand); }
.att-cell.sel { border-color: var(--c-brand); box-shadow: 0 0 0 1px var(--c-brand) inset; }
.att-cell.att-empty { cursor: default; }
.att-cell .d { font-weight: 700; font-size: 11px; color: var(--c-text-2); line-height: 1; }
.att-cell .s { font-size: 9.5px; font-weight: 700; letter-spacing: .02em; line-height: 1.15;
  padding: 2px 4px; border-radius: 4px; align-self: flex-start;
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 900px) {
  .att-cal { gap: 3px; }
  .att-cell { min-height: 44px; padding: 4px; border-radius: 6px; }
  .att-cell .s { font-size: 8.5px; padding: 1px 3px; }
}
@media (max-width: 520px) {
  .att-cal { gap: 2px; }
  .att-cell { min-height: 44px; padding: 3px; }
  .att-cell .d { font-size: 10px; }
  .att-cell .s { font-size: 7.5px; padding: 1px 2px; letter-spacing: 0; }
  /* The short form (data-s, set in the My Attendance calendar) replaces the label:
     at 7.5px '\2713 Present' and 'Absent' still clipped in a 31px cell. */
  .att-cell .s[data-s] { font-size: 0; align-self: stretch; text-align: center; padding: 2px 0; }
  .att-cell .s[data-s]::after { content: attr(data-s); font-size: 9px; }
  /* Icon short forms (tokens set in the calendar's CHIP table), drawn from the
     Tabler font the app already loads - not emoji check / cross / warning. */
  .att-cell .s[data-s^="i-"]::after { font-family: "tabler-icons"; font-size: 12px; line-height: 1; }
  .att-cell .s[data-s="i-check"]::after { content: "\ea5e"; }
  .att-cell .s[data-s="i-x"]::after     { content: "\eb55"; }
  .att-cell .s[data-s="i-alert"]::after { content: "\ea06"; }
  /* The .card child rules inset each block by margin AND padding, 40px a side; on a
     phone the calendar needs that width more. !important: those rules out-rank a
     class selector. Header, grid and key drop it together so they stay aligned. */
  .myatt-cal-hdr, .myatt-cal-hdr ~ .att-cal, .myatt-key { padding-left: 0 !important; padding-right: 0 !important; }
}

/* My Attendance calendar header: title + month-picker + arrows, always inline */
/* No width:100% - the .card child rules give it 20px side margins, and 100% plus those
   pushed it 20px past the card's right edge: the arrows sat on the edge and the rule
   ran off it. Auto width fits between the margins like the grid and key below. */
.myatt-cal-hdr { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:nowrap; padding:14px 20px; border-bottom:1px solid var(--c-border); }
/* .card has no padding: the header, grid and key each carry their own. */
.myatt-cal-hdr ~ .att-cal { padding: 16px 20px 0; }
.myatt-key { display:flex; gap:12px; flex-wrap:wrap; margin:16px 20px 0; padding:12px 0 16px; border-top:1px solid var(--c-border); }
.myatt-nav { display:flex; align-items:center; gap:6px; flex:0 0 auto; }
.myatt-month-pick { position:relative; display:inline-flex; align-items:center; cursor:pointer; }
.myatt-month-pick > span { border-bottom:1px dashed var(--c-border-strong, #c8cdd6); }
.myatt-month-pick:hover > span { color:var(--c-brand); border-bottom-color:var(--c-brand); }
.myatt-month-pick input[type=month] { position:absolute; inset:0; opacity:0; width:100%; height:100%; cursor:pointer; border:0; padding:0; }
@media (max-width:420px){ .myatt-cal-hdr .card-title{ font-size:14px; } .myatt-nav .btn-sm{ padding:5px 8px; } }

/* My Attendance counters: always ONE row of compact cards. Three since Logged was
   removed; four columns left a quarter of the row empty after Absent. */
.myatt-stats { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)) !important; gap: 10px; }
.myatt-stats .stat-card { padding: 12px 10px; }
.myatt-stats .stat-value { font-size: 18px !important; }
.myatt-stats .stat-label { font-size: 10px; }
@media (max-width: 640px) {
  .myatt-stats { gap: 6px; }
  .myatt-stats .stat-card { padding: 8px 6px; flex-direction: column; text-align: center; gap: 4px; }
  .myatt-stats .stat-value { font-size: 16px !important; }
}
.att-empty { border: none; background: transparent; }
.att-today { outline: 2px solid var(--c-brand); outline-offset: -2px; }
.att-s-present  { background: rgba(34,160,84,.22);  color: #14663a; }
.att-s-late     { background: rgba(230,150,20,.26); color: #8a5606; }
.att-s-half_day { background: rgba(230,150,20,.26); color: #8a5606; }
/* A Live Seller's session held for review: amber, as on her dashboard strip -
   the red .att-s-anomaly read as an error on a day she did work. */
.att-s-awaiting { background: rgba(230,150,20,.26); color: #8a5606; }
.att-s-absent   { background: rgba(220,60,60,.22);  color: #a11c15; }
.att-s-anomaly  { background: rgba(220,60,60,.30);  color: #a11c15; }
.att-s-on_leave { background: rgba(60,120,230,.22); color: #1e4dae; }
.att-s-holiday  { background: rgba(120,80,220,.22); color: #5a30b0; }
.att-s-holiday_duty { background: rgba(120,80,220,.34); color: #4a2596; font-weight: 700; }
.att-s-rest, .att-s-off { background: rgba(127,127,127,.16); color: var(--c-text-3); }
.att-s-wfh { background: rgba(60,120,230,.18); color: #1e4dae; }
td {
  padding: 10px 12px;
  font-size: 13px;
  color: var(--c-text);
  vertical-align: middle;
}
.td-name { font-weight: 600; color: var(--c-text); }
.td-sub  { font-size: 11px; color: var(--c-text-3); }
.td-mono { font-family: 'JetBrains Mono', monospace; font-size: 12px; }

/* ── BADGE ──────────────────────────────────────────── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.badge-success { background: var(--c-success-bg); color: var(--c-success); }
.badge-danger  { background: var(--c-danger-bg);  color: var(--c-danger);  }
.badge-warning { background: var(--c-warning-bg); color: var(--c-warning); }
.badge-info    { background: var(--c-info-bg);    color: var(--c-info);    }
.badge-neutral { background: var(--c-surface-2);  color: var(--c-text-2);  border: 1px solid var(--c-border); }
.badge-brand   { background: var(--c-brand-10);   color: var(--c-brand);   }

/* ── FILTERS BAR ────────────────────────────────────── */
.filters {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 12px 20px;
  border-bottom: 1px solid var(--c-border);
}
.filters .field-input { height: 34px; font-size: 12px; max-width: 220px; }
.filters .field-input.no-icon { max-width: 160px; padding-left: 10px; }

/* Filter controls: .filter-select (selects, and date/month/text inputs that
   borrow the class) and .search-box > i + .search-input. About 40 controls
   across the admin tabs had only layout rules, so they rendered as the
   browser's default grey boxes. Same frame as .field-input, at the compact
   filter-bar height. */
.filter-select, .search-input {
  height: 34px;
  box-sizing: border-box;
  min-width: 0;
  max-width: 100%;
  border: 1.5px solid var(--c-border);
  border-radius: var(--radius);
  padding: 0 10px;
  font-family: inherit;
  font-size: 12.5px;
  color: var(--c-text);
  background-color: var(--c-white);
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}
.filter-select:hover, .search-input:hover { border-color: var(--c-border-2); }
.filter-select:focus, .search-input:focus {
  border-color: var(--c-brand);
  box-shadow: 0 0 0 3px var(--c-brand-10);
}
.filter-select::placeholder, .search-input::placeholder { color: var(--c-text-3); }
select.filter-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237c7fa0' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 28px;
  text-overflow: ellipsis;
  cursor: pointer;
}
input.filter-select[type="date"], input.filter-select[type="month"] { padding-right: 6px; }
.search-box {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 1 260px;
  width: 260px;
  max-width: 100%;
  min-width: 0;
}
.search-box > i {
  position: absolute;
  left: 11px;
  font-size: 15px;
  color: var(--c-text-3);
  pointer-events: none;
}
.search-box .search-input { width: 100%; padding-left: 33px; }
.modal-body .search-box { width: 100%; }
/* A button in a filter bar lines up with the controls beside it. */
.filters .btn { height: 34px; }
/* The tab's actions (Add, Export) and its count live at the right end of the
   table's toolbar, not on a row of their own above the card. */
.filters-end { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.filters-count { font-size: 12px; color: var(--c-text-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 768px) {
  .filters-end { flex: 1 1 100%; justify-content: flex-end; flex-wrap: nowrap; }
  .filters-end .filters-count { margin-right: auto; }
}
@media (max-width: 480px) {
  /* A phone has no room for the count beside two buttons; the buttons share the row. */
  .filters-end .filters-count { display: none; }
  /* Equal shares while they fit, but never narrower than the label: "Run
     Absence Detection" + "Manual Entry" ran 7px past a 360px screen. They
     take a row each rather than being cut. */
  .filters-end { flex-wrap: wrap; }
  .filters-end .btn { flex: 1 1 0; min-width: fit-content; }
  .filters input.filter-select[type="number"] { flex: 0 0 92px; }
}
/* Requests: the queue tabs are the table card's own header - folder tabs on a
   grey strip, the open one joined to the table under it. They sat above the
   card as a row of links, and with Export all / Leave Balances on the same line
   they wrapped to two rows on a 1366px screen. The buttons live in the toolbar
   now. The ten tabs need ~1,100px; below that they wrap to a second row rather
   than scroll, because a scrolled-off tab hid queues that were waiting (Attendance
   12 sat past the edge at 1280px with nothing to say it was there). */
.card > .req-head { margin: 0 !important; padding: 8px 12px 0 !important;
  background: var(--c-surface-2); border-bottom: 1px solid var(--c-border); }
.req-head > .req-tabs { border-bottom: 0; margin: 0 0 -1px; flex-wrap: wrap; overflow: visible; gap: 0 2px;
  /* Where they still wrap (sidebar at 1280-1350px, tablets), a rule under every
     36px row, so a tab open on the first row meets a line like the last row's. */
  background: repeating-linear-gradient(to bottom, transparent 0 35px, var(--c-border) 35px 36px); }
/* One fixed height: a tab with a count pill was 36px and one with a grey 0 was
   32px, so under align-items:flex-end the labels sat at two heights. The
   tighter padding and gap fit all ten on one row from 1366px up. */
.req-head .req-tabs .req-tab { box-sizing: border-box; height: 36px; padding: 0 8px; gap: 6px; font-size: 12.5px;
  border: 1px solid transparent; border-bottom: 0; border-radius: 8px 8px 0 0; }
.req-head .req-tabs .req-tab:hover { background: rgba(255,255,255,0.6); }
.req-head .req-tabs .req-tab.active { background: var(--c-surface);
  border-color: var(--c-border); box-shadow: inset 0 2px 0 var(--c-brand); }
.req-head .req-tabs .req-tab.active::after { display: none; }
/* Narrower than one row (~1,003px: phones, tablets, a laptop with the sidebar)
   the folder tabs wrapped into three or four ragged rows of different lengths.
   There they become an even grid of tiles - label left, count right - so every
   queue is still in view and the columns line up. Measured on the card, not the
   screen, because the sidebar changes how much room the card has. */
.req-head { container: reqhead / inline-size; }
@container reqhead (max-width: 1010px) {
  .req-head > .req-tabs { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 6px; margin: 0; padding: 0 0 10px; background: none; }
  .req-head .req-tabs .req-tab { height: auto; min-height: 38px; padding: 6px 10px; justify-content: space-between;
    white-space: normal; text-align: left; line-height: 1.25;
    background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 8px; }
  .req-head .req-tabs .req-tab:hover { background: var(--c-surface); border-color: var(--c-brand-20); }
  .req-head .req-tabs .req-tab.active { background: var(--c-brand-10); border-color: var(--c-brand); box-shadow: none; }
  .req-head .req-tabs .req-tab > span { flex: 0 0 auto; }
}
@container reqhead (max-width: 330px) {
  .req-head > .req-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.req-tb-end { display: flex; gap: 8px; }
@media (max-width: 640px) {
  .req-tb-end { flex: 1 1 100%; }
  .req-tb-end .btn { flex: 1 1 0; min-width: 0; }
}
/* Branch Locations: the view switch and the add button on one row. */
.br-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.br-bar .seg-toggle { margin-bottom: 0; }
.br-actions { margin-left: auto; }
/* File pickers (announcement and job images): a button like the rest, not the
   browser's grey "Choose File". */
input[type="file"] { font: inherit; font-size: 12.5px; color: var(--c-text-2); max-width: 100%; }
input[type="file"]::file-selector-button {
  height: 32px; margin-right: 10px; padding: 0 12px; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--c-text);
  background: var(--c-surface); border: 1.5px solid var(--c-border-2); border-radius: var(--radius);
}
input[type="file"]::file-selector-button:hover { background: var(--c-surface-2); }
/* Balancing: its messages sit straight on the page, so give them the card frame. */
#bal-body > .empty-panel { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg); }
/* Text links in the portal ("Decided ones are under Approved", "open the
   Leave form"). There was no base rule, so they showed browser-default blue. */
#app a:not([class]), .modal a:not([class]) { color: var(--c-brand); font-weight: 600; text-decoration: none; }
#app a:not([class]):hover, .modal a:not([class]):hover { text-decoration: underline; }
/* Ledger "Record one": its fields carry inline fixed widths, so on a phone
   they ended ragged. Employee takes the row; the rest pair up. The card
   already insets its children, so the inline side padding doubled it. */
.led-form { padding-left: 0 !important; padding-right: 0 !important; }
@media (max-width: 640px) {
  .led-form > label { flex: 1 1 130px !important; min-width: 0 !important; }
  .led-form > label:first-child { flex-basis: 100% !important; }
}
/* Add sits level with the 34px fields beside it. */
.led-form > .btn { height: 34px; }
@media (max-width: 640px) {
  /* Ledger filters share the row evenly instead of wrapping at odd widths. */
  .led-filters { width: 100%; }
  .led-filters > * { flex: 1 1 140px; }
}
/* Small inline inputs keep their own size but take the same focus ring. */
.ot-hrs-in:focus, .ded-amt:focus, .req-pg-size:focus {
  outline: none;
  border-color: var(--c-brand);
  box-shadow: 0 0 0 3px var(--c-brand-10);
}
/* ── ACTION BUTTONS (compact row / queue actions) ─── */
.queue-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* In a table row the actions stay on one line: wrapped, three icons stacked
   two-over-one and every row came out a different height. */
.data-tbl .queue-actions { flex-wrap: nowrap; }
/* Performance: the rating table sits edge to edge like every other table. */
.card > #perf-host.perf-flush { margin-left: 0; margin-right: 0; padding-left: 0; padding-right: 0; }
.action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--c-border);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  color: var(--c-text-2);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  transition: var(--transition);
}
.action-btn:hover { background: var(--c-surface-2); color: var(--c-text); border-color: var(--c-text-3); }
.action-btn.approve { color: var(--c-success); }
.action-btn.approve:hover { background: var(--c-success-bg); border-color: var(--c-success); color: var(--c-success); }
.action-btn.reject { color: var(--c-danger); }
.action-btn.reject:hover { background: var(--c-danger-bg); border-color: var(--c-danger); color: var(--c-danger); }

/* ── MODAL ──────────────────────────────────────────── */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(8,9,32,0.55);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 16px;
}
.modal {
  background: var(--c-surface);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 540px;
  box-shadow: var(--shadow-lg);
  animation: modalIn 180ms ease;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
@keyframes modalIn { from { opacity:0; transform:scale(0.97) translateY(8px); } to { opacity:1; transform:scale(1) translateY(0); } }
.modal-header {
  padding: 18px 24px 16px;
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}
.modal-title {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 700;
  font-size: 16px;
  color: var(--c-text);
  letter-spacing: -0.01em;
}
.modal-close {
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--c-text-3);
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}
.modal-close:hover { background: var(--c-surface-2); color: var(--c-text); }
.modal-body {
  padding: 20px 24px;
  overflow-y: auto;
  flex: 1;
}
.modal-footer {
  padding: 14px 24px;
  border-top: 1px solid var(--c-border);
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  flex-shrink: 0;
}

/* ── FORM ROWS ──────────────────────────────────────── */
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-row.cols3 { grid-template-columns: 1fr 1fr 1fr; }
/* Rules labels wrap to 2 or 3 lines in a half-width card; without this the
   inputs beside each other sat up to 40px apart. */
#rules-fields .form-row { align-items: end; }
.form-section-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--c-text-3);
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--c-border);
}

/* ── SIDE PANEL ─────────────────────────────────────── */
.side-panel-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(8,9,32,0.45);
  backdrop-filter: blur(2px);
  z-index: 500;
  display: none;
}
.side-panel-backdrop.active { display: block; }
.side-panel {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: 400px;
  background: var(--c-surface);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  z-index: 501;
  transform: translateX(100%);
  transition: transform 220ms ease;
}
.side-panel.open { transform: translateX(0); }
.side-panel-header {
  padding: 16px 20px;
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}
.side-panel-title {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-weight: 700;
  font-size: 15px;
  color: var(--c-text);
}
.side-panel-body { flex: 1; overflow-y: auto; padding: 20px; }
.side-panel-footer {
  padding: 14px 20px;
  border-top: 1px solid var(--c-border);
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* ── DETAIL ROWS ────────────────────────────────────── */
.detail-row { display: flex; padding: 9px 0; border-bottom: 1px solid var(--c-border); font-size: 13px; }
.detail-row:last-child { border-bottom: none; }
.detail-lbl { width: 140px; color: var(--c-text-3); flex-shrink: 0; font-size: 12px; }
.detail-val { font-weight: 500; color: var(--c-text); }

/* ── TABS ───────────────────────────────────────────── */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--c-border); margin-bottom: 20px; }
.tab-btn {
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  color: var(--c-text-3);
  border: none;
  background: transparent;
  cursor: pointer;
  position: relative;
  font-family: inherit;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
  white-space: nowrap;
}
.tab-btn:hover { color: var(--c-text); background: var(--c-surface-2); }
.tab-btn.active {
  color: var(--c-brand);
  font-weight: 600;
}
.tab-btn.active::after {
  content: '';
  position: absolute;
  bottom: -1px;
  left: 0; right: 0;
  height: 2px;
  background: var(--c-brand);
  border-radius: 2px 2px 0 0;
}

/* ── PAYSLIP ────────────────────────────────────────── */
.payslip {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  max-width: 680px;
  margin: 0 auto;
  box-shadow: var(--shadow);
}
.payslip-header {
  background: var(--c-brand);
  padding: 20px 28px;
  display: flex;
  align-items: center;
  gap: 18px;
}
.payslip-header-logo {
  height: 28px;
  width: auto;
  filter: brightness(0) invert(1);
  opacity: 0.9;
}
.payslip-company-name { display: none; } /* replaced by logo */
.payslip-company-sub {
  font-size: 10px;
  color: rgba(255,255,255,0.45);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: 2px;
}
.payslip-period {
  margin-left: auto;
  text-align: right;
}
.payslip-period-lbl { font-size: 10px; color: rgba(255,255,255,0.4); letter-spacing: 0.06em; text-transform: uppercase; }
.payslip-period-val { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 700; font-size: 14px; color: #fff; margin-top: 2px; }
.payslip-meta { display: grid; grid-template-columns: repeat(4, 1fr); border-bottom: 1px solid var(--c-border); }
.payslip-meta-item { padding: 12px 20px; border-right: 1px solid var(--c-border); }
.payslip-meta-item:last-child { border-right: none; }
.payslip-meta-label { font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--c-text-3); margin-bottom: 3px; }
.payslip-meta-value { font-size: 13px; font-weight: 600; color: var(--c-text); }
.payslip-body { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.payslip-col { padding: 20px; }
.payslip-col:first-child { border-right: 1px solid var(--c-border); }
.payslip-col-title { font-size: 10px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--c-text-3); margin-bottom: 12px; }
.payslip-line { display: flex; justify-content: space-between; align-items: center; padding: 5px 0; font-size: 13px; border-bottom: 1px solid var(--c-border); }
.payslip-line:last-child { border-bottom: none; }
.payslip-line .lbl { color: var(--c-text-2); }
.payslip-line .amt { font-weight: 600; font-family: 'JetBrains Mono', monospace; font-size: 12px; }
.payslip-line .ded { color: var(--c-danger); }
.payslip-subtotal { display: flex; justify-content: space-between; padding: 10px 0 0; font-size: 13px; font-weight: 700; border-top: 1.5px solid var(--c-border); margin-top: 6px; }
.payslip-footer {
  background: var(--c-brand);
  padding: 16px 28px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.payslip-net-lbl { font-size: 12px; font-weight: 600; color: rgba(255,255,255,0.6); }
.payslip-net-amt { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; font-size: 26px; color: #fff; letter-spacing: -0.03em; }

/* ── PAYSLIP — corporate format (Ditucalan) ─────────── */
.ps-doc { background:#fff; color:#1a1a1a; font-family:'Inter','Plus Jakarta Sans',system-ui,sans-serif; font-size:12px; line-height:1.4; padding:26px 30px; border:1px solid var(--c-border); border-radius:var(--radius); max-width:920px; }
.ps-head { display:flex; justify-content:space-between; align-items:flex-start; gap:20px; padding-bottom:14px; }
.ps-head-l { display:flex; align-items:center; gap:18px; flex:1; min-width:0; }
.ps-logo { height:54px; width:auto; flex:0 0 auto; }
.ps-addr { font-size:11px; color:#333; }
.ps-head-r { text-align:right; flex:0 0 auto; }
.ps-title { font-size:15px; font-weight:700; letter-spacing:0.02em; }
.ps-sub { font-size:12px; color:#333; margin-top:2px; }
.ps-period { font-size:11px; color:#333; margin-top:4px; }
.ps-period strong { font-style:italic; font-weight:600; }
.ps-info, .ps-rates { width:100%; border-collapse:collapse; }
.ps-info { border-top:1.5px solid #1a1a1a; }
.ps-info td { padding:5px 8px; font-size:12px; vertical-align:top; border-bottom:1px solid #e6e6e6; }
.ps-info td.k { white-space:nowrap; color:#1a1a1a; }
.ps-info td:nth-child(1) { width:14%; }
.ps-info td:nth-child(2) { width:40%; }
.ps-info td:nth-child(3) { width:12%; border-left:1px solid #e6e6e6; padding-left:22px; }
.ps-info td:nth-child(4) { width:34%; }
.ps-info td.v.b { font-weight:700; }
.ps-rates { border-bottom:1.5px solid #1a1a1a; }
.ps-rates td { padding:6px 8px; font-size:12px; }
.ps-rates td.k { white-space:nowrap; }
.ps-banner { background:#41719c; color:#fff; text-align:center; font-weight:700; font-size:12px; letter-spacing:0.45em; padding:5px 0; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
.ps-cols { display:grid; grid-template-columns:1fr 1fr; gap:0; }
.ps-col { padding:4px 0; min-width:0; }
.ps-col:first-child { padding-right:26px; }
.ps-col:last-child { padding-left:26px; border-left:1px solid #e6e6e6; }
.ps-sec { font-weight:700; font-style:italic; font-size:12px; margin:14px 0 4px; line-height:1.25; }
.ps-sec:first-child { margin-top:8px; }
.ps-tbl { width:100%; border-collapse:collapse; }
.ps-tbl th, .ps-tbl td { font-size:12px; padding:3px 4px; }
.ps-tbl th { font-weight:700; color:#1a1a1a; border-bottom:1px solid #d4d4d4; }
.ps-tbl td { color:#333; }
.ps-tbl .r { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; width:1%; padding-left:10px; }
.ps-tbl td:first-child { word-break:break-word; }
.ps-tbl-3 th:first-child, .ps-tbl-3 td:first-child { width:46%; }

/* The basis copy sits beside the slip in a ~430px column from about 1280px up.
   Split in two there, the loans table got 184px for four columns and Balance /
   Deduction / Rembal ran 27px past a 1366px screen. It stacks by the width of
   its own column, not the screen, so it still sits in two when it wraps below
   the slip at full width. */
#slip-basis-side { container: slipside / inline-size; }
@container slipside (max-width: 560px) {
  #slip-basis-side .ps-cols { grid-template-columns: 1fr; }
  #slip-basis-side .ps-col:first-child { padding-right: 0; }
  #slip-basis-side .ps-col:last-child { padding-left: 0; border-left: none; border-top: 1px solid #e6e6e6; margin-top: 10px; padding-top: 10px; }
}
/* The header tables are three label/value pairs a row and need ~400px; on a
   phone the slip scrolled sideways. Below 440px of SLIP width (not screen:
   Save as Image renders at 920px and keeps the printed layout) each pair takes
   its own line, and pairs with no value are left out instead of listed blank. */
.ps-doc { container: psdoc / inline-size; }
@container psdoc (max-width: 440px) {
  .ps-info tr, .ps-rates tr { display: grid; grid-template-columns: max-content minmax(0, 1fr); }
  .ps-info td, .ps-rates td { width: auto !important; border-left: none !important; padding-left: 8px !important; }
  .ps-info td.v, .ps-rates td.v { text-align: right; }
  .ps-rates td.k:empty, .ps-rates td.k:empty + td,
  .ps-info td.k:has(+ td.v:empty), .ps-info td.k:has(+ td.v:empty) + td,
  .ps-rates td.k:has(+ td.v:empty), .ps-rates td.k:has(+ td.v:empty) + td { display: none; }
}
/* ── Payslip: fully responsive ── */
@media (max-width: 700px) {
  .ps-doc { padding: 14px 12px; font-size: 11px; }
  .ps-head { flex-direction: column; align-items: stretch; gap: 8px; }
  .ps-head-l { gap: 12px; }
  .ps-logo { max-width: 150px; height: auto; }
  .ps-head-r { text-align: left; }
  .ps-cols { grid-template-columns: 1fr; }
  .ps-col:first-child { padding-right: 0; }
  .ps-col:last-child { padding-left: 0; border-left: none; border-top: 1px solid #e6e6e6; margin-top: 10px; padding-top: 10px; }
  .ps-tbl th, .ps-tbl td { font-size: 11px; padding: 3px 2px; }
  .ps-addr { font-size: 10px; }
}
@media (max-width: 420px) {
  .ps-doc { padding: 10px 8px; font-size: 10.5px; }
  .ps-tbl th, .ps-tbl td { font-size: 10.5px; }
}

/* Snapshot mode: the saved image is ALWAYS the desktop slip, any viewport */
.ps-capture { width: 920px !important; max-width: 920px !important; padding: 26px 30px !important; font-size: 12px !important; border: none !important; }
.ps-capture .ps-head { flex-direction: row !important; align-items: flex-start !important; gap: 20px !important; }
.ps-capture .ps-head-r { text-align: right !important; }
.ps-capture .ps-logo { height: 54px !important; width: auto !important; max-width: 240px !important; }
.ps-capture .ps-addr { font-size: 11px !important; }
.ps-capture .ps-cols { grid-template-columns: 1fr 1fr !important; }
.ps-capture .ps-col:first-child { padding-right: 26px !important; }
.ps-capture .ps-col:last-child { padding-left: 26px !important; border-left: 1px solid #e6e6e6 !important; border-top: none !important; margin-top: 0 !important; padding-top: 4px !important; }
.ps-capture .ps-tbl th, .ps-capture .ps-tbl td { font-size: 12px !important; padding: 3px 4px !important; }

/* Employee payslip period list (light card - dark text, active highlight) */
.myps-item { display: block; width: 100%; box-sizing: border-box; text-align: left; padding: 9px 10px; margin-bottom: 2px;
  border: 1px solid transparent; background: transparent; border-radius: var(--radius-sm);
  cursor: pointer; font-family: inherit; transition: background var(--transition); }
.myps-item:hover { background: var(--c-surface-2); }
.myps-item.active { background: rgba(3, 0, 131, .07); border-color: var(--c-brand); }


/* ── Print: the PURE payslip and nothing else ── */
@media print {
  body * { visibility: hidden !important; }
  .ps-doc, .ps-doc * { visibility: visible !important; }
  .ps-doc {
    position: absolute !important; left: 0; top: 0; width: 100%;
    max-width: none; margin: 0; border: none; border-radius: 0; box-shadow: none;
    padding: 0; font-size: 12px;
  }
  .modal-backdrop { position: static !important; background: none !important; padding: 0 !important; overflow: visible !important; }
  .modal { box-shadow: none !important; max-height: none !important; overflow: visible !important; }
  .sidebar, .topbar, .page-hdr, .toast-wrap { display: none !important; }
  /* margin:0 removes the browser's own date/title/URL header & footer */
  .ps-doc { padding: 12mm 14mm !important; }
  .card, .modal, .content, hr { border: none !important; box-shadow: none !important; }
  hr { display: none !important; }
}
@page { size: A4; margin: 0; }
.ps-tbl-loan td:first-child { width:50%; }
.ps-totals { margin-top:14px; border-top:1.5px solid #1a1a1a; }
.ps-trow { display:flex; justify-content:space-between; padding:6px 4px; font-size:13px; font-weight:700; border-bottom:1px solid #e6e6e6; }
.ps-trow span:last-child { font-variant-numeric:tabular-nums; }
.ps-trow.net { background:#eef3f9; font-size:15px; -webkit-print-color-adjust:exact; print-color-adjust:exact; }
.ps-notes { margin-top:18px; font-size:10.5px; color:#444; }
.ps-note-title { font-weight:700; font-size:11px; color:#1a1a1a; margin-bottom:4px; }
.ps-note-body { line-height:1.5; }

/* Real-template payslip additions */
.ps-gnet { margin-top:16px; border-top:1.5px solid #1a1a1a; }
.ps-gnet .row { display:flex; justify-content:space-between; align-items:baseline; gap:12px; padding:4px 2px; }
.ps-gnet .row > span:first-child { min-width:0; }
.ps-gnet .row > span:last-child  { flex:0 0 auto; white-space:nowrap; text-align:right; }
.ps-gnet .g   { border-bottom:1px solid #1a1a1a; }
.ps-gnet .net { font-weight:800; font-size:14px; border-bottom:3px double #1a1a1a; }
.ps-td { margin-top:16px; display:flex; justify-content:space-between; align-items:baseline; gap:12px;
  font-weight:800; border-top:1px solid #1a1a1a; border-bottom:3px double #1a1a1a; padding:5px 2px; }
.ps-td > span:first-child { min-width:0; }
.ps-td > span:last-child  { flex:0 0 auto; white-space:nowrap; text-align:right; }
.ps-recv { margin-top:28px; display:flex; justify-content:flex-end; align-items:flex-end; gap:10px; font-size:12px; }
.ps-recv .line { display:inline-block; width:210px; border-bottom:1.5px solid #1a1a1a; }

/* Payroll run list: delete button */
.period-del { margin-left:6px; color:var(--c-danger); opacity:.5; padding:6px; border-radius:8px; flex:0 0 auto; }
.period-del:hover { opacity:1; background:var(--c-danger-bg); }

/* Per-item deduction rows (employee form) */
.ded-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.ded-check { display: flex; align-items: center; gap: 8px; font-size: 13px; flex: 1; cursor: pointer; }
.ded-check input { width: 16px; height: 16px; accent-color: var(--c-brand); }
.ded-amt { width: 116px; padding: 7px 9px; border: 1px solid var(--c-border); border-radius: 8px; font: inherit; text-align: right; }

/* Roster connectivity: clickable cells + names, day-detail modal */
.rg-click { cursor: pointer; }
.rg-click:hover { outline: 2px solid var(--c-brand-20); outline-offset: -2px; }
.rg-emp-n { cursor: pointer; }
.rg-emp-n:hover { color: var(--c-brand); text-decoration: underline; }
.rdi-row { display: flex; justify-content: space-between; gap: 12px; padding: 7px 2px;
  border-bottom: 1px dashed var(--c-border); font-size: 13px; }
.rdi-row .k { color: var(--c-text-3); flex: 0 0 auto; }
.rdi-pay { margin-top: 12px; padding: 11px 13px; background: var(--c-brand-10); border-radius: 10px;
  font-size: 13px; line-height: 1.55; }
.ps-confidential { margin-top:10px; font-style:italic; color:#777; font-size:10px; line-height:1.45; border-top:1px dashed #ccc; padding-top:8px; }
.ps-sign { margin-top:26px; display:flex; align-items:flex-end; gap:12px; justify-content:flex-end; }
.ps-sign-line { display:inline-block; width:240px; border-bottom:1px solid #1a1a1a; height:1px; }

/* ── CLOCK PAGE ─────────────────────────────────────── */
.clock-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; max-width: 800px; margin: 0 auto; }
.clock-display-panel { display: flex; flex-direction: column; align-items: center; padding: 32px 24px; background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg); }
.clock-time { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; font-size: 52px; color: var(--c-brand); letter-spacing: -0.04em; line-height: 1; }
.clock-date { font-size: 13px; color: var(--c-text-3); margin-top: 6px; text-align: center; }
.webcam-area { width: 100%; aspect-ratio: 4/3; background: var(--c-brand); border-radius: var(--radius); overflow: hidden; position: relative; margin: 20px 0; }
.webcam-scan { position: absolute; width: 100%; height: 2px; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent); animation: scan 2.5s linear infinite; }
@keyframes scan { 0% { top: 0; } 100% { top: 100%; } }
.webcam-face-ring { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.face-ring { width: 110px; height: 110px; border-radius: 50%; border: 1.5px solid rgba(255,255,255,0.25); }
.webcam-status { position: absolute; bottom: 12px; left: 0; right: 0; text-align: center; font-size: 11px; color: rgba(255,255,255,0.5); letter-spacing: 0.05em; }
.clock-actions-panel { display: flex; flex-direction: column; padding: 24px; background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg); gap: 12px; }
.clock-action-btn { height: 50px; width: 100%; border-radius: 14px; font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 700; font-size: 16px; letter-spacing: -0.01em; color: #fff; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 10px; transition: transform var(--transition), box-shadow var(--transition), filter var(--transition); }
.clock-action-btn i { font-size: 20px; }
.clock-action-btn:active { transform: translateY(0) scale(0.99); }
.clock-in  { background: linear-gradient(135deg, #15a657, #0f7b41); box-shadow: 0 4px 14px rgba(15,123,65,0.28); }
.clock-in:hover  { transform: translateY(-1px); box-shadow: 0 8px 22px rgba(15,123,65,0.36); filter: brightness(1.04); }
.clock-out { background: linear-gradient(135deg, #e23b48, #c0202c); box-shadow: 0 4px 14px rgba(192,32,44,0.28); }
.clock-out:hover { transform: translateY(-1px); box-shadow: 0 8px 22px rgba(192,32,44,0.36); filter: brightness(1.04); }
#btn-cam-flip { flex: 0 0 auto; }
.clock-divider { height: 1px; background: var(--c-border); margin: 4px 0; }
.clock-status-note { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--c-success); background: var(--c-success-bg); border: 1px solid var(--c-success); border-radius: var(--radius-sm); padding: 10px 12px; margin-bottom: 4px; }
.clock-status-note.done { flex-direction: column; align-items: flex-start; gap: 3px; color: var(--c-text-2); background: var(--c-surface-2); border-color: var(--c-border); }
.clock-status-note.done span { font-weight: 600; color: var(--c-text); }
/* Clock-in kiosk — single focused column */
.clock-kiosk { max-width: 460px; margin: 0 auto; }
.clock-kiosk .clock-time { text-align: center; }
.clock-kiosk .clock-date { text-align: center; margin-bottom: 22px; }
.clock-kiosk .webcam-area { margin: 0; }

/* ── KIOSK: one fixed screen, and a preview that IS the file ─────────────
   The punch screen does not scroll. Header, the two selects, the camera and
   the action bar are four bands in a column; only the camera flexes, so on a
   short phone the preview shrinks and the button never moves off the bottom.
   100dvh, not 100vh: mobile Safari's 100vh includes chrome that is not there. */
/* The height is MEASURED, not inherited. showPanel wraps every panel in a
   .panel-host div of its own and that div has no height, so height:100% on the
   panel resolved against auto and did nothing: the column was never
   height-constrained, the camera band took its natural size and the page
   scrolled. #app is 100dvh now, so this subtraction and the shell agree on
   what a viewport is - topbar + this + .content's padding is one screen. */
.ci-fixed { display: flex; flex-direction: column; overflow: hidden;
  height: calc(100dvh - var(--topbar-h) - (var(--content-pad, 24px) * 2)); }
@media (max-width: 768px) {
  .clock-kiosk.ci-fixed {
    position: fixed; left: 0; right: 0; top: var(--topbar-h);
    height: calc(100dvh - var(--topbar-h));
    max-width: 460px; margin: 0 auto; padding: 0 var(--content-pad, 16px);
  }
}
.ci-actions { display: flex; gap: 8px; margin-bottom: 9px; }
.ci-actions .btn { flex: 1 1 0; justify-content: center; padding: 7px 10px; font-size: 12.5px; }
.ci-fixed .ci-scroll { flex: 0 0 auto; }
/* The gates live in the dock, above the button, and are the only place camera
   and location are reported. */
.ci-gates { display: flex; flex-direction: column; gap: 3px; margin-bottom: 9px; }
.ci-gates:empty { display: none; }
/* Only the camera gives. The status line and the Start-it button default to
   flex-shrink:1, so on a short screen they would squash their own text
   instead of letting the preview shrink. The gates are in the dock, which
   is already flex:0 0 auto. */
.ci-fixed > #webcam-status-bar,
.ci-fixed > #btn-cam-activate { flex: 0 0 auto; }
/* The strip sat directly on the action bar's border, so the location line and
   the button read as one block. A gap separates what the screen is telling you
   from what it is asking you to do. */
.ci-fixed > #webcam-status-bar { margin-bottom: 4px; }
/* A floor, because the confirm step asks "is this photo clear?" about the
   picture in this band - and with the dock unable to shrink, the band was
   squeezed to 130px and the answer was unknowable. */
.ci-fixed .ci-camband { flex: 1 1 auto; min-height: 26%; display: flex; align-items: center;
  justify-content: center; background: transparent; padding: 0; }

/* THE WHOLE FRAME, NOT A CROP. object-fit:cover filled the box by trimming the
   stream, so the edges of the saved photo were never on screen - people framed
   themselves against a boundary that was not the file's. `contain` fits it all
   and letterboxes the remainder onto the dark surround. Portrait, because that
   is how the phone is held and how the JPEG comes out. */
.ci-frame { position: relative; height: 100%; aspect-ratio: var(--ci-cam-ar, 3 / 4); max-width: 100%;
  background: var(--c-brand); border-radius: 4px; overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.10); }
.ci-frame video, .ci-frame img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* Corner ticks mark the file boundary to the pixel. Everything inside them is
   saved; the dark area outside is screen. */
/* The band ciCapturePhoto burns into the JPEG, drawn live at the same corner
   and roughly the same scale (font = width/40 there). */
.ci-stamp { position: absolute; left: 0; bottom: 0; background: rgba(0,0,0,0.60); color: #fff;
  font-family: ui-monospace, Menlo, monospace; font-weight: 700; font-size: 8.5px;
  line-height: 1.5; padding: 5px 6px; white-space: pre; letter-spacing: -0.01em;
  max-width: 100%; overflow: hidden; }

/* ── the locked action bar ── */
.ci-dock { flex: 0 0 auto; overflow-y: auto; border-top: 1px solid var(--c-border); background: var(--c-surface);
  padding: 10px 15px calc(10px + env(safe-area-inset-bottom, 0px)); }
.ci-gate { display: flex; gap: 8px; align-items: center; font-size: 12px; padding: 2px 0; }
.ci-gate .g-ic { width: 15px; height: 15px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; font-size: 10px; flex: 0 0 auto; color: #fff; }
.ci-gate.ok  .g-ic { background: var(--c-success); }
.ci-gate.bad .g-ic { background: var(--c-warning); }
.ci-gate .g-tx { color: var(--c-text-2); min-width: 0; }
.ci-gate .g-tx b { font-weight: 700; }
.ci-gate .g-fix { margin-left: auto; color: var(--c-info); font-weight: 700; font-size: 11.5px;
  background: none; border: 0; cursor: pointer; padding: 0; white-space: nowrap;
  font-family: inherit; text-decoration: underline; text-underline-offset: 2px; }
.ci-warnrow { background: var(--c-warning-bg); border-radius: 9px; padding: 7px 10px; margin-top: 7px; }
.ci-warnrow b { display: block; font-size: 11.5px; color: var(--c-warning); font-weight: 700; }
.ci-warnrow p { margin: 1px 0 0; font-size: 11px; color: var(--c-text-2); }
/* Shown only after capture: it states what the button is about to write, so
   "Confirm" never has to be guessed at. */
.ci-confirmrow { background: var(--c-success-bg); border: 1px solid rgba(15,123,65,0.22);
  border-radius: 9px; padding: 8px 10px; margin-top: 7px; display: flex; align-items: center; gap: 9px; }
.ci-confirmrow .g-ic { width: 17px; height: 17px; border-radius: 50%; background: var(--c-success);
  color: #fff; display: flex; align-items: center; justify-content: center; font-size: 10px; flex: 0 0 auto; }
.ci-confirmrow b { display: block; font-size: 12px; color: var(--c-success); font-weight: 700; line-height: 1.3; }
.ci-confirmrow p { margin: 1px 0 0; font-size: 11px; color: var(--c-text-2); }
/* The post-capture question. Only one thing is being decided at this point, so
   it is asked in words rather than left implicit in a button label. */
.ci-ask { padding: 2px 2px 8px; }
.ci-ask b { display: block; font-size: 15px; font-weight: 700; letter-spacing: -0.01em; color: var(--c-text-1); }
.ci-ask p { margin: 2px 0 0; font-size: 12px; color: var(--c-text-3); }
.clock-action-btn[disabled] { background: #e9ebf4; color: #9aa0bd; box-shadow: none; cursor: not-allowed; }
.clock-action-btn[disabled]:hover { transform: none; filter: none; box-shadow: none; }
/* Compact selects so the fixed window holds them above the camera without
   scrolling. Same controls, same order, less height. */
.ci-fixed .ci-block { margin-bottom: 7px; padding: 0; background: none; border: 0;
  display: flex; align-items: center; gap: 9px; }
.ci-fixed .ci-block-label { margin-bottom: 0; flex: 0 0 78px; font-size: 12px; color: var(--c-text-3); }
.ci-fixed .ci-block-label i { font-size: 13px; }
.ci-fixed .ci-block > select { flex: 1 1 0; min-width: 0; }
/* The two pickers share a row and split it exactly. flex-basis 0 with
   min-width 0 is what makes them equal: with an auto basis each column sized
   to its own label, and "SCHEDULE *" is wider than "WORK SETUP" wrapped. */
.ci-fixed .ci-pickers { display: flex; gap: 8px; margin-bottom: 9px; }
.ci-fixed .ci-field { flex: 1 1 0; min-width: 0; display: block; }
.ci-fixed .ci-field > select { width: 100%; font-size: 12.5px; padding: 9px 7px; }
.ci-fixed .ci-flabel { display: block; margin: 0 0 4px 1px; font-size: 11.5px;
  font-weight: 700; color: var(--c-text-3); }
.ci-fixed .ci-flabel .ci-req { margin-left: 0; font-size: 11px; }
/* The work-setup notice is a row of its own, not a third item beside the
   select - as a flex sibling it shortened that dropdown the moment it
   appeared, so the two rows stopped matching. */
.ci-fixed .ci-block { flex-wrap: wrap; }
.ci-fixed .ci-block > .clock-status-note,
.ci-fixed .ci-block > div[id$="-note"] { flex: 0 0 100%; margin-top: 6px; }
.ci-block { margin-bottom: 16px; }
.ci-block-label { display: flex; align-items: center; gap: 8px; font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 700; font-size: 13px; color: var(--c-text); margin-bottom: 8px; }
.ci-block-label i { font-size: 16px; color: var(--c-brand); }
.ci-block-primary { background: rgba(3,0,131,0.04); border: 1px solid rgba(3,0,131,0.16); border-radius: var(--radius); padding: 14px; }
.ci-req { margin-left: auto; font-size: 13px; line-height: 1; color: var(--c-danger); }
/* Time In with no schedule. select.field-input so it holds over :focus, which
   the helper sets on the same element. */
.ci-field-err > select.field-input { border-color: var(--c-danger); box-shadow: 0 0 0 3px var(--c-danger-bg); }
.ci-ferr { display: flex; align-items: center; gap: 4px; margin-top: 4px;
  font-size: 11.5px; font-weight: 600; line-height: 1.3; color: var(--c-danger); }
.ci-ferr i { font-size: 13px; }
.cam-controls { position: absolute; top: 8px; right: 8px; display: flex; gap: 6px; z-index: 2; }
.cam-ctrl-btn { width: 38px; height: 38px; border-radius: 10px; border: none; background: rgba(0,0,0,0.5); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 18px; transition: background var(--transition); }
.cam-ctrl-btn:hover { background: rgba(0,0,0,0.72); }
/* Attendance: within-grace tardiness (present, not penalised) */
.att-grace { display: inline-block; font-size: 11px; font-weight: 600; color: #96690e; background: #fdf4e0; border: 1px solid #f0e0b6; border-radius: 999px; padding: 1px 9px; white-space: nowrap; }

/* ── ESS HEADER ─────────────────────────────────────── */
.ess-header {
  background: var(--c-brand);
  border-radius: var(--radius-lg);
  padding: 24px 28px;
  margin-bottom: 20px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 24px;
  flex-wrap: wrap;
}
.ess-welcome { color: rgba(255,255,255,0.5); font-size: 12.5px; font-weight: 600; margin-bottom: 4px; }
.ess-name { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; font-size: 22px; color: #fff; letter-spacing: -0.025em; }
.ess-meta { font-size: 12px; color: rgba(255,255,255,0.45); margin-top: 4px; }
.ess-clock-status { font-size: 12px; color: rgba(255,255,255,0.4); margin-top: 8px; }
.ess-clock-status strong { color: rgba(255,255,255,0.85); }
.ess-leave-stats { display: flex; gap: 16px; flex-wrap: wrap; }
.ess-leave-stat { text-align: right; border-left: 1px solid rgba(255,255,255,0.12); padding-left: 16px; }
.ess-leave-stat:first-child { border-left: none; padding-left: 0; }
.ess-leave-val { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; font-size: 28px; color: #fff; line-height: 1; }
.ess-leave-lbl { font-size: 11.5px; color: rgba(255,255,255,0.38); margin-top: 3px; }

/* ── ANNOUNCEMENT ───────────────────────────────────── */
.ann-item { padding: 16px 20px; border-bottom: 1px solid var(--c-border); display: grid; grid-template-columns: auto 1fr auto; gap: 14px; align-items: start; }
.ann-item:last-child { border-bottom: none; }   /* keeps its bottom padding: the card's own padding skips .ann-item, so 0 left the last date on the card edge */
.ann-type-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--c-brand); flex-shrink: 0; margin-top: 7px; }
.ann-type-dot.pinned { background: var(--c-warning); }
.ann-title { font-size: 13px; font-weight: 600; color: var(--c-text); margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
.ann-body { font-size: 12px; color: var(--c-text-3); line-height: 1.6; margin-bottom: 6px; }
.ann-meta { font-size: 11px; color: var(--c-text-3); }
.ann-pinned-tag { font-size: 11.5px; font-weight: 700; background: var(--c-warning-bg); color: var(--c-warning); padding: 1px 6px; border-radius: 2px; }

/* ── TOAST ──────────────────────────────────────────── */
#toast-container {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 340px;
  pointer-events: none;   /* icon + text only: a toast never takes the tap meant for what is under it */
}
/* On a phone the bottom is where Save and Time In sit: a 4-second error toast covered
   the button you needed to press again. Under the top bar instead. */
@media (max-width: 640px) {
  #toast-container { top: calc(var(--topbar-h) + 8px); bottom: auto; left: 12px; right: 12px; max-width: none; }
}
.toast {
  background: var(--c-text);
  color: #fff;
  border-radius: var(--radius);
  padding: 12px 16px;
  font-size: 13px;
  font-weight: 500;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  box-shadow: var(--shadow-lg);
  animation: slideUp 200ms ease;
  border-left: 3px solid rgba(255,255,255,0.2);
}
.toast i { font-size: 15px; flex-shrink: 0; margin-top: 1px; }
.toast.success { border-left-color: #4ade80; }
.toast.error   { border-left-color: #f87171; }
.toast.warning { border-left-color: #fbbf24; }
.toast.out { animation: slideOut 200ms ease forwards; }

/* ── LOADING ────────────────────────────────────────── */
.loading-panel { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 24px; gap: 14px; color: var(--c-text-3); }
.spinner { width: 22px; height: 22px; border: 2px solid var(--c-border); border-top-color: var(--c-brand); border-radius: 50%; animation: spin 0.7s linear infinite; }

/* Skeleton: plain grey blocks in the shape of what is loading. Screen
   skeletons (SKEL_SCREENS in app.js) are built from the screens' own layout
   classes, so only the grey parts need rules here. Reduced motion stops the
   pulse through the global rule; the blocks stay. */
.skel { display: block; background: var(--c-border); border-radius: var(--radius-sm); animation: skelPulse 1.4s ease-in-out infinite; }
.skel-line  { height: 12px; }
.skel-page  { display: flex; flex-direction: column; gap: 16px; }
.skel-page > * { margin-bottom: 0; }   /* the gap spaces the parts; a stat-grid's own 20px would add to it */
.skel-hdr   { display: flex; align-items: center; gap: 8px; min-height: 34px; }
.skel-push  { flex: 1 1 auto; }
.skel-field { height: 34px; flex: 1 1 140px; max-width: 220px; border-radius: var(--radius); }
.skel-input { height: 38px; border-radius: var(--radius); margin-top: 8px; }
.skel-stack { display: flex; flex-direction: column; gap: 10px; width: 100%; }
.skel-center { align-items: center; }
.skel-tabs  { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; min-height: 44px; padding: 8px 20px; border-bottom: 1px solid var(--c-border); }
.skel-formrow { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.skel-formrow .skel-field { max-width: none; }
.skel-tr td .skel { width: 70%; max-width: 180px; }
.skel-tr td .skel-ava { width: 28px; }
.skel-person { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; }   /* grows in a phone row-card cell (a flex row), where its % lines would collapse */
.skel-tr td > .skel-stack { flex: 1 1 auto; }
.skel-ava  { width: 28px; height: 28px; border-radius: 50%; flex: 0 0 28px; }
@media (min-width: 1024px) { .skel-tr td { height: var(--skel-h, 48px); } }   /* each table's measured row height; a phone row-card sizes itself */
.skel-labelled { flex: 1 1 140px; max-width: 240px; gap: 8px; }
.skel-labelled .skel-field { max-width: none; flex: 0 0 34px; }
.skel-cal   { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; margin-top: 16px; }
.skel-cal .skel { height: 64px; border-radius: var(--radius); }
.skel-ann   { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: start; padding: 4px 20px; border-bottom: 1px solid var(--c-border); }
.skel-ann:last-child { border-bottom: 0; }
.skel-ess-ava { width: clamp(46px, 12vh, 104px); height: clamp(46px, 12vh, 104px); border-radius: 50%; justify-self: center; }   /* the real .ess-ava sizes */
@media (max-width: 560px) { .skel-ess-ava { width: 52px; height: 52px; } }
@media (max-width: 480px) {   /* like .page-actions: the buttons drop to their own full-width row */
  .skel-hdr { flex-wrap: wrap; }
  .skel-hdr .skel-push { flex: 0 0 100%; }
  .skel-hdr .skel-field { flex: 1 1 0 !important; height: 40px; }
}
.skel-pins  { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.skel-note  { padding: 10px 20px; }
.skel-picker { margin: 12px 0; }
.skel-profile { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.skel-ava-lg { width: 56px; height: 56px; border-radius: 50%; flex: 0 0 56px; }
.skel-value { height: 14px; width: 70%; margin-top: 8px; }
.skel-list  { display: flex; flex-direction: column; }
.skel-list-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--c-border); }
.skel-list-row:last-child { border-bottom: 0; }
.skel-bars  { display: flex; flex-direction: column; gap: 14px; }
.skel-barline { height: 10px; border-radius: 99px; }
.skel-bars > :nth-child(2) .skel-barline { width: 80%; }
.skel-bars > :nth-child(3) .skel-barline { width: 62%; }
.skel-bars > :nth-child(4) .skel-barline { width: 45%; }
.skel-bars > :nth-child(5) .skel-barline { width: 30%; }
.skel-doc   { width: 100%; max-width: 640px; aspect-ratio: 1 / 1.3; margin: 0 auto; border-radius: var(--radius); }
/* A dropdown whose options are still loading (its only option carries
   data-loading) is a grey field; the rule lets go when the options arrive. */
select:has(> option[data-loading]:only-child) { color: transparent; background: var(--c-border) none; border-color: transparent;
  animation: skelPulse 1.4s ease-in-out infinite; pointer-events: none; }
/* On the brand-blue sidebar the grey would glare; a faint white reads as the same block. */
.sidebar .skel, .boot-skel-side .skel { background: rgba(255,255,255,0.12); }
/* skelLines(): the lines for a box inside a screen (pop-up, card, table body). */
.skel-block { display: flex; flex-direction: column; gap: 12px; padding: 14px 0; width: 100%; }  /* full width even in a flex row (phone table cells): its lines are % widths */
.skel-block .skel:nth-child(4n+1) { width: 92%; }
.skel-block .skel:nth-child(4n+2) { width: 74%; }
.skel-block .skel:nth-child(4n+3) { width: 84%; }
.skel-block .skel:nth-child(4n)   { width: 58%; }
.skel-label { font-size: 12px; color: var(--c-text-3); }
.skel-name { width: 120px; height: 12px; margin: 2px 0 6px; }
.skel-role { width: 70px; height: 10px; }

/* The boot placeholder mirrors #app: sidebar, top bar, content. */
.boot-skel { display: flex; }
.boot-skel-side   { width: var(--sidebar-w); flex-shrink: 0; background: var(--c-brand); padding: 0 18px; overflow: hidden; }
.boot-skel-brand  { height: var(--topbar-h); display: flex; align-items: center; border-bottom: 1px solid rgba(255,255,255,0.06); }
.boot-skel-brand .skel { width: 140px; height: 26px; }
.boot-skel-user   { display: flex; align-items: center; gap: 10px; padding: 18px 0; }
.boot-skel-user .skel-line { width: 110px; }
.boot-skel-avatar { width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; }
.boot-skel-nav    { display: flex; flex-direction: column; gap: 22px; padding-top: 10px; }
.boot-skel-nav .skel { height: 12px; width: 70%; }
.boot-skel-nav .skel:nth-child(3n+2) { width: 55%; }
.boot-skel-nav .skel:nth-child(3n)   { width: 80%; }
.boot-skel-main   { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.boot-skel-top    { height: var(--topbar-h); flex-shrink: 0; background: var(--c-white); border-bottom: 1px solid var(--c-border);
                    display: flex; align-items: center; gap: 16px; padding: 0 24px; }
.boot-skel-top .skel-line { width: 140px; height: 16px; }
.boot-skel-burger { display: none; width: 34px; height: 34px; border-radius: var(--radius); }
.boot-skel-content { flex: 1; padding: 24px; overflow: hidden; }
@media (max-width: 1279px) {
  .boot-skel-side   { display: none; }
  .boot-skel-burger { display: block; }
}
@media (max-width: 768px) {
  .boot-skel-top     { padding: 0 16px; }
  .boot-skel-content { padding: 16px; }
}
.empty-panel { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 48px 24px; gap: 10px; color: var(--c-text-3); text-align: center; }
.empty-panel i { font-size: 32px; color: var(--c-border-2); }
.empty-panel p { font-size: 13px; }

/* ── SIDEBAR OVERLAY (mobile) ───────────────────────── */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(8,9,32,0.45);
  z-index: 199;
  opacity: 0;
  transition: opacity 200ms ease;
}

/* ── PRINT ──────────────────────────────────────────── */
@media print {
  .sidebar, .topbar, .btn, .filters, .page-actions, #toast-container { display: none !important; }
  .app-main { overflow: visible !important; }
  .content { padding: 0 !important; overflow: visible !important; background: white !important; }
  .payslip { box-shadow: none !important; border: 1px solid #ccc !important; }
  .ps-doc { box-shadow: none !important; border: 1px solid #ccc !important; max-width: none !important; }
}

/* ── ANIMATIONS ─────────────────────────────────────── */
@keyframes fadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideOut{ to   { opacity: 0; transform: translateX(20px); } }
@keyframes spin    { to   { transform: rotate(360deg); } }
@keyframes skelPulse { 50% { opacity: 0.5; } }
@keyframes panelIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }

.panel-enter { animation: panelIn 180ms ease; }

/* ── DIVIDER ────────────────────────────────────────── */
.divider { height: 1px; background: var(--c-border); margin: 16px 0; }

/* ── GRID HELPERS ───────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
.grid-sidebar { display: grid; grid-template-columns: 220px 1fr; gap: 16px; }

/* Payslips — payroll-period chips (compact clickable buttons, top of page) */
.period-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.period-item {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 12px; max-width: 100%;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 12px;
  cursor: pointer; font: inherit; text-align: left;
  transition: background .13s ease, border-color .13s ease, box-shadow .13s ease;
}
.period-item:hover { border-color: var(--c-brand); background: var(--c-brand-10); }
.period-item.active { border-color: var(--c-brand); background: var(--c-brand-10); box-shadow: 0 0 0 2px var(--c-brand-20); }
.period-item.active .p-dates { color: var(--c-brand); }
/* The text takes the chip's width, so the delete icon sits at its right edge
   when a chip is full-width on a phone. */
.period-item .p-body { flex: 1 1 auto; min-width: 0; }
/* A payslip's header table is wider than a phone; it scrolls sideways instead
   of cutting off the YTD column. */
#slip-doc { overflow-x: auto; }
.p-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; background: #e8a13c; }
.p-dot.released { background: var(--c-success, #1a9e57); }
.p-dot.voided   { background: var(--c-danger, #d64545); }
.p-body  { display: flex; flex-direction: column; min-width: 0; }
.p-dates { font-size: 13px; font-weight: 700; line-height: 1.25; color: var(--c-text); white-space: nowrap; }
.p-meta  { font-size: 11px; color: var(--c-text-3); line-height: 1.35; white-space: nowrap; }
@media (max-width: 560px) {
  .period-item { flex: 1 1 100%; }
  .p-dates, .p-meta { white-space: normal; }
}
.grid-sidebar-right { display: grid; grid-template-columns: 1fr 280px; gap: 16px; align-items: start; }   /* the list no longer stretches to the compose form's height */
.flex-col { display: flex; flex-direction: column; gap: 16px; }

/* ============================================================
   MONTHLY ROSTER GRID
   ============================================================ */
.rg-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
/* Roster: one row of view controls (sub-tabs left, This month / Reference
   roster right), and the month bar, the day-off bar and the grid read as one
   card. They were three stacked rows of differently styled controls floating
   above the grid. The grid card stays the scroll container, so the bars sit
   outside it and join it by their borders. */
.rs-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.rs-top .rt-views, .rs-top .rg-tabs { margin-bottom: 0; }
.rg-toolbar { background: var(--c-surface); border: 1px solid var(--c-border); border-bottom: 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0; padding: 10px 14px; margin-bottom: 0; }
.rg-toolbar + .rg-dobar { border-radius: 0; margin-bottom: 0; border-bottom: 0; }
.rg-toolbar + .rg-card, .rg-toolbar + .rg-dobar + .rg-card { border-top-left-radius: 0; border-top-right-radius: 0; }
@media (max-width: 640px) {
  #rs-subtabs { flex: 1 1 100%; }
}
.rg-nav { display: flex; align-items: center; gap: 8px; }
.rg-month { font-weight: 700; font-size: 15px; min-width: 150px; text-align: center; color: var(--c-text); }
.rg-legend { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.rg-notk-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--c-text-2); cursor: pointer; user-select: none; white-space: nowrap; }
.rg-notk-toggle input { cursor: pointer; margin: 0; }
.rg-emp-row { display: flex; align-items: center; gap: 8px; }
.rg-emp-txt { min-width: 0; }
.rg-grip { color: var(--c-text-3); cursor: grab; display: inline-flex; align-items: center; font-size: 15px; flex: 0 0 auto; }
.rg-grip:active { cursor: grabbing; }
/* Day-Off tab: employee selection */
.rosdo-cb { width: 15px; height: 15px; flex: 0 0 auto; cursor: pointer; accent-color: var(--c-brand); }
.rg-selrow > td { background: #eef1ff; }
.rg-selrow > td.rg-emp { box-shadow: inset 3px 0 0 var(--c-brand); }

/* Day-Off control bar. Deliberately NOT .rg-toolbar: that one is
   justify-content:space-between, which flings these controls to opposite edges.
   Here everything stays left-grouped (who -> when), with only the action buttons
   pushed right, and separators marking the two groups. */
.rg-dobar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
            padding: 9px 12px; margin-bottom: 12px; background: #fff;
            border: 1px solid var(--c-border); border-radius: 10px; }
.rg-dosel { max-width: 168px; }
.rg-dosep { width: 1px; height: 22px; background: var(--c-border); margin: 0 3px; flex: 0 0 auto; }
.rg-dospace { flex: 1 1 auto; min-width: 8px; }
.rg-dolbl { font-size: 12px; color: var(--c-text-2); white-space: nowrap; }
.rg-dohint { font-size: 12px; color: var(--c-text-3); white-space: nowrap; font-weight: 400; }
.rg-docount { font-size: 12px; color: var(--c-text-2); white-space: nowrap; }
.rg-docount.on { color: var(--c-brand); font-weight: 600; }
.rg-dolink { background: none; border: 0; padding: 2px 3px; font-size: 12px; font-weight: 600;
             color: var(--c-brand); cursor: pointer; white-space: nowrap; }
.rg-dolink:hover { text-decoration: underline; }
.rg-dolink:disabled { color: var(--c-text-3); cursor: default; text-decoration: none; }

/* ── New Request: type tiles ────────────────────────────────────────────────
   Was a hardcoded repeat(4,1fr) with gap:1px over a border-coloured container,
   so an incomplete last row left a dead grey block where the container showed
   through the empty cells. Separate rounded tiles + auto-fill means a short row
   is just whitespace, and the column count follows the screen. */
.rt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
           gap: 10px; padding: 14px; }
.rt-tile { display: flex; flex-direction: column; align-items: center; justify-content: center;
           gap: 9px; min-height: 96px; padding: 18px 12px; cursor: pointer;
           font-family: inherit; background: var(--c-surface);
           border: 1px solid var(--c-border); border-radius: 10px;
           transition: border-color .15s, background .15s, box-shadow .15s, transform .15s; }
.rt-tile i    { font-size: 22px; color: var(--c-brand); }
.rt-tile span { font-size: 12px; font-weight: 600; color: var(--c-text-2);
                text-align: center; line-height: 1.3; }
.rt-tile:hover { border-color: var(--c-brand); background: var(--c-surface-2);
                 transform: translateY(-1px); box-shadow: 0 2px 8px rgba(3, 0, 131, .08); }
.rt-tile:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 2px; }
.rt-tile.active { border-color: var(--c-brand); background: #eef1ff;
                  box-shadow: inset 0 0 0 1px var(--c-brand); }
.rt-tile.active i, .rt-tile.active span { color: var(--c-brand); }
@media (max-width: 700px) {
  .rt-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 10px; }
  .rt-tile { min-height: 82px; padding: 14px 8px; gap: 7px; }
  .rt-tile i    { font-size: 20px; }
  .rt-tile span { font-size: 11px; }
}
.rg-dodays { display: flex; gap: 3px; flex: 0 0 auto; }
.rg-day { width: 38px; height: 28px; padding: 0; border: 1px solid var(--c-border);
          background: #fff; border-radius: 6px; font-size: 11px; font-weight: 600;
          color: var(--c-text-2); cursor: pointer; }
.rg-day:hover { border-color: var(--c-brand); color: var(--c-brand); }
.rg-day.on { background: var(--c-brand); border-color: var(--c-brand); color: #fff; }

/* ── Payslip adjustments ───────────────────────────────────────────────────
   Column headers so the boxes are self-explanatory, a peso prefix on the amount,
   and a coloured left edge so incentives and deductions are scannable at a glance. */
.adj-head { display: grid; grid-template-columns: 132px 1fr 148px 34px; gap: 8px;
            padding: 0 10px 5px; font-size: 11.5px; font-weight: 700; color: var(--c-text-3); }
.adj-head span:nth-child(3) { text-align: right; padding-right: 4px; }
.adj-row { display: grid; grid-template-columns: 132px 260px 148px 34px; gap: 8px; justify-content: start;
           align-items: center; margin-bottom: 6px; padding: 7px 8px 7px 9px;
           background: var(--c-surface-2); border: 1px solid var(--c-border);
           border-left: 3px solid var(--c-border); border-radius: 8px; }
.adj-row.inc { border-left-color: var(--c-success); }
.adj-row.ded { border-left-color: var(--c-danger); }
.adj-amt { position: relative; display: block; }
.adj-amt .peso { position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
                 font-size: 12px; color: var(--c-text-3); pointer-events: none; }
.adj-amt input { padding-left: 21px; text-align: right; }
.adj-del { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px;
           padding: 0; border: 1px solid var(--c-border); border-radius: 7px; background: #fff;
           color: var(--c-text-3); cursor: pointer; transition: color .15s, border-color .15s; }
.adj-del:hover { color: var(--c-danger); border-color: var(--c-danger); }
.adj-empty { display: flex; align-items: center; gap: 7px; padding: 14px 10px;
             border: 1px dashed var(--c-border); border-radius: 8px;
             color: var(--c-text-3); font-size: 12.5px; }
.adj-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.adj-sum { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
           margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--c-border); }
.adj-sum .k { font-size: 12px; color: var(--c-text-2); }
.adj-sum .v { font-size: 13px; font-weight: 700; }
.adj-net { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; margin-left: auto; }
.adj-net .from { font-size: 12px; color: var(--c-text-3); text-decoration: line-through; }
.adj-net .to   { font-size: 15px; font-weight: 800; color: var(--c-brand); }
@media (max-width: 700px) {
  .adj-head { display: none; }
  .adj-row { grid-template-columns: 1fr 34px; grid-template-areas: 't x' 'l l' 'a a'; gap: 6px; }
  .adj-row .adj-t { grid-area: t; } .adj-row .adj-amt { grid-area: a; }
  .adj-row input[type=text] { grid-area: l; }
  .adj-row .adj-del { grid-area: x; }
  .adj-net { margin-left: 0; width: 100%; }
}

/* Payslip picker: a run has ~100 slips, so filter first, then pick. */
.slip-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
                margin-bottom: 8px; }
.slip-filters .slip-f-q { min-width: 180px; flex: 1 1 180px; max-width: 260px; }
.slip-filters .filter-select { max-width: 190px; }
.slip-picker { display: flex; flex-wrap: wrap; gap: 6px; max-height: 132px;
               overflow-y: auto; padding: 2px; margin-bottom: 10px; }

/* Employee chip: checkbox (select for a bulk adjustment) + name (open payslip) */
.slip-chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 6px 2px 7px;
             border: 1px solid var(--c-border); border-radius: 7px; background: #fff; }
.slip-chip.cur { border-color: var(--c-brand); box-shadow: inset 0 0 0 1px var(--c-brand); }
.slip-chip.sel { background: #eef1ff; border-color: var(--c-brand); }
.slip-chip button { border: 0; background: none; padding: 3px 2px; font: inherit; font-size: 12px;
                    color: var(--c-text-2); cursor: pointer; white-space: nowrap; }
.slip-chip.cur button { color: var(--c-brand); font-weight: 700; }
.slip-chip button:hover { color: var(--c-brand); }

/* Bulk adjustment bar — appears once at least one employee is ticked */
.slip-bulk-hint { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-bottom: 10px;
                  padding: 8px 10px; border: 1px dashed var(--c-border); border-radius: 8px;
                  color: var(--c-text-3); font-size: 12px; }
.slip-bulk-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 10px;
                 padding: 9px 11px; border: 1px solid var(--c-border); border-left: 3px solid var(--c-success);
                 border-radius: 8px; background: var(--c-surface-2); }
.slip-bulk-bar.ded { border-left-color: var(--c-danger); }
.slip-bulk-n { font-size: 12px; font-weight: 700; color: var(--c-brand); white-space: nowrap; }

/* Deep-link affordances: a cell/row that has something to review is clickable */
.rg-review { cursor: pointer; position: relative; }
.rg-c.rg-review::after { content: ''; position: absolute; top: 3px; right: 3px; width: 5px; height: 5px;
                         border-radius: 50%; background: var(--c-warning); }
.rg-c.rg-review:hover { outline: 2px solid var(--c-brand); outline-offset: -2px; }
@keyframes reqFlash { 0%,100% { background: transparent; } 25% { background: rgba(255,193,7,.28); } }
tr.req-flash > td { animation: reqFlash 2.2s ease; }
tr.req-flash > td:first-child { box-shadow: inset 3px 0 0 var(--c-warning); }

/* Early Bird scope toggle (This Month / All-Time running tally) */
.eb-scope { display: inline-flex; flex: 0 0 auto; border: 1px solid var(--c-border); border-radius: 8px; overflow: hidden; }
.eb-scope-btn { white-space: nowrap; }
.eb-scope-btn { border: 0; background: #fff; padding: 5px 10px; font: inherit; font-size: 12px;
                color: var(--c-text-2); cursor: pointer; white-space: nowrap; }
.eb-scope-btn + .eb-scope-btn { border-left: 1px solid var(--c-border); }
.eb-scope-btn.active { background: var(--c-brand); color: #fff; font-weight: 600; }

/* Loans tab */
.loan-filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.loan-fbtn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--c-border);
             background: #fff; border-radius: 999px; padding: 7px 14px; font: inherit; font-size: 13px;
             color: var(--c-text-2); cursor: pointer; transition: all .12s; }
.loan-fbtn:hover { border-color: var(--c-brand); color: var(--c-brand); }
.loan-fbtn.active { background: var(--c-brand); color: #fff; border-color: var(--c-brand); font-weight: 600; }
.loan-fcount { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px;
               padding: 0 5px; border-radius: 999px; background: rgba(0,0,0,.08); font-size: 11px; font-weight: 700; }
.loan-fbtn.active .loan-fcount { background: rgba(255,255,255,.25); }

.loan-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap: 16px; }
.loan-card2 { background: #fff; border: 1px solid var(--c-border); border-radius: 14px; padding: 18px;
              display: flex; flex-direction: column; gap: 14px; transition: box-shadow .15s, border-color .15s; }
.loan-card2:hover { box-shadow: 0 6px 20px rgba(16,24,40,.08); border-color: #d7dbe3; }
.loan-card2-paid { background: #f6fef9; border-color: #cdeede; }

.loan-top { display: flex; align-items: center; gap: 12px; }
.loan-avatar { width: 42px; height: 42px; border-radius: 12px; flex-shrink: 0; display: flex; align-items: center;
               justify-content: center; font-weight: 700; font-size: 15px; color: #fff;
               background: var(--c-brand); }
.loan-who { flex: 1; min-width: 0; }
/* One line each, ellipsis past it. A branch like "Greenwood PH - San Rafael"
   wrapped to two lines and pushed that card's whole body down, so no two cards
   in a row lined up. */
.loan-name, .loan-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* THE REASON — why the money was advanced. Fixed two-line reserve so every card
   hands off to the figure block on the same baseline whether the reason is two
   words or twenty. */
/* The reason, plainly. min-height fought -webkit-line-clamp: the box grew past
   the clamp and overflow:hidden sliced a third line in half, so a long request
   ended mid-word with the remains still visible. Fixed HEIGHT of exactly two
   lines, so it clamps where it is meant to and every card is the same height. */
.loan-reason { font-size: 13px; line-height: 1.5; color: var(--c-text-2, #475467);
               display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
               overflow: hidden; height: 3em; }
.loan-reason-none { color: var(--c-text-3, #98a2b3); font-style: italic; }

/* The figure needs no label. In a cash-advance ledger the big number is what is
   still owed - writing OUTSTANDING above it explains nothing and adds a second
   thing to read. The quiet line beneath carries the principal and the state. */
.loan-money { display: flex; flex-direction: column; gap: 2px; }
.loan-context { font-size: 12.5px; color: var(--c-text-3); }

/* Solid, not a dashed outline. This is the primary action on the card. */
/* Quiet until wanted. Four solid navy slabs down a grid pull more attention than
   the balances they sit under; a tint reads as available without competing. */
.loan-force-btn { width: 100%; margin-top: auto; border: 1px solid transparent; border-radius: 10px;
                  padding: 10px 14px; font-weight: 600; font-size: 13px; cursor: pointer;
                  background: rgba(3,0,131,.06); color: var(--c-brand);
                  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
                  transition: background .15s, color .15s, border-color .15s; }
.loan-force-btn:hover { background: var(--c-brand); color: #fff; border-color: var(--c-brand); }
.loan-name { font-weight: 650; font-size: 15px; line-height: 1.25; color: var(--c-text); }
.loan-sub { font-size: 11.5px; color: var(--c-text-3); margin-top: 1px; }
.loan-status { flex-shrink: 0; font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: 999px; white-space: nowrap; }
.loan-status-paid { background: #d8f5e5; color: #067647; display: inline-flex; align-items: center; gap: 4px; }
.loan-status-open { background: #fff2d9; color: #9a6a00; }
.loan-status-new  { background: #eef1f6; color: #667085; }

.loan-amt-big { font-size: 25px; font-weight: 700; letter-spacing: -.02em; line-height: 1.15; color: var(--c-text); }
.loan-amt-paid { color: #067647; }

.loan-bar { position: relative; display: flex; height: 8px; border-radius: 999px; overflow: hidden; background: #eef1f6; }
.loan-bar-paid { background: linear-gradient(90deg, #12b76a, #099250); }
.loan-bar-sched { background: repeating-linear-gradient(45deg, #f5c451, #f5c451 4px, #ffd979 4px, #ffd979 8px); }

.loan-pmts { display: flex; flex-direction: column; gap: 2px; border-top: 1px solid var(--c-border); padding-top: 10px; }
.loan-pmt { display: grid; grid-template-columns: 1fr auto auto auto; align-items: center; gap: 10px;
            padding: 6px 4px; border-radius: 8px; }
.loan-pmt:hover { background: #f8f9fb; }
.loan-pmt-amt { font-weight: 650; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.loan-pmt-date { font-size: 12px; color: var(--c-text-2); white-space: nowrap; }
.loan-pmt-ctrls { display: flex; gap: 2px; }
.loan-pill { display: inline-block; font-size: 10.5px; font-weight: 600; padding: 2px 8px; border-radius: 999px; }
.loan-pill-done  { background: #d8f5e5; color: #067647; }
.loan-pill-sched { background: #fff2d9; color: #9a6a00; }
.loan-pill-force { background: #e4e9ff; color: #3538cd; }
.loan-iconbtn { border: none; background: transparent; width: 26px; height: 26px; border-radius: 7px; cursor: pointer;
                color: var(--c-text-3); display: inline-flex; align-items: center; justify-content: center; transition: all .12s; }
.loan-iconbtn:hover { background: #eef1f6; color: var(--c-text); }
.loan-iconbtn-danger:hover { background: #fee4e2; color: #d92d20; }


/* Early Bird / Needs Attention - compact bar chart */
.eb-bars { display: flex; flex-direction: column; gap: 10px; padding: 4px 2px 2px; }
.eb-bar-row { display: flex; align-items: center; gap: 10px; }
.eb-bar-rank { width: 22px; text-align: center; font-size: 16px; flex-shrink: 0; }
.eb-bar-main { flex: 1; min-width: 0; }
.eb-bar-name { font-size: 13px; font-weight: 600; color: var(--c-text); display: flex; align-items: baseline;
               gap: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; }
.eb-bar-sub { font-size: 11px; font-weight: 400; color: var(--c-text-3); white-space: nowrap; }
.eb-bar-track { height: 7px; background: #eef1f6; border-radius: 999px; overflow: hidden; }
.eb-bar-fill { height: 100%; border-radius: 999px; transition: width .4s ease; min-width: 3px; }
.eb-bar-val { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap;
              flex-shrink: 0; min-width: 52px; text-align: right; }

/* Attendance Concerns - compact detailed list, NO medals (it's a concern, not a prize) */
.lt-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 2px 20px; }
.lt-row { display: flex; align-items: center; gap: 10px; padding: 8px 2px; border-bottom: 1px solid var(--c-border); }
.lt-row:last-child { border-bottom: none; }
.lt-num { width: 20px; text-align: center; font-size: 12px; font-weight: 600; color: var(--c-text-3); flex-shrink: 0; }
.lt-info { flex: 1; min-width: 0; }
.lt-name { font-size: 13.5px; font-weight: 600; color: var(--c-text); }
.lt-branch { font-size: 11px; color: var(--c-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lt-chips { display: flex; gap: 5px; flex-shrink: 0; }
.lt-chip { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; white-space: nowrap; }
.lt-chip-late { background: #fff2d9; color: #9a6a00; }
.lt-chip-abs  { background: #fee4e2; color: #b42318; }
.slip-bulk-div { width: 1px; height: 20px; background: var(--c-border); }
@media (max-width: 700px) {
  .slip-bulk-bar { gap: 6px; }
  .slip-bulk-div { display: none; }
  .slip-bulk-bar .adj-amt { width: 100% !important; }
}
@media (max-width: 640px) {
  .slip-filters .filter-select, .slip-filters .slip-f-q { max-width: 100%; flex: 1 1 140px; }
  .slip-picker { max-height: 110px; }
}
@media (max-width: 640px) {
  .adj-row { grid-template-columns: 1fr 1fr; grid-template-areas: 't a' 'l l' 'x x'; }
  .adj-row .adj-t { grid-area: t; } .adj-row .adj-a { grid-area: a; }
  .adj-row input[type=text] { grid-area: l; }
  .adj-row .action-btn { grid-area: x; justify-self: end; }
}
@media (max-width: 900px) {
  .rg-dosep, .rg-dospace { display: none; }
  .rg-dobar { gap: 7px; }
  .rg-dosel { max-width: 100%; flex: 1 1 140px; }
}
.rg-table tr.rg-dragging { opacity: 0.45; }
.rg-table tr.rg-drop-before > td { box-shadow: inset 0 2px 0 0 var(--c-brand, #030083); }
.ot-hrs-wrap { display: inline-flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 12px; }
.ot-hrs-in { width: 74px; padding: 3px 6px; border: 1px solid var(--c-border); border-radius: var(--radius); font-family: inherit; font-size: 12px; color: var(--c-text); background: var(--c-white); }
/* A time box sized for hours only cut off its AM/PM: "07:18" was 7:18 PM and nothing on
   screen said so, so a time typed into the empty Out box could land 12 hours off.
   Let the browser size it to its own fields (hh:mm AM/PM + picker). */
.ot-hrs-in[type="time"], .ot-hrs-in[type="date"] { width: auto; }
/* Anomaly row actions (two approvals + Remove Time Out + Mark Absent): wrap, two a row,
   right-aligned, instead of running off the end of the Action column. */
.an-acts { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; max-width: 330px; }
.req-tabs { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 14px; border-bottom: 1px solid var(--c-border); }
.req-tab { display: inline-flex; align-items: center; gap: 6px; padding: 9px 14px; font-size: 13px; font-weight: 600; color: var(--c-text-2); background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; white-space: nowrap; }
.req-tab:hover { color: var(--c-text-1); }
.req-tab.active { color: var(--c-brand, #030083); border-bottom-color: var(--c-brand, #030083); }
.req-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; font-size: 11px; font-weight: 700; color: #fff; background: var(--c-brand, #030083); border-radius: 9px; }
.ot-delta { display: inline-block; margin-left: 4px; font-size: 11px; font-weight: 700; }
.ot-delta-neg { color: #dc2626; }
.ot-delta-pos { color: #16a34a; }
.rg-lg { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--c-text-2); }
.rg-sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

.rg-card { padding: 0; overflow: auto; max-height: 72vh; }
.rg-table { border-collapse: separate; border-spacing: 0; font-size: 9px; }
.rg-table th, .rg-table td { border-right: 1px solid var(--c-border); border-bottom: 1px solid var(--c-border); }
.rg-table thead th { position: sticky; top: 0; z-index: 2; background: var(--c-surface-2); }

.rg-emp, .rg-emp-h { position: sticky; left: 0; z-index: 1; background: var(--c-surface);
  min-width: 154px; max-width: 154px; text-align: left; padding: 6px 10px; }
.rg-table thead th.rg-emp, .rg-table thead th.rg-emp-h { z-index: 3; background: var(--c-surface-2); }
.rg-emp-n { font-size: 12px; font-weight: 600; color: var(--c-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rg-emp-d { font-size: 10px; color: var(--c-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.rg-d { min-width: 42px; max-width: 42px; text-align: center; padding: 3px 0; }
.rg-dnum { font-size: 11px; font-weight: 700; color: var(--c-text); }
.rg-dow { font-size: 9px; color: var(--c-text-3); text-transform: uppercase; letter-spacing: .3px; }
.rg-d.rg-wknd { background: #eff1f7; }

.rg-c { width: 42px; min-width: 42px; max-width: 42px; height: 34px; text-align: center;
  font-size: 9px; font-weight: 600; padding: 1px; line-height: 1.15; vertical-align: middle; }
.rg-c .rg-t { display: inline-block; }
.rg-c .rg-t b { font-size: 11px; font-weight: 700; }
.rg-c .rg-mark { display: block; font-size: 11px; line-height: 1; margin-top: 1px; }
/* Fit the month in the card - no sideways scroll on a laptop. The name column
   keeps its width and the days share the rest (about 30px each at 1366, 49px at
   1920). The grid also drops the card's 20px side margins: it is the scroll
   container and never needed them, and they added 40px to the overflow.
   Phones keep the scroll: 31 columns cannot fit 390px. */
@media (min-width: 900px) {
  .rg-card > .rg-table { margin-left: 0 !important; margin-right: 0 !important; width: 100%; table-layout: fixed; }
  .rg-card .rg-table .rg-emp-h { width: 154px; }
  .rg-card .rg-table .rg-abs-h { width: 48px; }
  .rg-card .rg-table .rg-d, .rg-card .rg-table .rg-c { width: auto; min-width: 0; max-width: none; }
  .rg-card .rg-table .rg-c { overflow: hidden; }
}
@media (min-width: 900px) and (max-width: 1599px) {
  .rg-card .rg-table .rg-c { font-size: 8px; }   /* "Onsite" fits a ~28px cell */
}
.rg-edit { cursor: pointer; }
.rg-edit:hover { outline: 2px solid var(--c-brand); outline-offset: -2px; }

.rg-onsite  { background: #dff3e6; color: #0d6d3a; }
.rg-wfh     { background: #e2effb; color: #1d5fad; }
.rg-offduty { background: #ece9fb; color: #030083; }
.rg-off     { background: #eceef5; color: #8a8da8; }
.rg-planon  { background: #f1faf4; color: #2f8a56; }
.rg-half    { background: #fdf3e0; color: #96650d; }
.rg-absent  { background: #fbe4e4; color: #c0202c; }
.rg-leave   { background: #fff4e0; color: #b45309; }
.rg-holiday { background: #fdeaf3; color: #a3316f; }
.rg-holduty { background: #efe6fb; color: #5a30b0; font-weight: 700; }
.rg-pending { background: #fef3c7; color: #92400e; font-weight: 700; }
/* Late is a WORKED day - green like Onsite, not red like an absence - but
   distinguishable, because the payslip charges a tardy deduction for it and
   219 late days in August rendered identically to on-time ones. */
.rg-late    { background: #e8f5ec; color: #7a6410; }

.rg-sw.rg-onsite  { background: #dff3e6; border: 1px solid #b6e0c6; }
.rg-sw.rg-wfh     { background: #e2effb; border: 1px solid #b9d6f2; }
.rg-sw.rg-offduty { background: #ece9fb; border: 1px solid #cbc4f2; }
.rg-sw.rg-off     { background: #eceef5; border: 1px solid #d3d6e4; }
.rg-sw.rg-planon  { background: #f1faf4; border: 1px solid #bfe6cd; }
.rg-sw.rg-half    { background: #fdf3e0; border: 1px solid #f0d9a8; }
.rg-sw.rg-absent  { background: #fbe4e4; border: 1px solid #f0bcbc; }

.rg-abs { min-width: 48px; max-width: 48px; text-align: center; font-size: 11px; font-weight: 700; color: var(--c-danger); }
.rg-table thead th.rg-abs-h { font-size: 9px; color: var(--c-text-3); line-height: 1.1; }


.rg-menu { position: absolute; z-index: 100; background: var(--c-surface); border: 1px solid var(--c-border-2);
  border-radius: var(--radius); box-shadow: 0 10px 28px rgba(8,9,32,.18); width: 188px; padding: 5px; }
.rg-menu-h { font-size: 11px; color: var(--c-text-3); padding: 4px 8px 6px; font-weight: 600; }
.rg-menu-i { display: flex; align-items: center; gap: 9px; width: 100%; padding: 8px 10px; border: 0;
  background: transparent; border-radius: var(--radius-sm); cursor: pointer; font-size: 13px; color: var(--c-text); text-align: left; font: inherit; }
.rg-menu-i:hover { background: var(--c-surface-2); }
.rg-menu-i.active { background: var(--c-brand-10); color: var(--c-brand); font-weight: 600; }
.rg-menu-i i { font-size: 16px; }

.rg-tabs { display: inline-flex; gap: 2px; background: var(--c-surface-2); border: 1px solid var(--c-border); border-radius: var(--radius); padding: 3px; margin-bottom: 14px; }
.rg-tab { display: inline-flex; align-items: center; gap: 6px; padding: 7px 15px; border: 0; background: transparent; border-radius: var(--radius-sm); cursor: pointer; font: inherit; font-size: 13px; font-weight: 600; color: var(--c-text-3); }
.rg-tab.active { background: var(--c-surface); color: var(--c-brand); box-shadow: 0 1px 3px rgba(8,9,32,.12); }
.rg-tab:hover:not(.active) { color: var(--c-text); }
@media (max-width: 480px) {
  /* Three tabs fit one line only without their icons; with them "Day Off"
     and "Actual Attendance" each broke onto two lines. */
  .rg-tabs { display: flex; }
  .rg-tab { flex: 1 1 auto; justify-content: center; padding: 7px 10px; white-space: nowrap; }
  .rg-tab i { display: none; }
}
.rg-sw.rg-dutybox { background: #fff; border: 1px solid var(--c-border-2); }
.rg-sw.rg-holiday { background: #fdeaf3; border: 1px solid #f2c3da; }
.rg-sw.rg-holduty { background: #efe6fb; border: 1px solid #cdb6f0; }

/* holiday date-headers */
.rg-dclk { cursor: pointer; }
.rg-dclk:hover { background: var(--c-brand-10); }
.rg-d.rg-dhol { background: #fdeef4; }
.rg-holtag { display: inline-block; margin-top: 2px; font-size: 7px; font-weight: 800; letter-spacing: .3px;
  padding: 1px 3px; border-radius: 3px; line-height: 1.35; color: #fff; }
.rg-holtag-reg { background: #a3316f; }
.rg-holtag-snw { background: #b45309; }
.rg-holtag-sw  { background: #5b6273; }
.rg-menu-clear { color: var(--c-danger); }
.rg-menu-clear:hover { background: var(--c-danger-bg); }

/* Attendance photo viewer */
.att-photo-row { display: flex; gap: 16px; }
.att-photo-cell { flex: 1; min-width: 0; }
/* A phone selfie is portrait: at the cell's full width it drew 456x809 and the
   face sat below the pop-up's fold. Capped to 60% of the screen, whole, and the
   link still opens it full size. */
.att-photo-img { display: block; width: auto; max-width: 100%; max-height: 60vh; height: auto; object-fit: contain; border-radius: var(--radius-sm);
  border: 1px solid var(--c-border); background: #0b0c18; cursor: zoom-in; }
@media (max-width: 700px) { .att-photo-row { flex-direction: column; } }

/* Clock-in location status (under the camera) */

.action-btn:disabled { opacity: .35; cursor: not-allowed; }
.action-btn:disabled:hover { background: transparent; }

/* Time-In requirements checklist */
.req-row { display: flex; gap: 11px; align-items: flex-start; padding: 12px 13px;
  border: 1px solid var(--c-border); border-radius: var(--radius-sm); margin-bottom: 10px; }
.req-ic { flex: 0 0 auto; width: 27px; height: 27px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 15px; }
.req-ok .req-ic  { background: var(--c-success-bg); color: var(--c-success); }
.req-bad .req-ic { background: var(--c-danger-bg);  color: var(--c-danger); }
.req-t { font-weight: 600; margin-bottom: 3px; }
.req-steps { font-size: 12px; color: var(--c-text-2); line-height: 1.65; }
.req-note { display: flex; gap: 9px; align-items: flex-start; padding: 11px 12px; margin-bottom: 12px;
  background: var(--c-warning-bg); color: var(--c-warning); border: 1px solid #f3ddb8;
  border-radius: var(--radius-sm); font-size: 12px; line-height: 1.55; }
.req-note i { font-size: 16px; flex: 0 0 auto; margin-top: 1px; }

/* Clock-location modal rows */
.att-loc-row { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--c-border); border-radius: var(--radius-sm); margin-bottom: 10px; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1024px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: 1fr 1fr; }
}

/* The menu is a drawer below 1280px, not only on phones. Held open at 252px it
   left a 1024px tablet 724px of content, and Employees, Attendance, Requests,
   Ledger, Accounts and Audit Trail all scrolled sideways with their action
   buttons off screen. 1280 keeps it: the narrowest common laptop. */
@media (max-width: 1279px) {
  .topbar-hamburger { display: flex; }
  .sidebar {
    position: fixed;
    left: 0;
    max-width: 82vw;
    transform: translateX(-100%);
    transition: transform 220ms cubic-bezier(.22,.61,.36,1);
    will-change: transform;
    z-index: 200;
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-close {
    display: flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border: 0; border-radius: var(--radius);
    background: transparent; color: rgba(255,255,255,0.7); font-size: 18px; cursor: pointer;
  }
  .sidebar-overlay.active { display: block; opacity: 1; }
}

@media (max-width: 768px) {
  .content { --content-pad: 16px; }
  .topbar  { padding: 0 16px; }

  .stat-grid { grid-template-columns: 1fr 1fr; }
  /* minmax(0,1fr), not 1fr: a bare 1fr cannot shrink below its content, so the
     399px payslip pushed My Payslips 28px wider than a 390px phone. */
  .grid-2, .grid-sidebar, .grid-sidebar-right, .grid-3 { grid-template-columns: minmax(0, 1fr); }
  #my-slip-doc { overflow-x: auto; }
  .form-row, .form-row.cols3 { grid-template-columns: 1fr; }

  .clock-layout { grid-template-columns: 1fr; }
  .payslip-body { grid-template-columns: 1fr; }
  .payslip-col:first-child { border-right: none; border-bottom: 1px solid var(--c-border); }
  .payslip-meta { grid-template-columns: 1fr; }
  .payslip-meta-item { border-right: none; border-bottom: 1px solid var(--c-border); }
  .ps-cols { grid-template-columns: 1fr; }
  .ps-col:last-child { padding-left: 0; border-left: none; }
  .ps-col:first-child { padding-right: 0; }

  .ess-header { flex-direction: column; padding: 18px 18px; gap: 14px; }
  .ess-name { font-size: 18px; }
  .ess-leave-stats { justify-content: flex-start; gap: 0; width: 100%; }
  .ess-leave-stat { text-align: left; flex: 1; border-left: 1px solid rgba(255,255,255,0.14); padding: 0 14px; }
  .ess-leave-stat:first-child { padding-left: 0; }
  .ess-leave-val { font-size: 22px; }
  .ess-leave-lbl { font-size: 11px; }
  /* card headers with a right-side button: keep them on one row, tidy */
  .card-header { gap: 8px; flex-wrap: wrap; }
  .card-header .btn-sm { padding: 5px 10px; font-size: 12px; }

  .login-root { grid-template-columns: 1fr; }
  .login-brand-panel { display: none; }
  .login-form-panel { padding: 40px 28px; }
}

@media (max-width: 480px) {
  .stat-grid { grid-template-columns: 1fr; }
  /* A bottom sheet suits tall, scrolling content. A two-field form pinned to
     the bottom leaves most of the screen empty and puts Save on the phone's
     gesture bar. Centre it instead, keep the full width, and let a genuinely
     tall modal grow to 88vh and scroll inside itself. */
  .modal {
    max-width: 100%;
    max-height: 88vh;
    overflow-y: auto;
    border-radius: var(--radius);
  }
  .modal-backdrop {
    align-items: center;
    padding: 16px 12px calc(16px + env(safe-area-inset-bottom, 0px));
  }
}

/* ── INLINE-GRID RESPONSIVE OVERRIDES ──────────────────────────────────
   Several panels (payroll, payslip, attendance) set grid-template-columns
   inline for their desktop layout. Inline styles outrank stylesheet media
   queries, so without these !important rules those multi-column grids never
   collapse — causing horizontal overflow and clipped content (e.g. the
   Compute Payroll button) on tablets and phones. */
@media (max-width: 1024px) {
  .stat-grid[style*="grid-template-columns"] { grid-template-columns: repeat(2, 1fr) !important; }
}
@media (max-width: 768px) {
  .grid-2[style*="grid-template-columns"],
  .grid-3[style*="grid-template-columns"] { grid-template-columns: 1fr !important; }
  .stat-grid[style*="grid-template-columns"],
  [style*="grid-template-columns:repeat(4"] { grid-template-columns: repeat(2, 1fr) !important; }
}
@media (max-width: 480px) {
  .stat-grid[style*="grid-template-columns"],
  [style*="grid-template-columns:repeat(4"] { grid-template-columns: 1fr !important; }
}

/* ============================================================
   RESPONSIVE ENHANCEMENTS  (extends the 1024/768/480 breakpoints
   above — adds mobile data-tables, filter stacking, and a few
   fixed-width fixes. No desktop rules are changed.)
   ============================================================ */

/* ── DATA TABLES → STACKED CARDS (phones/tablets) ──────────
   Wide HR tables (Employees, Requests, Payroll, Schedules, the
   11-column Attendance grid, etc.) become one labelled card per
   row instead of scrolling sideways. Labels come from each cell's
   data-label (set by labelizeTables() in app.js from the <thead>).
   Scoped to tables that have a header; narrow 2-column reference
   tables (no <thead>) keep their normal layout. 1023, not 768: below 1024px
   (the drawer gives the page its full width from there up) Employees, Ledger
   and Accounts need 940-965px as tables and hid their action buttons past
   the edge. */
@media (max-width: 1023px) {
  .tbl-wrap { overflow-x: visible; }

  table.data-tbl:has(thead) { display: block; background: transparent; }
  table.data-tbl:has(thead) thead { display: none; }
  /* The 10px between these row-cards was showing the enclosing .card's
     white, so a dozen separate cards read as one chained ribbon. The
     tbody carries the page colour here, and the gaps become gaps. */
  table.data-tbl:has(thead) tbody { display: block; background: var(--c-bg);
                                    padding: 10px; border-radius: var(--radius); }

  table.data-tbl:has(thead) tbody tr {
    display: block;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    background: var(--c-surface);
    margin-bottom: 10px;
    padding: 2px 0;
    box-shadow: var(--shadow-sm);
  }
  table.data-tbl:has(thead) tbody tr:last-child { margin-bottom: 0; }
  table.data-tbl:has(thead) tbody tr:hover { background: var(--c-surface); }

  table.data-tbl:has(thead) tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    width: auto;
    padding: 9px 14px;
    border: none;
    border-bottom: 1px solid var(--c-border);
    text-align: right;
    white-space: normal;
  }
  table.data-tbl:has(thead) tbody td:last-child { border-bottom: none; }

  /* field label, pulled from the header row */
  table.data-tbl:has(thead) tbody td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 40%;
    text-align: left;
    font-size: 11.5px;
    font-weight: 700;
    color: var(--c-text-3);
    /* The label is the column name, never the value's style: a .t-mono cell
       (Employee ID, amounts, dates) printed its label in the number font too. */
    font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
    font-style: normal; letter-spacing: normal; text-transform: none;
  }
  /* action / unlabelled cells: let the buttons span the row */
  table.data-tbl:has(thead) tbody td:not([data-label]) { justify-content: flex-end; }
  table.data-tbl:has(thead) tbody td:not([data-label]) > * { flex: 0 1 auto; }
  table.data-tbl:has(thead) tbody td[colspan] { justify-content: center; text-align: center; }

  /* keep value-side content from being squashed */
  table.data-tbl:has(thead) tbody td > * { min-width: 0; }

  /* FREE TEXT (reason, remarks, notes) stacks under its label and reads left to
     right. The default row is label-left / value-right, which is correct for a
     date or a badge but wrong for a sentence: right-aligned wrapped prose gives
     every line a different starting x, and the eye has to hunt for each one. A
     40% label also leaves barely half the screen for text that needs all of it. */
  table.data-tbl:has(thead) tbody td.td-prose {
    flex-wrap: wrap;
    align-items: flex-start;
    text-align: left;
    gap: 4px;
  }
  table.data-tbl:has(thead) tbody td.td-prose[data-label]::before {
    flex: 0 0 100%;
  }
  table.data-tbl:has(thead) tbody td.td-prose > *,
  table.data-tbl:has(thead) tbody td.td-prose { max-width: 100%; }

  /* Long single-token values (an address, an email) must break rather than force
     the row wider than the screen. */
  table.data-tbl:has(thead) tbody td { overflow-wrap: anywhere; }
  /* The OT minutes editor sits on the value side: its inputs line up with the
     right-aligned summary under them, not with the label column. */
  table.data-tbl:has(thead) tbody td .ot-hrs-wrap { justify-content: flex-end; }
}
/* On a tablet one card spanning the width put each label 600px from its value.
   Cards are at least 300px, so a full-width table gets two or three per row and
   a table in a half-width card (Statutory Tables) stays at one. */
@media (max-width: 1023px) {
  table.data-tbl:has(thead) tbody { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 10px; align-items: start; }
  table.data-tbl:has(thead) tbody tr { margin-bottom: 0; }
  table.data-tbl:has(thead) tbody tr:has(td[colspan]) { grid-column: 1 / -1; }
}
/* The grey well under the row-cards was rounded, but on most screens it runs
   edge to edge in the card, between the filter bar and the footer: its corners
   left four white wedges where it met them. Square by default; rounded only
   where white padding surrounds it on purpose (a pop-up or a card body).
   A lone message row (nothing found, loading) is not a record, so it is not
   drawn as a shadowed card floating in the well - it reads as on desktop. */
@media (max-width: 1023px) {
  table.data-tbl:has(thead) tbody { border-radius: 0; }
  :is(.modal-body, .card-body, .card-body-sm) table.data-tbl:has(thead) tbody { border-radius: var(--radius); }
  table.data-tbl:has(thead) tbody:has(> tr:only-child > td[colspan]:only-child) { background: transparent; padding: 0; }
  table.data-tbl:has(thead) tbody > tr:only-child:has(> td[colspan]:only-child) { border: 0; box-shadow: none; background: transparent; }
}

/* ── FILTER BAR (search + selects) ─────────────────────────
   Let controls grow and wrap to full width instead of sitting at
   fixed widths with ragged gaps. Matters most for panels with
   several filter dropdowns (e.g. Attendance, Section 12). */
@media (max-width: 768px) {
  .filters { padding: 12px 16px; gap: 10px; }
  .filters .search-box   { flex: 2 1 220px; min-width: 0; }
  .filters .search-input { width: 100%; }
  .filters .filter-select,
  .filters select        { flex: 1 1 150px; min-width: 0; max-width: none; }
  /* keep the legacy field-input filters (if any) consistent */
  .filters .field-input,
  .filters .field-input.no-icon { flex: 1 1 150px; min-width: 0; max-width: none; }
}

/* ── TABS: scroll instead of overflow when many are present ── */
@media (max-width: 768px) {
  .tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { height: 0; }
}

/* ── SIDE PANEL: full width on small screens ─────────────── */
@media (max-width: 480px) {
  .side-panel { width: 100%; max-width: 100%; }
}

/* ── PAGE HEADER ACTIONS + TOPBAR on phones ──────────────── */
@media (max-width: 480px) {
  .page-actions { width: 100%; }
  .page-actions .btn { flex: 1 1 auto; justify-content: center; }
  /* free up space in the cramped top bar */
  .topbar { gap: 10px; padding: 0 14px; }
  /* The clock STAYS: the kiosk no longer carries one of its own and this
     is the only one left. */
  .topbar-time { padding-right: 0; border-right: 0; white-space: nowrap; }
  /* One line, always. "Incentives & Deductions" wrapped to two and took the
     clock with it. A title too long for the bar ends in an ellipsis instead. */
  .topbar-title { font-size: 14px; flex: 1 1 auto; min-width: 0;
                  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* ── Mobile performance helpers ── */
@media (max-width: 768px) {
  .card, .tbl-wrap { contain: content; }
  .content { -webkit-overflow-scrolling: touch; }
  .nav-item, .btn, .action-btn, .myps-item, .att-cell, .queue-item { -webkit-tap-highlight-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* Mounted panels (kept in DOM; visibility toggled on nav) */
.panel-host { display: block; max-width: 1600px; }

/* ── Performance Eval ────────────────────────────────────────────────────── */
/* The attendance record, shown above the scores. A rater who can see the actual
   late count is not rating from memory, which is the documented failure mode of
   every rating scale. */
.perf-facts { display: flex; flex-wrap: wrap; gap: 22px; padding: 12px 14px; margin-bottom: 16px;
              background: var(--c-surface, #f7f8fc); border-radius: 10px; }
.perf-facts > div { font-size: 11.5px; color: var(--c-text-3); line-height: 1.3; }
.perf-facts span { display: block; font-size: 19px; font-weight: 700; color: var(--c-text);
                   letter-spacing: -.02em; }

.perf-scale { font-size: 11.5px; color: var(--c-text-3); margin: 0 0 16px; line-height: 1.7; }
.perf-scale b { color: var(--c-text); }

.perf-reply { margin-top: 14px; padding: 10px 12px; border-left: 3px solid var(--c-brand);
              background: var(--c-surface, #f7f8fc); border-radius: 0 8px 8px 0; font-size: 13px; }
.perf-reply b { display: block; font-size: 12.5px;
                color: var(--c-text-3); margin-bottom: 3px; }

/* Top three. Deliberately the only place with any ornament - it is the one part
   of this screen meant to be looked at rather than read. */
.perf-tops { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
             gap: 12px; margin-bottom: 20px; }
.perf-top { padding: 14px 16px; border: 1px solid var(--c-border); border-radius: 12px;
            background: var(--c-bg, #fff); }
.perf-top i { font-size: 20px; color: var(--c-brand); }
.perf-top-n { font-weight: 650; font-size: 14px; margin-top: 6px; white-space: nowrap;
              overflow: hidden; text-overflow: ellipsis; }
.perf-top-s { font-size: 26px; font-weight: 700; letter-spacing: -.02em; line-height: 1.15; }

.perf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.perf-h { font-size: 12.5px; font-weight: 650;
          color: var(--c-text-3); margin: 0 0 10px; }

/* Spread across the five bands. Everyone bunched at the top is grade inflation,
   and it is the one thing a rating system cannot detect about itself. */
.perf-band { display: grid; grid-template-columns: 150px 1fr 28px; align-items: center;
             gap: 10px; margin-bottom: 7px; }
.perf-band-bar { height: 7px; background: var(--c-border); border-radius: 99px; overflow: hidden; }
.perf-band-bar i { display: block; height: 100%; background: var(--c-brand); border-radius: 99px; }
.perf-band-l { min-width: 0; }
.perf-band-n { font-size: 12.5px; font-weight: 600; text-align: right; }

/* Kiosk notices. .clock-status-note is a flex row: exactly one icon and one
   span inside, or the text breaks into columns. The icon must not shrink and
   the span must be free to wrap as ordinary prose. */
.clock-status-note { align-items: flex-start; line-height: 1.5; font-weight: 500; }
.clock-status-note > i { flex: 0 0 auto; font-size: 16px; line-height: 1.5; }
.clock-status-note > span { flex: 1 1 auto; min-width: 0; }
.clock-status-note b { font-weight: 700; }
.clock-status-note.ci-note-warn {
  color: var(--c-warning); background: var(--c-warning-bg); border-color: var(--c-warning);
}
.clock-status-note.ci-note-info {
  color: var(--c-text-2); background: var(--c-surface-2); border-color: var(--c-border);
}
.clock-status-note.ci-note-info b { color: var(--c-text); }

/* A fixed-choice list has four possible answers; it does not need the slack the
   free-text field it replaced was given. The amount is the figure being checked,
   so it keeps its width and the list stops growing past its longest option. */
.adj-row > select:not(.adj-t) { max-width: 320px; }
.slip-bulk-bar > select:not(:first-of-type) { max-width: 260px; }

/* A <select> is as wide as its longest option unless told otherwise. Inside a
   flex column that made the whole card overflow, because a flex item's default
   min-width is min-content. Clip the label instead of widening the layout. */
select.field-input { min-width: 0; text-overflow: ellipsis; }
/* Balancing Records — a register 70 columns wide. It scrolls horizontally
   inside its own frame rather than stretching the page, and the first column
   stays put so a row can still be identified after scrolling right. */
/* Six totals sized to their own figures wrapped 3-2-1 on a phone, leaving
   Employer expenses alone on the last row. Two even columns there. */
.bal-sum { display: flex; gap: 8px; flex-wrap: wrap; padding: 0 0 12px; }
@media (max-width: 480px) { .bal-sum { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.bal-scroll { overflow: auto; max-height: calc(100vh - 210px); border: 1px solid var(--c-border);
              border-radius: var(--radius); background: var(--c-white); }
.bal-tbl { border-collapse: separate; border-spacing: 0; font-size: 11.5px; white-space: nowrap; }
.bal-tbl th { position: sticky; top: 0; z-index: 2; background: var(--c-surface-2);
              font-size: 12px; font-weight: 600;
              color: var(--c-text-2); padding: 8px 10px; border-bottom: 1px solid var(--c-border);
              text-align: right; }
.bal-tbl th:first-child, .bal-tbl td:first-child {
              position: sticky; left: 0; z-index: 1; background: var(--c-white);
              text-align: left; border-right: 1px solid var(--c-border); }
.bal-tbl th:first-child { z-index: 3; background: var(--c-surface-2); }
.bal-tbl td { padding: 7px 10px; border-bottom: 1px solid var(--c-border-soft, #eee); }
.bal-num { text-align: right; font-variant-numeric: tabular-nums; }
.bal-txt { text-align: left; }
.bal-bold { font-weight: 600; }
.bal-na { border-bottom: 1px dotted var(--c-text-3); }
.bal-tbl tfoot td { position: sticky; bottom: 0; background: var(--c-surface-2);
              border-top: 1px solid var(--c-border); font-weight: 600; }

/* Reference roster. Seven dropdowns per row across 105 employees, so the cells
   are as small as they can be and still be hit: the colour carries the meaning
   and the text is there for anyone who does not read it that way. */
.rt-bar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:12px; }
.rt-gap { flex:1 1 auto; }
.rt-sel { width:auto; min-width:0; padding:0 30px 0 10px; height:32px; font-size:12px; }
.rt-tabs { display:flex; gap:2px; padding:2px; margin-bottom:10px; overflow:auto;
           border:1px solid var(--c-border); border-radius:var(--radius); background:var(--c-surface); }
.rt-tab { font:inherit; font-size:12px; padding:5px 11px; border:0; border-radius:4px;
          background:none; color:var(--c-text-2); cursor:pointer; white-space:nowrap;
          display:inline-flex; align-items:center; gap:6px; }
.rt-tab span { font-size:10px; opacity:.65; }
.rt-tab.active { background:var(--c-brand); color:#fff; font-weight:500; }
.rt-tab:focus-visible { outline:2px solid var(--c-brand); outline-offset:-2px; }
.rt-scroll { overflow:auto; max-height:calc(100vh - 320px);
             border:1px solid var(--c-border); border-radius:var(--radius); }
.rt-tbl { border-collapse:separate; border-spacing:0; width:100%; font-size:12px; }
.rt-tbl th { position:sticky; top:0; z-index:2; background:var(--c-surface-2);
             font-size:11.5px; font-weight:600;
             color:var(--c-text-3); padding:7px 4px; text-align:center; white-space:nowrap;
             border-bottom:1px solid var(--c-border); }
.rt-tbl th:first-child, .rt-tbl td:first-child { position:sticky; left:0; z-index:1;
             background:var(--c-white); text-align:left; padding-left:12px; min-width:180px;
             border-right:1px solid var(--c-border); }
.rt-tbl th:first-child { z-index:3; background:var(--c-surface-2); }
.rt-tbl td { padding:3px 2px; border-bottom:1px solid var(--c-border); text-align:center; }
.rt-emp { padding:5px 0; line-height:1.3; }
.rt-emp b { display:block; font-weight:500; }
.rt-emp span { font-size:10.5px; color:var(--c-text-3); }
.rt-cell { width:100%; min-width:62px; font:inherit; font-size:11px; padding:4px 2px;
           border:1px solid var(--c-border); border-radius:4px;
           background:var(--c-white); color:var(--c-text-2); cursor:pointer; }
.rt-cell[data-p="dayoff"] { color:#b4551e; background:#fdf0e4; border-color:#e2b48c; font-weight:600; }
.rt-cell[data-p="wfh"]    { color:#1d6b52; background:#e6f4ee; border-color:#9ccbb6; font-weight:600; }
.rt-foot { display:flex; justify-content:space-between; gap:12px; flex-wrap:wrap;
           padding-top:10px; font-size:11.5px; color:var(--c-text-2); }
.rt-stat { display:flex; gap:22px; padding:4px 0 10px; flex-wrap:wrap; }
.rt-stat div { min-width:74px; }
.rt-stat b { display:block; font-size:19px; font-weight:600; color:var(--c-text);
             font-variant-numeric:tabular-nums; line-height:1.2; }
.rt-stat span { font-size:10.5px; color:var(--c-text-3); }

/* The two roster views. A segmented control, not tabs: there are exactly two and
   they answer the same question at different scales - this month, or every month. */
.rt-views { display:inline-flex; gap:2px; padding:2px; margin-bottom:12px;
            border:1px solid var(--c-border); border-radius:var(--radius); background:var(--c-surface); }
.rt-view { font:inherit; font-size:12px; font-weight:500; padding:6px 14px; border:0;
           border-radius:4px; background:none; color:var(--c-text-2); cursor:pointer; white-space:nowrap; }
.rt-view.active { background:var(--c-brand); color:#fff; }
.rt-view:focus-visible { outline:2px solid var(--c-brand); outline-offset:-2px; }

/* The include tick. A row that is out keeps its pattern on screen but greyed,
   so you can see what you would get back by ticking it again. */
.rt-tick { width:34px; text-align:center; padding-left:0 !important; min-width:0 !important; }
.rt-tick input { width:14px; height:14px; cursor:pointer; accent-color:var(--c-brand); }
.rt-tbl tr.rt-out td { opacity:.42; }
.rt-tbl tr.rt-out .rt-tick { opacity:1; }

/* The employer share sits beside the employee one. Quieter, because it never
   touches take-home - it is the company cost, shown for reconciliation. */
.ded-row { grid-template-columns: 1fr 100px 100px; }
.ded-er { background: var(--c-surface-2); }

/* app.js emits these with no rule anywhere in this sheet - td-secondary text
   rendered at the same weight as the name beside it (189 cells), avatars as
   two bare letters, and the employee-form section headers as plain lines.
   The si-* chips colour the stat icon the stat-card aliases above shape. */
.td-primary    { font-weight: 600; color: var(--c-text); }
.td-secondary  { font-size: 12px; color: var(--c-text-3); }
.cell-with-ava { display: flex; align-items: center; gap: 10px; }
.cell-with-ava > div { min-width: 0; }   /* lets a truncating email give way in a narrow card */
.ava {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  background: var(--c-brand-10); color: var(--c-brand);
  font-size: 12px; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
}
.stat-body { flex: 1; min-width: 0; } /* min-width: a flex child refuses to shrink below its content (6.31) */
.form-section {
  font-size: 12px; font-weight: 600; color: var(--c-text-2); letter-spacing: 0.01em;
  margin: 18px 0 10px; padding-top: 14px; border-top: 1px solid var(--c-border);
}
.form-section:first-child,
.alert:first-child + .form-section { margin-top: 0; padding-top: 0; border-top: 0; }   /* the hidden error alert comes first in the add/edit forms */
.badge.t-cap { text-transform: capitalize; }

/* Second sweep of emitted-but-undefined classes. The harness reported twelve
   because its report slices to twelve - the honest total was larger, and its
   collector could not see one- and two-letter definitions at all. */
.panel-head h1 { margin: 0; } /* the group above sets the type; the UA margin must go */
.btn-xs    { height: 24px; padding: 0 7px; font-size: 11px; }
.filter-bar { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.form-col-full { grid-column: 1 / -1; }
.loan-tbl th, .loan-tbl td { padding: 6px 8px; font-size: 12px; }
.queue-item { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--c-border); }
.queue-item:last-of-type { border-bottom: 0; }
.queue-icon {
  width: 32px; height: 32px; border-radius: var(--radius-sm); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 15px;
}
.qi-leave  { background: var(--c-info-bg); color: var(--c-info); }
.queue-info { flex: 1; min-width: 0; }
.queue-name { font-size: 13px; font-weight: 600; color: var(--c-text); }
.queue-sub  { font-size: 12px; color: var(--c-text-3); }
.seg-toggle {
  display: inline-flex; gap: 4px; padding: 4px; margin-bottom: 14px;
  background: var(--c-surface-2); border-radius: 10px;
}
.seg-btn {
  border: none; padding: 7px 16px; border-radius: 7px; cursor: pointer;
  font-weight: 600; font-size: 13px; background: transparent; color: var(--c-text-2);
}
.seg-btn.active { background: var(--c-brand); color: #fff; }
.tk-switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }

/* ── EMPLOYEE DOCUMENTS ────────────────────────────────────────────────────
   A certificate is an artifact in the employee's world: printed, stamped,
   handed to a bank or an embassy. So each entry gets a document's presence -
   a sheet-proportioned plate carrying its own file type - rather than a table
   row. This replaced a full-bleed table whose View and Download sat about
   1,400px from the title they acted on, with column headings for a date and a
   file size. Nothing is compared across rows, so nothing needs columns.

   Capped at 760px because the job is FIND AND OPEN: a line you have to track
   across a widescreen monitor is harder to scan, not easier. */
.doc-list { max-width: 760px; display: flex; flex-direction: column; gap: 7px; }

.doc-row {
  display: flex; align-items: center; gap: 13px;
  background: var(--c-white); border: 1px solid var(--c-border);
  border-radius: var(--radius-lg); padding: 11px 13px;
  transition: border-color .14s ease, box-shadow .14s ease;
}
.doc-row:hover { border-color: #c3c7e4; box-shadow: 0 2px 10px rgba(8,9,32,.06); }

/* 1:1.414 - the proportion of the paper this ends up printed on. The extension
   IS the content; an icon beside it would say the same thing twice. */
.doc-plate {
  flex: 0 0 auto; width: 40px; height: 54px; border-radius: 5px;
  background: var(--c-surface-2); border: 1px solid var(--c-border);
  display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;
}
/* The folded corner is what makes it read as a sheet rather than a swatch. */
.doc-plate::after {
  content: ''; position: absolute; top: 0; right: 0;
  border-width: 0 12px 12px 0; border-style: solid;
  border-color: #c9cde2 var(--c-white);
}
.doc-plate .ext {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 10px; font-weight: 700; letter-spacing: .05em; color: var(--c-text-3);
}
/* Tinted by what the file IS, so the type reads before the label does. */
.doc-row.is-pdf   .doc-plate { background: #fdf3f4; border-color: #f0ccd0; }
.doc-row.is-pdf   .doc-plate .ext { color: var(--c-danger); }
.doc-row.is-pdf   .doc-plate::after { border-color: #e3aab1 var(--c-white); }
.doc-row.is-image .doc-plate { background: #f1f2fd; border-color: #ccd0f0; }
.doc-row.is-image .doc-plate .ext { color: var(--c-brand); }
.doc-row.is-image .doc-plate::after { border-color: #aab1e3 var(--c-white); }

.doc-main  { flex: 1 1 auto; min-width: 0; }
.doc-title {
  font-size: 14.5px; font-weight: 700; color: var(--c-text); letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* One quiet line for everything that used to be a column heading. The dots are
   set at a real weight - at 50% they read as dirt on the screen, not as
   separators. */
.doc-meta {
  font-size: 12.5px; color: var(--c-text-3); margin-top: 2px; line-height: 1.5;
}
.doc-meta .dot { margin: 0 6px; color: #b9bcd6; }
.doc-meta .sz  { font-family: var(--font-mono, 'JetBrains Mono', monospace); font-size: 11.5px; }
/* Aligned to the title, not indented past it - a note that floats free of the
   thing it annotates reads as a stray line. */
.doc-note { font-size: 12.5px; color: var(--c-text-2); margin-top: 4px; font-style: italic; }
.doc-acts { flex: 0 0 auto; display: flex; gap: 6px; }

@media (max-width: 640px) {
  .doc-row  { flex-wrap: wrap; }
  .doc-main { flex: 1 1 60%; }
  .doc-acts { flex: 1 1 100%; margin-top: 4px; }
  .doc-acts .btn { flex: 1 1 0; justify-content: center; }
}

/* ── EMPLOYEE DASHBOARD ────────────────────────────────────────────────────
   The old header was a 180px navy block carrying a name the employee already
   knows and two leave balances set in 28px - which on most accounts are 0 and
   0, so the largest figures on the screen said nothing. Prominence follows
   importance here instead: today's punches, then a running overtime session,
   then the cutoff that decides the pay.

   Nothing on this screen shows money. Net pay, gross, deductions and the loan
   amount are all gone - somebody reading over a shoulder on a jeepney should
   learn a shift and a timer, not a salary. Payslips are one tap away on their
   own panel. */

/* THE PANEL CANNOT OVERFLOW - that is a property of the structure, not of the
   numbers. One fixed-height container; at each level below it exactly ONE row
   is minmax(0, 1fr) and every other row is bounded. The 1fr row absorbs all
   slack and is allowed to reach zero height, so there is nothing left to push
   past the bottom edge. Tuning cannot achieve this; only the grid can.

   Height arithmetic is .ci-fixed's, which the kiosk has used since __31:
   the 58px navbar, and the content padding top and bottom.

   container-type: size makes cqh mean "a percent of THIS PANEL". Every figure
   and every padding below is expressed against the panel rather than against
   the viewport, so the layout does not care what chrome surrounds it. The vh
   line before each cqh line is the fallback for engines without container
   query units; where both parse, the second wins. */
/* .content scrolls for every other panel and must keep doing so, but this one
   is sized to the viewport on purpose - a scrollbar here means a rounding
   error rather than content, and it lets the page drag by a pixel. */
.content:has(> .panel-enter.ess-fit) { overflow: hidden; }
.ess-fit { height: calc(100dvh - var(--topbar-h) - (var(--content-pad, 24px) * 2));
           display: grid; grid-template-rows: auto auto minmax(0, 1fr); row-gap: 0;
           /* NOT gap: a grid gap is drawn between every pair of tracks, and
              row 2 is empty whenever no overtime session is running - so a gap
              here reserves space around an element that is not there. The
              spacing lives on .ess-day and .ess-ot as margin-bottom, which
              disappears with the element it belongs to. */
           min-height: 0; container-type: size; }
/* Placed explicitly, never auto-placed: with no OT session row 2 collapses to
   zero and the card still lands in the 1fr row. Auto-placement would have put
   it in an implicit unbounded row the moment a third child appeared. */
.ess-fit > .ess-day     { grid-row: 1; }
.ess-fit > .ess-ot      { grid-row: 2; min-height: 0; overflow: hidden;
                          max-height: 34cqh; }
/* Its own paddings were fixed while everything around them scaled, so on a
   710px panel it took a quarter of the height and squeezed the card. */
.ess-fit > .ess-ot      { padding: clamp(9px, 1.8cqh, 18px) 14px; margin-bottom: 10px; }
.ess-fit .ess-ot-big    { font-size: clamp(20px, 3.4cqh, 30px); }
.ess-fit .ess-ot-sub    { font-size: clamp(10.5px, 1.4cqh, 12px); margin-top: clamp(3px, .7cqh, 7px); }
.ess-fit .ess-ot-act    { margin-top: clamp(6px, 1.2cqh, 12px); }
.ess-fit > .grid-2      { grid-row: 3; min-height: 0; display: grid;
                          grid-template-rows: minmax(0, 1fr); }
.ess-fit > .grid-2 > div { min-height: 0; display: grid; grid-template-rows: minmax(0, 1fr); }

/* header - stats and strip - RECORDS - balances - actions.
   Records is the 1fr; the four around it hold figures that must not be cropped. */
/* header - figures - actions. The figures band is the 1fr now that Records is
   gone, and it distributes what it is given across the tally, the footnote,
   the strip and the key with space-evenly - so the gaps between them grow and
   shrink together instead of one void opening at the bottom. The buttons sit
   in an `auto` row at the end of the card and can never be pushed off it. */
.ess-fit .card { min-height: 0; display: grid;
                 grid-template-rows: auto minmax(0, 1fr) auto; }
.ess-fit .ess-band--stats { min-height: 0; overflow: hidden; display: flex;
                            flex-direction: column; justify-content: flex-start; }
/* The slack lands between the figures and the strip. auto collapses to zero
   when the band is tight, so overflow can only ever go downward and clip
   cleanly - it can never centre itself and crop through the middle of a
   digit, which is what space-evenly did here. */
.ess-fit .ess-band--stats > .ess-strip { margin-top: auto; }
.ess-fit .ess-band--stats > .ess-strip-k { margin-bottom: auto; }

/* SPACING IS A PERCENT OF THE PANEL, NOT A PIXEL COUNT. Fixed paddings are why
   a tall screen looked stretched and a short one cramped - the content resized
   and the space around it did not. Every measure here is cqh, a percent of the
   panel the layout was actually given, with a floor that keeps a 300px
   landscape strip legible and a ceiling that stops a laptop from drifting. */
.ess-fit .card-header      { padding: clamp(7px, 1.5cqh, 15px) 14px; }
.ess-fit .ess-day          { padding: clamp(9px, 2cqh, 20px) 14px; }
.ess-fit .ess-band         { padding: clamp(7px, 1.5cqh, 16px) 14px; }
.ess-fit .ess-band--stats  { padding-bottom: clamp(5px, 1cqh, 12px); }
.ess-fit .ess-strip        { padding: clamp(5px, 1.2cqh, 12px) 0 clamp(3px, .7cqh, 8px); }
.ess-fit .ess-strip .sq    { height: clamp(20px, 3.8cqh, 38px);
                             font-size: clamp(9px, 1.5cqh, 13px); }
.ess-fit .ess-day          { row-gap: clamp(4px, 1cqh, 10px); }
.ess-fit .ess-tally        { margin-bottom: clamp(6px, 1.3cqh, 14px); }


/* AVATAR ABOVE THE NAME. .ess-day is three children in DOM order - avatar,
   name, In/Out/Worked - so this is a grid re-placement, not a markup change. */
.ess-day {
  background: var(--c-white); border: 1px solid var(--c-border);
  border-radius: var(--radius-lg); padding: 13px 15px; margin-bottom: 10px;
  display: grid; grid-template-columns: minmax(0, 1fr); row-gap: 6px;
}
/* Margins, not `gap`: the payslip print path and older mobile browsers ignore
   flex gap, and the columns silently collide. */
.ess-day > * { margin-right: 15px; }
.ess-day > *:last-child { margin-right: 0; }
.ess-who   { flex: 0 0 auto; min-width: 0; width: 100%; text-align: center; }
.ess-who-n { font-weight: 700; font-size: 15.5px; letter-spacing: -.015em; }
.ess-who-r { font-size: 11.5px; color: var(--c-text-3); margin-top: 1px;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Full width at every size, so the three centre lines are true thirds of the
   card rather than thirds of the row's own text. */
/* Two cells now - paid leave and maternity/paternity. auto-fit rather than
   a hardcoded 2 so a third balance would not need this rule re-tuned. */
.ess-pin   { display: grid; width: 100%;
             grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }
.ess-pin > div { padding: 0 8px; text-align: center;
                 border-left: 1px solid var(--c-border); }
.ess-pin > div:first-child { border-left: 0; }
.ess-pin-l { font-size: 11px;
             color: var(--c-text-3); font-weight: 700; }
.ess-pin-v { font-family: var(--font-mono, 'JetBrains Mono', monospace);
             font-size: 14px; font-weight: 700; margin-top: 2px;
             white-space: nowrap; }
.ess-fit .ess-pin > div { container-type: inline-size; }
.ess-fit .ess-pin-v { font-size: clamp(13px, 20cqi, 18px); }

/* THE STATE IS THE DESIGN. Amber under the 60-minute floor, green past it. An
   employee could previously work 55 minutes of overtime and never learn why it
   paid nothing. */
.ess-ot {
  border-radius: var(--radius-lg); padding: 13px 15px; margin-bottom: 10px;
  display: flex; align-items: center; flex-wrap: wrap;
}
.ess-ot > * { margin-right: 13px; }
.ess-ot.is-earn { background: #eef7f1; border: 1px solid #b8dcc7; }
.ess-ot.is-wait { background: #fff8ec; border: 1px solid #f0d9ae; }
.ess-ot-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
              animation: essPulse 1.9s ease-in-out infinite; }
.ess-ot.is-earn .ess-ot-dot { background: var(--c-success); }
.ess-ot.is-wait .ess-ot-dot { background: var(--c-warning); }
@keyframes essPulse { 0%,100% { opacity: 1; transform: scale(1); }
                      50%     { opacity: .35; transform: scale(.8); } }
.ess-ot-m   { flex: 1 1 200px; font-size: 13.5px; line-height: 1.5; }
.ess-ot.is-earn .ess-ot-m { color: #14603a; }
.ess-ot.is-wait .ess-ot-m { color: #7a5310; }
/* nowrap, or "2h 31m" breaks after the hour and the strip grows a second line
   every time the counter ticks past a boundary. */
.ess-ot-big { font-family: var(--font-mono, 'JetBrains Mono', monospace);
              font-size: 21px; font-weight: 700; letter-spacing: -.02em;
              white-space: nowrap; }
.ess-ot.is-earn .ess-ot-big { color: var(--c-success); }
.ess-ot.is-wait .ess-ot-big { color: var(--c-warning); }
.ess-ot-tag { font-family: var(--font-mono, 'JetBrains Mono', monospace);
              font-size: 12px; font-weight: 700; padding: 3px 9px;
              border-radius: 20px; flex: 0 0 auto; }
.ess-ot.is-earn .ess-ot-tag { background: #d9ede2; color: #14603a; }
.ess-ot.is-wait .ess-ot-tag { background: #f7e6c8; color: #7a5310; }
/* Links to the kiosk rather than punching. An OT-out carries a photo and
   coordinates; a dashboard button that skipped them would weaken a control. */
.ess-ot-act { flex: 0 0 auto; margin-right: 0; background: var(--c-brand);
              color: #fff; border: 0; border-radius: var(--radius);
              padding: 9px 15px; font-size: 13px; font-weight: 700;
              cursor: pointer; font-family: inherit; }
.ess-ot-sub { flex: 1 1 100%; font-size: 11.5px; margin: 5px 0 0; }
.ess-ot.is-earn .ess-ot-sub { color: #3d7a58; }
.ess-ot.is-wait .ess-ot-sub { color: #8a6520; }

/* The dashboard's card headers put a meta line to the right of the title;
   without nowrap "This cutoff so far" and "Recent days" wrap onto two lines
   and every header doubles in height. Scoped to this panel so no other card
   header changes. */
.ess-day ~ .grid-2 .card-title, .grid-2 .card-title { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* The card bodies were 13px all round on top of a 12px header, which on a
  phone puts ~50px of padding around four numbers. Tighter, and the tally rows
  wrap instead of squeezing their labels. */
/* 20px, matching .card-header - which is what every card title on every other
   screen uses. At 13px the day strip and the OT strip sat 7px left of every
   card below them and the panel had a ragged left edge. */
.ess-day, .ess-ot { padding: 12px 20px; }
.ess-recent  { padding: 5px 0; }

/* EQUAL COLUMNS, not content-width. These were flex with a margin-right and no
   basis, so every tile was as wide as its own text: "11", "2", "1" and
   "5h 06m" produced four different column positions and the row read as
   ragged. auto-fit means the 5th tile (Awaiting review) drops in without
   re-tuning anything. */
.ess-tally   { display: flex; flex-wrap: wrap; justify-content: center;
               gap: 10px 8px; margin-bottom: 11px; }
/* A basis, not 0: with five figures (a held day adds Awaiting review) on a
   260px card, flex: 1 1 0 shrank the cells to 43px and the value spilled
   out again. A 56px basis makes them WRAP instead - and justify-content
   centres the wrapped row, so a short row reads as deliberate rather than
   as the fourth figure falling off the left. */
.ess-tally > div { flex: 1 1 56px; min-width: 0; text-align: center;
                   container-type: inline-size; }
.ess-tally-v { font-family: var(--font-mono, 'JetBrains Mono', monospace);
               font-size: clamp(14px, 2.4vh, 18px); font-weight: 700; line-height: 1;
               white-space: nowrap; }   /* was up to 22px: the figures outweighed the card's own title */
.ess-fit .ess-tally-v { font-size: clamp(12px, 20cqi, 18px); }
.ess-tally-l { font-size: 10.5px; color: var(--c-text-3); margin-top: 3px; }
.ess-bar { display: flex; height: 7px; border-radius: 4px; overflow: hidden;
           margin: 0 0 8px; background: var(--c-surface-2); }
.ess-bar i { display: block; height: 100%; }
.ess-key { display: flex; flex-wrap: wrap; font-size: 11.5px; color: var(--c-text-2); }
.ess-key > span { margin-right: 14px; display: inline-flex; align-items: center; }
.ess-key em { width: 8px; height: 8px; border-radius: 2px; margin-right: 5px; font-style: normal; }

.ess-recent { font-size: 12.5px; color: var(--c-text-2); padding: 6px 0;
              border-bottom: 1px solid var(--c-border); }
.ess-recent:last-child { border-bottom: 0; }
.ess-recent b { font-family: var(--font-mono, 'JetBrains Mono', monospace);
                font-weight: 700; color: var(--c-text); margin-right: 6px; }

.ess-acts { display: flex; flex-wrap: wrap; margin-bottom: 10px; }
.ess-acts > .btn { flex: 1 1 0; margin-right: 8px; }
.ess-acts > .btn:last-child { margin-right: 0; }
/* Its own row above the pair, so the three buttons read as one block with the
   same gap between every pair of them. */
.ess-acts > .ess-act-lead { flex: 1 1 100%; margin: 0 0 8px; }

/* auto-fit, not a fixed 3: sick leave was removed and a hardcoded third column
   left a dead gap where it had been. */
.ess-bal { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
           gap: 8px }
.ess-bal > div { min-width: 0; text-align: center; container-type: inline-size; }
.ess-bal-v { font-family: var(--font-mono, 'JetBrains Mono', monospace);
             font-size: 16px; font-weight: 700; white-space: nowrap; }
.ess-fit .ess-bal-v { font-size: clamp(13px, 20cqi, 18px); }
.ess-bal-l { font-size: 10.5px; color: var(--c-text-3); margin-top: 1px; }

.ess-req { display: flex; align-items: center; justify-content: space-between;
           font-size: 12.5px; color: var(--c-text-2); padding: 5px 0;
           border-bottom: 1px solid var(--c-border); }
.ess-req:last-child { border-bottom: 0; }

@media (max-width: 640px) {
  .ess-pin { margin: 2px 0 0; }
  /* Symmetric padding, or 'centred' is a lie: 13px on the left only put the
     text 6.5px right of the column's centre line, and the padding-left:0
     override - correct while the text was flush left - shifted the first
     column the other way. The three centres have to be at 1/6, 3/6, 5/6. */
  /* .ess-who is the last item on the first line once .ess-pin wraps, so its
     15px right margin was dead space and the branch line ellipsed 15px early.
     :last-child cannot reach it - .ess-pin is the last child. */
  .ess-who { margin-right: 0; }
  /* On a phone the message wrapped mid-sentence around a 21px counter beside
     it - "Overtime running since 11:00 / AM". Message on its own line, then
     the counter and the tag together, then the button. */
  .ess-ot-m   { flex: 1 1 100%; margin: 0 0 7px; }
  .ess-ot-big { margin-right: 10px; }
  .ess-ot-act { flex: 1 1 100%; margin: 10px 0 0; }
  /* One row of four. I tried two-up at 46% and the card got TALLER, not
     shorter - two rows of tall figures cost more than the wrapped label saved,
     and the reading order went ragged. Smaller figures, tighter gaps instead. */
  .ess-tally { gap: 9px 6px }
  .ess-tally-l { font-size: clamp(9px, 1.3vh, 10.5px); }
  .ess-fit .ess-tally-l { font-size: clamp(9px, 11cqi, 10.5px); }
  /* Short screens buy their height back from padding and the day squares
     before they take it from the figures or the records. */
}

/* AT A GLANCE. Leave balance, requests, loan and announcements used to be four
   separate cards, each with a header, an icon and 13px of padding - to carry a
   zero, one line, the word "Settled" and a link. On a phone that is four boxes
   of equal weight below a cutoff tally that actually says something, and the
   eye has no idea which matters. One card, one row each. */
.ess-glance { display: flex; align-items: center; justify-content: space-between;
              padding: 7px 0; border-bottom: 1px solid var(--c-border);
              font-size: 12.5px; }
.ess-glance:last-child { border-bottom: 0; }
.ess-glance-l { color: var(--c-text-2); }
.ess-glance-v { font-family: var(--font-mono, 'JetBrains Mono', monospace);
                font-weight: 700; color: var(--c-text); text-decoration: none; }

/* ── PROFILE PHOTO ─────────────────────────────────────────────────────────
   Additive only. `.ava` and `.loan-avatar` keep every rule they had - the
   initials are still the content of the div, and the photo is an <img> laid
   over them. When there is no photo the img is absent (or removes itself via
   onerror) and the initials show through unchanged.

   That matters because 0 of 112 employees have one: for months most rows will
   be initials, and the two states have to look equally finished rather than one
   looking like a failed load. */
.ava { position: relative; overflow: hidden; }
.ava > img,
.loan-avatar > img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: inherit; display: block;
}
.loan-avatar { position: relative; overflow: hidden; }

/* The control on the employee's own profile. */
.pp-row { display: flex; align-items: center; background: var(--c-white);
          border: 1px solid var(--c-border); border-radius: var(--radius-lg);
          padding: 13px 14px; }
.pp-row > * { margin-right: 13px; }
.pp-row > *:last-child { margin-right: 0; }
.pp-ava { width: 54px; height: 54px; border-radius: 50%; flex: 0 0 auto;
          position: relative; overflow: hidden; display: flex;
          align-items: center; justify-content: center;
          background: var(--c-brand-10); color: var(--c-brand);
          font-weight: 700; font-size: 18px; }
.pp-ava > img { position: absolute; inset: 0; width: 100%; height: 100%;
                object-fit: cover; display: block; }
.pp-t  { flex: 1 1 auto; font-size: 12.5px; color: var(--c-text-2); line-height: 1.55; }
.pp-t b { color: var(--c-text); }
@media (max-width: 560px) {
  .pp-row { flex-wrap: wrap; }
  .pp-t   { flex: 1 1 60%; }
  .pp-row .btn { flex: 1 1 100%; margin-top: 10px; justify-content: center; }
}

/* The employee's own avatar on their dashboard. 44px - big enough to read as a
   face, small enough that the strip grows by 8px rather than 20. Same circle
   and same initials fallback as every other list, because most people will not
   have uploaded one. */

/* The avatar as a control. A camera badge in the corner is the only thing that
   says "this is tappable" - a bare circle reads as decoration, which is why the
   Add link in At a glance was the only discoverable route before. */
.ess-ava-btn { position: relative; flex: 0 0 auto; margin-right: 0; justify-self: center;
               background: none; border: 0; padding: 0; cursor: pointer;
               line-height: 0; border-radius: 50%; }
.ess-ava-btn:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 3px; }
/* Sized against the panel, not the screen: 11% of whatever height the panel
   was given, floored at 46px so it survives a 300px landscape strip. */
.ava.ess-ava { width: clamp(46px, 12vh, 104px); height: clamp(46px, 12vh, 104px);
               font-size: clamp(18px, 4.4vh, 34px); }
.ess-fit .ava.ess-ava { width: clamp(46px, 11cqh, 104px); height: clamp(46px, 11cqh, 104px);
                        font-size: clamp(18px, 4cqh, 34px); }
.ess-ava-cam { position: absolute; right: -1px; bottom: -1px; width: 21px; height: 21px;
               border-radius: 50%; background: var(--c-brand); color: #fff;
               border: 2px solid var(--c-white); display: flex;
               align-items: center; justify-content: center; }
.ess-ava-cam i { font-size: 11px; }

/* The sheet. Full-width rows, because on a phone this is thumbed. */
.pp-item { display: flex; align-items: center; width: 100%; text-align: left;
           background: none; border: 0; padding: 13px 20px; font-size: 14px;
           font-weight: 600; color: var(--c-text); cursor: pointer;
           font-family: inherit; }
.pp-item:hover { background: var(--c-surface-2); }
.pp-item i { font-size: 17px; margin-right: 11px; color: var(--c-text-3); }
.pp-item.is-danger { color: var(--c-danger); }
.pp-item.is-danger i { color: var(--c-danger); }

@media (max-width: 560px) {
  .ava.ess-ava { width: 52px; height: 52px; font-size: 18px; }
}

/* THE PHOTO SHEET. It reuses .modal so it inherits the bottom-sheet behaviour
   the app already has on phones, then drops the dialog chrome: a 34px header
   and 24px body padding are built for a form, and this is three tappable rows.

   The grip and the bottom inset are the two things that make it read as a sheet
   rather than a clipped dialog - without the inset the last row sits flush on
   the screen edge, under the home indicator. */
/* .modal is display:flex/column with overflow:hidden, built for a header +
   scrolling body + footer. My rows are plain buttons with no flex basis, so
   the box collapsed to 49px. Block layout instead - the sheet is short by
   design and never needs to scroll. */
.pp-sheet { display: block; padding: 0 0 8px; overflow: visible; }
.pp-sheet .pp-grip { width: 38px; height: 4px; border-radius: 3px;
                     background: var(--c-border); margin: 9px auto 4px; }
.pp-sheet .pp-head { font-size: 13.5px; font-weight: 700; color: var(--c-text-3);
                     padding: 6px 20px 8px; }
.pp-item.is-cancel { justify-content: center; color: var(--c-text-3);
                     border-top: 1px solid var(--c-border); margin-top: 4px; }
/* env() so the last row clears the home indicator; the 8px is the fallback for
   browsers without safe-area support. */
.pp-sheet .pp-item:last-child { margin-bottom: env(safe-area-inset-bottom, 0px); }

@media (max-width: 480px) {
  /* Full width at the bottom edge, like every other sheet in the app. */
  .pp-sheet { max-width: 100% !important; }
}

/* The OT button during its cooldown. Muted and not-allowed, so it reads as
   "not yet" rather than as a broken control - the countdown in the label says
   the rest. */
.clock-action-btn.is-held {
  background: var(--c-surface-2) !important;
  color: var(--c-text-3) !important;
  border-color: var(--c-border) !important;
  cursor: not-allowed;
  box-shadow: none !important;
}

/* Press-and-hold (OT In). The fill grows across the button for as long as it is
   held; the duration comes from CI_HOLD_MS via --ci-hold-ms. touch-action and the
   callout stop a phone from scrolling or opening its long-press menu mid-hold. */
.clock-action-btn.ci-hold { position: relative; overflow: hidden; touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.ci-hold-fill { position: absolute; inset: 0; background: rgba(255,255,255,.3); transform: scaleX(0); transform-origin: left center; pointer-events: none; }
.ci-hold.is-holding .ci-hold-fill { transform: scaleX(1); transition: transform var(--ci-hold-ms, 3s) linear; }
.ci-hold-label { position: relative; display: inline-flex; align-items: center; gap: 10px; pointer-events: none; }
.ci-hold-hint { font-size: 12px; color: var(--c-text-3); text-align: center; margin-top: 6px; }

/* ── Requests & Approvals ───────────────────────────────────────────────────
   ONE ROW OF TABS. A grouped rail with its own label column read as a table
   header bolted to the tabs and pushed them a third of the way across.
   Underline-style, and the active tab's underline meets the panel's top edge so
   the two read as one surface rather than a strip above a box. */
.req-tabs{display:flex;align-items:flex-end;gap:2px;overflow-x:auto;
  border-bottom:1px solid var(--c-border);margin:0 0 -1px;padding:0}
.req-tabs .req-tab{position:relative;flex:0 0 auto;display:inline-flex;align-items:center;gap:8px;
  white-space:nowrap;padding:8px 12px 10px;border:0;border-radius:0;background:none;
  font-size:13px;font-weight:500;color:var(--c-text-2);cursor:pointer;transition:color .12s}
.req-tabs .req-tab:hover{color:var(--c-text);background:none}
.req-tabs .req-tab.active{color:var(--c-brand);font-weight:600;background:none}
.req-tabs .req-tab.active::after{content:'';position:absolute;left:8px;right:8px;bottom:-1px;
  height:2px;border-radius:2px 2px 0 0;background:var(--c-brand)}
.req-tabs .req-tab i{font-size:14px;opacity:.75}

/* ONE BADGE RULE: a solid pill means someone must act. A grey figure is
   reference. Zero prints the number, never nothing, so an empty queue and an
   uncounted one stop looking identical. */
.req-tabs .req-tab-badge{display:inline-flex;align-items:center;justify-content:center;
  min-width:18px;height:18px;padding:0 6px;border-radius:9px;background:var(--c-brand);
  color:#fff;font-size:11px;font-weight:700;font-variant-numeric:tabular-nums}
.req-tab-zero{font-size:11.5px;font-variant-numeric:tabular-nums;color:var(--c-text-3);opacity:.7}
.req-tab.active .req-tab-zero{opacity:1}

/* Scrolls, draws no bar - THE TAB STRIP ONLY. The bar sat directly under the
   tab labels and read as a second rule beneath them.
   NOT .tbl-wrap. Hiding that one was a mistake: the table genuinely overflows on
   a narrow window, and a container that clips without a bar is the same defect
   as slice(0,200) - content present, counted, and unreachable. */
.req-tabs{scrollbar-width:none;-ms-overflow-style:none}
.req-tabs::-webkit-scrollbar{width:0;height:0;display:none}

/* ── outcome + search on ONE line ─────────────────────────────────────────
   The search had a bar to itself with the outcome chips stranded underneath it. */
.req-toolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.req-toolbar .search-box{margin-left:auto;width:100%;max-width:270px}
@media (max-width: 640px){.req-toolbar .search-box{margin-left:0;max-width:none;flex:1 1 100%}}

/* A DIFFERENT CONTROL FOR A DIFFERENT JOB. These chips reused .req-tab, so
   category and outcome carried identical weight. */
.req-seg{display:inline-flex;background:var(--c-bg);border:1px solid var(--c-border);
  border-radius:var(--radius-sm);padding:3px}
.req-seg-btn{border:0;background:none;font:inherit;font-size:13px;font-weight:500;
  color:var(--c-text-2);padding:5px 12px;border-radius:5px;cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;transition:background .12s,color .12s}
.req-seg-btn:hover{color:var(--c-text)}
.req-seg-btn.active{background:var(--c-surface);color:var(--c-text);font-weight:600;
  box-shadow:0 1px 2px rgba(16,18,35,.10)}
.req-seg-n{font-size:12px;font-variant-numeric:tabular-nums;color:var(--c-text-3)}
.req-seg-btn.active .req-seg-n{color:var(--c-text-2)}

/* ── pager: what replaced slice(0,200) ──────────────────────────────────── */
.req-pager{display:flex;align-items:center;gap:12px;padding:12px 16px;
  border-top:1px solid var(--c-border);flex-wrap:wrap}
.req-pager:empty{display:none}
/* The pager is a direct child of the table card: without this it took the
   card's doubled inset and its top rule stopped short of both edges. */
.card > .req-pager{margin-left:0!important;margin-right:0!important;padding:12px!important}
.badge.req-st{text-transform:capitalize}
.req-pg-count{font-size:12.5px;font-variant-numeric:tabular-nums;color:var(--c-text-2)}
.req-pg-count b{color:var(--c-text);font-weight:600}
.req-pg-size{height:32px;border:1px solid var(--c-border);border-radius:var(--radius-sm);
  padding:0 26px 0 8px;font:inherit;font-size:12.5px;color:var(--c-text-2);cursor:pointer;appearance:none;
  background:var(--c-surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237c7fa0' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 8px center}
.req-pg-btns{display:flex;gap:4px;margin-left:auto;align-items:center}
.req-pg{min-width:32px;height:32px;padding:0 8px;border:1px solid var(--c-border);
  background:var(--c-surface);border-radius:var(--radius-sm);font:inherit;font-size:13px;
  cursor:pointer;color:var(--c-text-2);font-variant-numeric:tabular-nums;transition:border-color .12s}
.req-pg:hover:not(:disabled){border-color:var(--c-brand);color:var(--c-text)}
.req-pg.active{background:var(--c-brand);border-color:var(--c-brand);color:#fff;font-weight:600}
.req-pg:disabled{opacity:.4;cursor:default}
.req-pg-gap{padding:0 2px;color:var(--c-text-3)}

/* ── row density ──────────────────────────────────────────────────────────
   Rows were tall against their content and the action cell piled four controls
   at four heights. One height for every control in the cell. */
#req-tbl td{padding-top:11px;padding-bottom:11px;vertical-align:top}

/* THE ACTION COLUMN SIZES ITSELF TO ITS WIDEST ROW AND IS NEVER CLIPPED.
   width:1% + nowrap is shrink-to-fit on a table cell: the browser gives the
   column exactly what its content needs and hands the rest to Details, which
   wraps. Off Duty is the widest row in the panel - a break field, three verdict
   buttons - so that row sets the column, and nothing narrower can overflow it.
   If the whole table still exceeds the window, .tbl-wrap scrolls and now shows
   a bar again. */
#req-tbl th:last-child,#req-tbl .act-cell,#vio-tbl .act-cell{width:1%;white-space:nowrap;text-align:right}
#req-tbl .act-cell>div,#vio-tbl .act-cell>div{justify-content:flex-end;gap:6px;align-items:center;flex-wrap:nowrap}
#req-tbl .act-cell .btn-sm,#req-tbl .act-cell .action-btn,#req-tbl .act-cell input{height:32px}
#req-tbl .act-cell .action-btn{min-width:32px}
/* Phones stack each row into a card; width:1% (right for a table column) left
   the action cell 28px wide and its buttons spilled over the "Action" label. */
@media (max-width: 1023px){#req-tbl .act-cell,#vio-tbl .act-cell{width:auto}}
/* Details is the one that gives, since it is prose and can wrap. */
#req-tbl td.td-secondary{white-space:normal;min-width:220px}

/* ── column sizing ─────────────────────────────────────────────────────────
   Nine auto-width columns share the table's slack evenly, so ID (10 chars),
   Type (one badge), Date Hired and Status each got as much room as Position -
   the gap between Position and Type on the Employees list was that slack, not a
   margin. .col-fit is shrink-to-fit: the column takes exactly its content and
   hands the remainder to the prose columns, which are the ones that need it. */
.col-fit{width:1%;white-space:nowrap}
/* Employees: the longest email (204px) set the Employee column and, with Date
   Hired, pushed the View/Edit/Documents buttons off a 1280px screen. The address
   truncates (full text in the title) and Date Hired, also in View, drops below
   1367px. Below 1024px rows are cards and keep every field. */
.emp-email{max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (min-width: 1024px) and (max-width: 1366px){#emp-tbl .col-hired, .skel-page .col-hired{display:none}}
#emp-tbl td,#emp-tbl th{padding-left:12px;padding-right:12px}
#emp-tbl td:first-child,#emp-tbl th:first-child{padding-left:16px}
#emp-tbl td:last-child,#emp-tbl th:last-child{padding-right:16px;text-align:right}
#emp-tbl td:last-child>div{justify-content:flex-end;gap:6px}

/* ── employee record: the person, then the paperwork ─────────────────────── */
.emp-view-hd{display:flex;align-items:center;gap:14px;padding:0 0 16px;margin:0 0 16px;
  border-bottom:1px solid var(--c-border)}
.ava.emp-view-ava{width:56px;height:56px;font-size:19px}
.emp-view-who{min-width:0}
.emp-view-name{font-size:17px;font-weight:700;letter-spacing:-.01em;line-height:1.25}
.emp-view-sub{margin-top:2px;font-size:13px;color:var(--c-text-2)}
.emp-view-chips{margin-top:8px;display:flex;gap:6px;flex-wrap:wrap}

/* ── modal: stop the second column pushing off the right edge ──────────────
   .form-row is a 1fr 1fr grid and grid items default to min-width:auto, which
   means they refuse to shrink below their CONTENT. A <select> sizes to its
   longest option - "Sales and Admin Assistant" - so the column grew past its
   1fr share, the grid grew past the modal, and everything on the right was
   clipped: the Daily Rate box, the holiday-pay note, both contribution amounts.
   min-width:0 is what lets 1fr actually mean 1fr. */
.modal .form-row > *, .modal .field, .modal .ded-row > * { min-width: 0 }
.modal .field-input, .modal select.field-input, .modal textarea { max-width: 100% }
.modal select.field-input { text-overflow: ellipsis }

/* ── deductions ────────────────────────────────────────────────────────────
   The block spans the whole form row. Its rows were grid ITEMS of the
   two-column .form-row, so a four-item list alternated left/right and every
   label wrapped inside half a column.

   Inside it, one grid: name on the left, the two amounts right-aligned in fixed
   columns that line up down the block. The heading names them once - they had
   only a title attribute before, which is a hover tooltip. */
.modal .ded-block { grid-column: 1 / -1; margin: 4px 0 2px;
  border: 1px solid var(--c-border); border-radius: var(--radius-md, 10px);
  padding: 12px 14px; background: var(--c-surface) }
.ded-title { font-size: 12.5px; font-weight: 600; color: var(--c-text) }

.modal .ded-head,
.modal .ded-row { display: grid; grid-template-columns: minmax(0,1fr) 96px 96px;
                  gap: 10px; align-items: center }
.modal .ded-head { padding-bottom: 6px; margin-bottom: 2px;
  border-bottom: 1px solid var(--c-border-light, #eceefa);
  font-size: 12px; font-weight: 700; color: var(--c-text-3) }
.modal .ded-head span { text-align: right }
.modal .ded-row { margin: 0; padding: 5px 0; border-bottom: 1px solid var(--c-border-light, #eceefa) }
.modal .ded-row:last-child { border-bottom: 0 }
.modal .ded-check { min-width: 0; flex: none; white-space: nowrap }
.modal .ded-amt { width: 100%; min-width: 0; text-align: right }
/* HMO has no employer side. Hold the column so the row does not shift left. */
.modal .ded-amt-gap { display: block }

@media (max-width: 560px) {
  .modal .ded-head, .modal .ded-row { grid-template-columns: minmax(0,1fr) 76px 76px; gap: 6px }
}

/* Long helper text beside a checkbox has to wrap, not extend the row. */
.modal .field > label, .modal label > span, .modal .t-hint { min-width: 0; overflow-wrap: anywhere }

/* Monthly, daily and hourly are one derivation read left to right, so they sit
   on one line rather than leaving the third alone on a row of its own. */
.emp-rate-trio { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 0 }
.emp-rate-trio > .field { margin-bottom: 18px }
@media (max-width: 640px) { .emp-rate-trio { grid-template-columns: 1fr } }

/* ── balancing: filter by how someone is paid ──────────────────────────────
   The column itself stays at position 66 - the register is in the order the
   finance sheet expects and BAL_UNSOURCED keeps blank columns in place so a
   paste still lines up. Moving it to make it sortable would break the thing the
   order exists for, so the mode is a filter above the table instead. */
.bal-modes{display:flex;gap:6px;flex-wrap:wrap;padding:0 0 12px}
.bal-mode{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 11px;
  border:1px solid var(--c-border);background:var(--c-surface);border-radius:var(--radius-sm);
  font:inherit;font-size:12.5px;font-weight:500;color:var(--c-text-2);cursor:pointer;
  transition:border-color .12s,color .12s}
.bal-mode:hover{border-color:var(--c-brand);color:var(--c-text)}
.bal-mode.active{background:var(--c-brand);border-color:var(--c-brand);color:#fff;font-weight:600}
.bal-mode span{font-size:11.5px;font-variant-numeric:tabular-nums;opacity:.75}
.bal-mode.active span{opacity:.9}

/* The mode of payment is editable in the register itself - see balSetPayMode. */
.bal-mode-sel{max-width:150px;height:26px;padding:0 4px;font:inherit;font-size:12px;
  border:1px solid transparent;border-radius:4px;background:transparent;color:var(--c-text);
  cursor:pointer;transition:border-color .12s,background .12s}
.bal-mode-sel:hover{border-color:var(--c-border);background:var(--c-surface)}
.bal-mode-sel:focus{border-color:var(--c-brand);background:var(--c-surface);outline:none}
.bal-mode-sel:disabled{opacity:.5;cursor:progress}


/* .action-btn is a fixed 32x32 icon square with padding:0 - a labelled button
   inside it gets clipped, which is why Cancel rendered as "Cance" with the icon
   sitting on top of the text. This is the same button with room for words.
   The icon buttons are untouched. */
.action-btn--label{width:auto;min-width:0;padding:0 11px;gap:6px;
  font-size:12.5px;font-weight:600;white-space:nowrap}
.action-btn--label i{font-size:15px}

/* ── ESS dashboard: the cutoff strip, Records, and the balances band ───────
   The old present/absent bar showed two states out of seven - a day off, a
   holiday, a held day and a day with no record were all missing from it. This
   is one square per day of the cutoff, every state in it, and the same colours
   the roster grid uses so the two screens agree at a glance. */
/* No horizontal padding: the parent is already padding:13px 20px, so 20px here
   put the strip 40px in while the tally beside it sat at 20px. */
.ess-strip{display:flex;gap:4px;padding:11px 0 6px}
/* The squares carry their day number now, so they need a text colour and
   enough contrast to read two digits at 9px. The fills below are the same
   hues taken up in saturation. */
.ess-strip .sq{flex:1;height:30px;border-radius:5px;min-width:6px;
  display:flex;align-items:center;justify-content:center;
  font:600 11px/1 var(--font-mono,'JetBrains Mono',monospace);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.sq-worked,   em.sq-worked      {background:#bfe9cd;color:#0d6d3a}
.sq-late,     em.sq-late        {background:#bfe9cd;color:#0d6d3a;box-shadow:inset 0 -3px 0 #d98f0d}
/* The states My Attendance distinguishes, in its colours, so a day does
   not change colour between the two screens. */
.sq-halfday     {background:#f7cf9a;color:#7d4a06}
.sq-wfh         {background:#c2dcfa;color:#12508f}
.sq-holduty     {background:#cdb8f2;color:#3f2196}
.sq-holiday     {background:#e2d5fa;color:#5a30b0}
.sq-offduty     {background:#dfe3ef;color:#6b7192;box-shadow:inset 0 -3px 0 #8b91b0}
.sq-anomaly     {background:#f19aa0;color:#8c0f18}
.sq-unpaidleave {background:#ffc9cb;color:#8c0f18;box-shadow:inset 0 0 0 1px #d98287}
.sq-overtime, em.sq-overtime    {background:#b3d7f7;color:#12508f}
.sq-absent,   em.sq-absent      {background:#f7b4b8;color:#96131c}
.sq-norecord, em.sq-norecord    {background:#f7b4b8;color:#96131c}
.sq-awaiting, em.sq-awaiting    {background:#fbd98a;color:#7a5000}
.sq-leave,    em.sq-leave       {background:#ffd9a3;color:#8a4b09;box-shadow:inset 0 0 0 1px #e0aa5e}
.sq-off,      em.sq-off,
.sq-idle,     em.sq-idle        {background:#dfe3ef;color:#6b7192}
/* A Live Seller's day with no session: just a date, neither off nor absent. */
.sq-none {background:transparent;color:var(--c-text-3);box-shadow:inset 0 0 0 1px var(--c-border)}
.sq-today, .sq-future, em.sq-today {background:#fff6d9;color:#8a6a10;box-shadow:inset 0 0 0 1px #e6c86a}
.sq-future      {background:#f0f2f8;color:#a8adc4}
.ess-strip-k{display:flex;gap:12px;flex-wrap:wrap;justify-content:center;padding:0 0 4px;font-size:11.5px;
  font-size:10.5px;color:var(--c-text-3)}
.ess-strip-k em{display:inline-block;width:12px;height:12px;border-radius:3px;
  margin-right:4px;vertical-align:-1px;font-style:normal}

/* Records: five calendar days, each saying what it IS. */
.ess-rec{display:flex;align-items:center;gap:10px;padding:6px 0;
  border-bottom:1px solid var(--c-border-light);font-size:12.5px}
.ess-rec:last-child{border-bottom:0}
.ess-rec b{font-family:'JetBrains Mono','SFMono-Regular',monospace;font-size:11.5px;
  font-weight:500;color:var(--c-text-3);flex:none;width:46px}
.ess-rec .m{flex:1;min-width:0}
/* A day with no punches prints its state in the value column. It used to ALSO
   get a chip saying the same words - "no record" twice on one row. The chip
   is gone for those rows, so the colour it carried moves onto the text. */
.ess-rec-s{color:var(--c-text-3)}
.ess-rec-s.c-ab{color:var(--c-danger)}
.ess-rec-s.c-hd{color:var(--c-warning)}
.ess-rec-s.c-lv{color:#b45309}
.ess-tally-fn{font-size:10.5px;color:var(--c-text-3);margin:-4px 0 10px;text-align:center}
.ess-chip{font-size:10.5px;font-weight:600;padding:2px 8px;border-radius:20px;
  white-space:nowrap;flex:none;background:var(--c-surface-2);color:var(--c-text-2)}
.ess-chip.c-ot{background:#e2effb;color:#1d5fad}
.ess-chip.c-hd{background:#fef3c7;color:#92400e}
.ess-chip.c-ab{background:#fbe4e4;color:#c0202c}
.ess-chip.c-lv{background:#fff4e0;color:#b45309}

/* One card, banded, instead of three stacked ones. */
/* 20px sides, matching .card-header and every card body on this panel. The
   bands are the card's own rows, so they must share its gutter or the panel
   has two left edges. */
.ess-band{padding:14px 20px;border-bottom:1px solid var(--c-border-light)}
.ess-band:last-child{border-bottom:0}
/* Only the bare label centres. Records' sits inside .ess-band-h beside the
   Full attendance button, and that row stays left-and-right. */
.ess-band > .ess-band-t{text-align:center}
.ess-band-t{font-size:11px;font-weight:700;
  color:var(--c-text-3);margin-bottom:9px;display:block}
/* Records lost its card-header when it became a band, so its title and the
   Full attendance link share one row here instead. */
.ess-band-h{display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin-bottom:6px}
.ess-band-h .ess-band-t{margin-bottom:0}
.ess-band-h .btn{padding:4px 10px;font-size:11.5px}
/* Five rows do not need a band's full vertical padding on top - the strip
   above already separates them. */
/* 22px above and 22px below. The gap to RECORDS was 30px (4 legend + 16
   wrapper + 10 here) while the gap to BALANCES was 22px (8 + 14). */
/* The figures band. Its padding used to be an inline style, which no media
   query could reach, so on a phone it sat 6px inside the rows below it. */
.ess-band--stats{padding-bottom:8px;border-bottom:0}
.ess-band--rec{padding-top:14px;padding-bottom:8px}
/* THE LINE. Leave balances is now a band of the cutoff card rather than a
   second card, so what separates them is one rule, not a border, a gap and
   another border. The action band closes the card and takes none. */
.ess-band--bal{border-top:1px solid var(--c-border);padding-top:12px;padding-bottom:10px}
.ess-band--acts{border-bottom:0;padding-top:0}

/* UNDER 520px OF PANEL IT STOPS STACKING. On an 844x390 phone held sideways
   the panel is about 300px tall, and the identity block, the figures, the
   strip, the balances and the buttons cannot occupy 300px in one column at any
   type size a person could read. Two columns is not a preference here, it is
   the only arrangement that fits: identity down the left, the cutoff card down
   the right, each its own full-height grid. */
@media (max-height: 560px) {
  .ess-fit { grid-template-columns: minmax(150px, 30%) minmax(0, 1fr);
             grid-template-rows: auto minmax(0, 1fr); column-gap: 8px; }
  .ess-fit > .ess-day  { grid-column: 1; grid-row: 1; margin-bottom: 0; }
  /* Under the identity card, NOT across both columns: spanning it cost 92px
     of a 284px panel and overflowed the strip by 47px. */
  .ess-fit > .ess-ot   { grid-column: 1; grid-row: 2; align-self: start; margin-bottom: 0; }
  .ess-fit > .grid-2   { grid-column: 2; grid-row: 1 / -1; }
  .ess-fit .ess-who-r  { display: none; }
}

/* NEW REQUEST — grouped rows.
   .rt-grid/.rt-tile stay: the admin side still uses them. This is a second
   arrangement, not a replacement. A row carries the line that explains what
   Offsetting is; a two-column tile has nowhere to put it. */
.rt-list  { padding: 12px 12px 14px; }
.rt-group { font-size: 12.5px;
            color: var(--c-text-3); font-weight: 600; margin: 14px 2px 7px; }
.rt-group:first-child { margin-top: 0; }
.rt-row   { display: flex; align-items: center; gap: 12px; width: 100%;
            padding: 11px 12px; margin-bottom: 7px; text-align: left;
            background: var(--c-surface); border: 1px solid var(--c-border);
            border-radius: 10px; cursor: pointer; font: inherit; color: inherit; }
.rt-row:hover  { border-color: var(--c-primary); background: var(--c-bg); }
.rt-row:active { transform: translateY(1px); }
.rt-ic    { width: 34px; height: 34px; border-radius: 9px; flex: 0 0 auto;
            display: flex; align-items: center; justify-content: center;
            background: var(--c-bg); color: var(--c-primary); font-size: 16px; }
.rt-txt   { flex: 1; min-width: 0; }
.rt-txt b { display: block; font-size: 13px; font-weight: 600; }
.rt-go    { color: var(--c-text-3); flex: 0 0 auto; }

/* ── DASHBOARD ────────────────────────────────────────── */
/* Two independent columns: each card is as tall as its content. */
.dash-layout { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.dash-main, .dash-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.dash-card { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg); padding: 18px 20px; min-width: 0; }
.dash-h { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.dash-h h3 { margin: 0; font-size: 14px; font-weight: 700; color: var(--c-text); }
.dash-h > div > .dash-note { display: block; margin-top: 2px; }
.dash-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: -4px 0 14px; }
.dash-filters .filter-select { width: auto; }
.dash-note { font-size: 12px; color: var(--c-text-3); }
.dash-hero { display: flex; align-items: baseline; gap: 10px; }
.dash-hero b { font-size: 48px; font-weight: 700; line-height: 1; color: var(--c-text); letter-spacing: -0.02em; }
.dash-hero span { font-size: 14px; color: var(--c-text-2); }
.dash-meter { display: flex; gap: 2px; height: 10px; margin: 16px 0 12px; border-radius: 4px; overflow: hidden; background: var(--c-surface-2); }
.dash-meter i { display: block; min-width: 3px; }
.dash-legend { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: 12.5px; color: var(--c-text-2); }
.dash-legend span::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: 6px; border-radius: 2px; background: var(--k); }
.dash-legend b { color: var(--c-text); font-weight: 600; }
.dash-foot { margin-top: 12px; font-size: 12px; color: var(--c-text-3); }
/* Label on its own line, then the bar with its value at the tip. Side-by-side,
   a long branch name wrapped to two lines and the value sat a card-width away
   from a short bar. The track keeps 2.6em on the right so a full bar's value
   still fits. */
.dash-bars { display: grid; gap: 2px; }
/* Analytics' Headcount by Branch runs 40+ rows. At full width it flows down
   columns (largest first, read top to bottom) instead of one long list. */
.bar-cols .dash-bars { display: block; columns: 240px; column-gap: 40px; }
.bar-cols .dash-bar { break-inside: avoid; margin-bottom: 2px; }
/* Waiting for approval: donut + legend. Wraps to donut-over-legend when the
   card is narrow (phone, or the side column on a small laptop). */
.dash-donut { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.dash-donut svg { width: 128px; height: 128px; flex: none; margin: 0 auto; }
.dash-donut .seg { fill: none; stroke-width: 16; transition: opacity .12s; }
.dash-donut .c-n { font-size: 22px; font-weight: 800; fill: var(--c-text); font-variant-numeric: tabular-nums; }
.dash-donut .c-l { font-size: 9px; fill: var(--c-text-3); }
.dash-dl { flex: 1 1 190px; min-width: 0; display: grid; gap: 1px; }
.dash-dl-r { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto 3.2em; align-items: center; gap: 8px;
  width: calc(100% + 12px); margin: 0 -6px; padding: 5px 6px; border: 0; border-radius: var(--radius-sm);
  background: none; font: inherit; font-size: 12.5px; color: var(--c-text-2); text-align: left; cursor: pointer; }
.dash-dl-r:hover, .dash-dl-r:focus-visible { background: var(--c-surface-2); }
.dash-dl-r i { width: 10px; height: 10px; border-radius: 3px; }
.dash-dl-l { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dash-dl-v { font-weight: 600; color: var(--c-text); font-variant-numeric: tabular-nums; }
.dash-dl-p { text-align: right; font-size: 11.5px; color: var(--c-text-3); font-variant-numeric: tabular-nums; }
.dash-bar { display: grid; grid-template-columns: minmax(0, 1fr); row-gap: 3px;
            width: calc(100% + 12px); padding: 5px 6px; margin: 0 -6px; border: 0; border-radius: var(--radius-sm); background: none;
            font: inherit; font-size: 12.5px; color: var(--c-text-2); text-align: left; }
button.dash-bar { cursor: pointer; }
button.dash-bar:hover, button.dash-bar:focus-visible { background: var(--c-surface-2); }
.dash-bar-l { line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dash-bar-f { display: block; height: 8px; background: var(--c-brand); border-radius: 0 4px 4px 0; }
.dash-bar-v { text-align: right; font-weight: 600; color: var(--c-text); font-variant-numeric: tabular-nums; }
.dash-bar .dash-bar-t { display: flex; align-items: center; gap: 6px; min-height: 16px; padding-right: 2.6em; }
.dash-bar .dash-bar-f { flex: none; }
.dash-bar .dash-bar-v { flex: none; text-align: left; line-height: 1; }
.dash-cols svg { display: block; width: 100%; height: auto; }
.dash-cols text { font-size: 11px; fill: var(--c-text-3); font-variant-numeric: tabular-nums; }
.dash-cols text.is-today { fill: var(--c-text); font-weight: 700; }
.dash-cols .dash-col-v { fill: var(--c-text); font-weight: 600; }
.dash-grid-l { stroke: #e6e8f3; stroke-width: 1; }
.dash-col path { fill: #6e72c4; }
.dash-col.is-today path, .dash-col:hover path { fill: var(--c-brand); }
.dash-hit { fill: transparent; }
.dash-table { margin-top: 8px; font-size: 12px; color: var(--c-text-3); }
.dash-table summary { cursor: pointer; width: max-content; }
.dash-table table { margin-top: 8px; }
.dash-ann { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; padding: 12px 0; border-top: 1px solid var(--c-border); }
.dash-ann:first-of-type { border-top: 0; padding-top: 0; }
.dash-ann.has-img { grid-template-columns: 64px minmax(0, 1fr); }
.dash-ann-img { width: 64px; height: 80px; object-fit: cover; border-radius: var(--radius); background: var(--c-surface-2); display: block; }
.dash-rank { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.dash-rank li { display: grid; grid-template-columns: 1.4em minmax(0, 13em) minmax(0, 1fr) 4.6em; align-items: center; gap: 10px; padding: 5px 0; font-size: 12.5px; color: var(--c-text-2); }
.dash-rank-n { text-align: right; color: var(--c-text-3); font-variant-numeric: tabular-nums; }
.dash-rank .dash-bar-f { background: #6e72c4; }
.dash-rank li.is-lead .dash-bar-f { background: var(--c-brand); }
.dash-rank li.is-lead .dash-rank-name { color: var(--c-text); font-weight: 600; }
@media (max-width: 900px) {
  /* One column, cards interleaved by their inline order (today, approvals, month...). */
  .dash-layout { display: flex; flex-direction: column; align-items: stretch; }
  .dash-main, .dash-side { display: contents; }
  .dash-rank li { grid-template-columns: 1.4em minmax(0, 9em) minmax(0, 1fr) 4.2em; }
}

/* Employee dashboard (content-height cards; the fit-to-screen mode left 150px gaps) */
/* The generic .card child rules space the band with margins; card-body-sm opts it out
   of their padding so the spacing is not applied twice (it left 37px under the key). */
.ess-band.card-body-sm { padding: 0; margin: 16px 20px 18px; }   /* the margins it used to take from the .card rules,
   which exempt .card-body-sm since __486 - without them the day squares ran edge to edge, 40px wider */
.ess-today-s { font-size: 16px; font-weight: 600; color: var(--c-text); }
.ess-today-a { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.ess-pay { display: flex; align-items: baseline; gap: 8px; margin: 0 0 14px; }
.ess-pay b { font-size: 28px; font-weight: 700; line-height: 1.1; color: var(--c-text); letter-spacing: -0.01em; }
.ess-pay span { font-size: 13px; color: var(--c-text-2); }
.ess-strip .sq { cursor: pointer; }
.ess-strip .sq.is-sel, .ess-strip .sq:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 1px; }
.ess-strip-d { margin-top: 8px; font-size: 12.5px; color: var(--c-text-2); }

/* PHONE TAP TARGETS (<= 640px). A sweep of every admin screen at 320-430px found
   no overflow anywhere, but 372 controls under 36px: the row approve / reject
   buttons at 32x32 on eight screens (a mis-tap decides somebody's request), form
   checkboxes at 13px, the OT-hours inputs at 24px, the Early Bird scope switch at
   28px, the filters and search boxes at 34px. Desktop sizes are untouched. */
@media (max-width: 640px) {
  /* #req-tbl sets its row buttons to 32px with a more specific rule: match it. */
  .action-btn, #req-tbl .act-cell .action-btn { width: 40px; min-width: 40px; height: 40px; }
  #req-tbl .act-cell .btn-sm, #req-tbl .act-cell input { height: 40px; }
  .ci-gate .g-fix { min-height: 36px; padding: 6px 10px; }
  .btn, .seg-btn, .rt-view, .dash-dl-r, .rg-tab { min-height: 36px; }
  .req-seg-btn, .req-tabs .req-tab { min-height: 36px; }
  .filter-select, .search-input, .filters .btn, .req-toolbar .btn,
  .card-header .btn, .page-hdr .btn { min-height: 40px; }
  .ot-hrs-in, .ded-amt, .req-pg-size { min-height: 36px; font-size: 14px; }
  .eb-scope-btn { min-height: 36px; padding-left: 12px; padding-right: 12px; }
  .btn.btn-ghost.btn-sm, .btn-sm { min-height: 36px; }
  input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; }
}
/* The slide-in menu is used by touch: its 23 items were 33px tall. Phones, and
   any touch screen while the sidebar is a drawer (below 1280px); a laptop with a
   mouse keeps the compact list. The list itself scrolls (.sidebar-nav). */
@media (max-width: 640px), (max-width: 1279px) and (pointer: coarse) {
  .sidebar .nav-item { min-height: 42px; }
}

/* ── CONTACT HR / MESSAGES ──────────────────────────────────
   The conversation card is exactly the height the content area leaves under the
   top bar, and the message list is its only flexing row (min-height:0), so a long
   thread scrolls inside the card and the reply box stays on screen at any size.
   It was calc(100vh - 150px): 76px of empty page under the card on a phone, and
   vh counts a phone's address bar, dvh does not. Every row is a .card-body so the
   card-content inset (.card > *) does not add 20px margins to a 100%-wide row -
   that pushed the conversation date off the right edge of the card. */
.chat-card { display: flex; flex-direction: column; min-height: 340px;
  height: calc(100vh - var(--topbar-h) - 2 * var(--content-pad, 24px));
  height: calc(100dvh - var(--topbar-h) - 2 * var(--content-pad, 24px)); }
.chat-card > .card-header { flex-wrap: nowrap; }
.chat-card > .card-header .card-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 20px; }
.chat-msg { display: flex; margin-bottom: 12px; }
.chat-msg.mine { justify-content: flex-end; }
.chat-bubble { max-width: 70%; padding: 10px 14px; font-size: 13px; white-space: pre-wrap; word-break: break-word;
  background: var(--c-surface-2); color: var(--c-text); border: 1px solid var(--c-border); border-radius: 12px 12px 12px 2px; }
.chat-msg.mine .chat-bubble { background: var(--c-brand); color: #fff; border-color: transparent; border-radius: 12px 12px 2px 12px; }
.chat-meta { font-size: 10px; margin-top: 5px; color: var(--c-text-3); }
.chat-msg.mine .chat-meta { color: rgba(255,255,255,.6); }
/* Seen: faces under the newest message each person has read (__583). */
.chat-seen { display: flex; justify-content: flex-end; gap: 3px; margin: -6px 0 12px; }
.chat-seen-who { display: flex; cursor: default; }
.ava.chat-seen-ava { width: 16px; height: 16px; font-size: 7px; font-weight: 700; }
.chat-compose { display: flex; gap: 8px; align-items: center; padding: 12px 20px; border-top: 1px solid var(--c-border); }
.chat-compose .field-input, .chat-compose .skel-input { flex: 1; min-width: 0; }
.chat-attach { width: 36px; height: 36px; padding: 0; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; }
.chat-attach i { font-size: 17px; }
.chat-att-pending { display: flex; align-items: center; gap: 8px; padding: 8px 20px 0; font-size: 12px; min-width: 0; }
.chat-att-pending[hidden] { display: none; }
.chat-att-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.chat-att-size { flex-shrink: 0; color: var(--c-text-3); font-size: 11px; }
.chat-att-x { border: 0; background: none; color: var(--c-text-3); cursor: pointer; padding: 2px; line-height: 1; flex-shrink: 0; }
.chat-att-imgwrap { display: block; margin: -2px 0 6px; }
.chat-att-img { display: block; max-width: min(240px, 100%); max-height: 240px; border-radius: 8px; object-fit: contain; background: rgba(127,127,127,.12); }
.chat-att-file { display: flex; gap: 8px; align-items: baseline; min-width: 0; margin-bottom: 6px; padding: 8px 10px; border-radius: 8px; background: rgba(127,127,127,.12); color: inherit; text-decoration: none; }
.chat-att-file:hover { text-decoration: underline; }
.chat-msg.mine .chat-att-file { background: rgba(255,255,255,.16); }
.chat-msg.mine .chat-att-size { color: rgba(255,255,255,.7); }
.msg-rows { padding: 0; }
.msg-row { display: flex; gap: 12px; align-items: center; width: 100%; padding: 12px 20px; border: 0; background: none; text-align: left; cursor: pointer; font: inherit; color: inherit; }
.msg-row + .msg-row { border-top: 1px solid var(--c-border); }
.msg-row:hover, .msg-row:focus-visible { background: var(--c-surface-2); }
.msg-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.msg-row-line { display: flex; gap: 8px; align-items: baseline; min-width: 0; }
.msg-row-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-row-branch, .msg-row-date { font-size: 11px; color: var(--c-text-3); white-space: nowrap; }
.msg-row-branch { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.msg-row-date { flex-shrink: 0; margin-left: auto; }
.msg-row-last { flex: 1; min-width: 0; font-size: 12px; color: var(--c-text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-row-line .req-tab-badge { flex-shrink: 0; align-self: center; }
@media (max-width: 640px) {
  .chat-scroll { padding: 12px; }
  .chat-compose { padding: 10px 12px; }
  .chat-att-pending { padding: 6px 12px 0; }
  .chat-attach { width: 34px; height: 34px; }
  .chat-att-img { max-width: min(200px, 100%); max-height: 200px; }
  .chat-bubble { max-width: 85%; }
  .chat-compose .field-input { font-size: 16px; }   /* below 16px, iPhone Safari zooms the page when the box is tapped */
  .chat-send { width: 40px; height: 40px; padding: 0; flex-shrink: 0; }
  .chat-send-lbl { display: none; }
  .msg-row { padding: 12px 16px; }
}

/* ── REQUESTS: QUEUE TILES (owner, 21 Oct) ─────────────────────
   Count on top, label under it, so what is waiting reads at a glance. Replaces
   the folder tabs (desktop) and the 2-across tiles (narrow) for Requests only;
   Violations keeps its tabs (two of its four carry no count). Compact on
   purpose: 11 across a 1440 screen, 4 across a phone at ~36px a row. */
.card > .req-head.req-tiles { padding: 10px 12px !important; background: var(--c-surface); }
/* Flex, not an even grid: an even column made "Outside Geofence" wrap and, stretched,
   every tile in its row 57px tall. Each tile grows from its own label instead. */
.req-tiles > .req-tabs { display: flex; flex-wrap: wrap; gap: 6px; align-items: stretch;
  margin: 0; padding: 0; background: none; border: 0; overflow: visible; }
.req-tiles .req-tabs .req-tab { display: flex; flex-direction: column-reverse; align-items: flex-start; justify-content: flex-end;
  flex: 1 1 auto; min-width: 78px; max-width: 200px; gap: 1px;   /* Violations has 4: no tile as wide as a quarter screen */ height: auto; min-height: 0; padding: 5px 10px; font-size: 11px; font-weight: 500; line-height: 1.2;
  color: var(--c-text-2); text-align: left; white-space: nowrap;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 8px; box-shadow: none; }
.req-tiles .req-tabs .req-tab:hover { background: var(--c-surface); border-color: var(--c-brand-20); color: var(--c-text); }
.req-tiles .req-tabs .req-tab.active { background: var(--c-brand-10); border-color: var(--c-brand); color: var(--c-brand);
  font-weight: 600; box-shadow: inset 0 0 0 1px var(--c-brand); }
.req-tiles .req-tabs .req-tab .req-tab-badge,
.req-tiles .req-tabs .req-tab .req-tab-zero { min-width: 0; height: auto; padding: 0; background: none; border-radius: 0;
  font-size: 15px; font-weight: 800; line-height: 1.15; opacity: 1; }
.req-tiles .req-tabs .req-tab .req-tab-badge { color: var(--c-brand); }
.req-tiles .req-tabs .req-tab .req-tab-zero { color: var(--c-text-3); font-weight: 600; }
/* Too narrow for one row: an even grid, so a tile left alone on the last row does not
   stretch across the card (Attendance did, at 1024px). */
@container reqhead (max-width: 1060px) {
  .req-tiles > .req-tabs { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .req-tiles .req-tabs .req-tab { min-width: 0; white-space: normal; }
}
@container reqhead (max-width: 520px) {
  .req-tiles > .req-tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 5px; }
  .req-tiles .req-tabs .req-tab { min-width: 0; padding: 3px 7px; font-size: 10.5px; line-height: 1.15; white-space: normal; }
  .req-tiles .req-tabs .req-tab .req-tab-badge,
  .req-tiles .req-tabs .req-tab .req-tab-zero { font-size: 13px; }
}
@media (max-width: 640px) {
  .card > .req-head.req-tiles { padding: 8px !important; }
  /* The phone tap-target sweep set these to 40px; 36px is still a comfortable tap. */
  .req-toolbar .search-input, .req-toolbar .btn, .req-toolbar .req-seg-btn { min-height: 36px; height: 36px; }
  .req-toolbar .btn { font-size: 12px; }
}

/* ── REQUESTS: ONE BUTTON LANGUAGE (owner, 5 Nov: "buttons in requests and
   approval is not consistent") ─────────────────────────────────────────────
   The Action column mixed 32px outlined icon squares with solid green, navy
   and red labelled buttons, stacked at ragged widths. Every control in it is
   now the same outlined button as the icon squares; colour is in the text and
   icon only: green pays or accepts, red refuses or marks absent, navy (lightly
   filled) is the suggested choice, grey is neutral. Several labelled choices
   stack as one column of equal width. The JS still says which is which. */
.req-acts { display: flex; gap: 6px; justify-content: flex-end; align-items: center; flex-wrap: nowrap; }
.req-acts .btn { height: 32px; min-height: 32px; padding: 0 12px; gap: 6px; font-size: 12px; font-weight: 600;
  border-radius: var(--radius); background: var(--c-surface); border: 1px solid var(--c-border);
  color: var(--c-text-2); box-shadow: none; justify-content: center; white-space: nowrap; }
.req-acts .btn i { font-size: 15px; }
.req-acts .btn:hover { background: var(--c-surface-2); border-color: var(--c-text-3); color: var(--c-text); box-shadow: none; }
.req-acts .btn-success { color: var(--c-success); border-color: rgba(15,123,65,.35); }
.req-acts .btn-success:hover { background: var(--c-success-bg); border-color: var(--c-success); color: var(--c-success); }
.req-acts .btn-danger { color: var(--c-danger); border-color: rgba(192,32,44,.35); }
.req-acts .btn-danger:hover { background: var(--c-danger-bg); border-color: var(--c-danger); color: var(--c-danger); }
.req-acts .btn-primary { color: var(--c-brand); border-color: rgba(3,0,131,.35); background: var(--c-brand-10); }
.req-acts .btn-primary:hover { background: var(--c-brand-20); border-color: var(--c-brand); color: var(--c-brand); }
.req-acts .action-btn { border-radius: var(--radius); }
.req-acts .action-btn--label { height: 32px; font-size: 12px; }
.req-acts .req-acts-col, .req-acts .an-acts.req-acts-col { display: flex; flex-direction: column; align-items: stretch;
  flex-wrap: nowrap; gap: 6px; width: 184px; max-width: none; }
.req-acts .req-acts-col .btn { width: 100%; }
.req-brk { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--c-text-3); }
.req-brk > span:first-child { margin-right: auto; }
.req-brk .ot-hrs-in { width: 64px; height: 32px; }

/* ── REQUESTS ON A PHONE: a compact card, not a label/value table ─────────
   Each row printed six labelled lines (Employee, Type, Details, Filed, Status,
   Action) and the OT editor stacked down the right edge: one request filled
   the screen. Now: name and status on top, type and filed date under it,
   details full width and left-aligned, the buttons along the bottom. */
@media (max-width: 1023px) {
  #req-tbl tbody tr:not(:has(> td[colspan])) { display: grid; grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "emp st" "type filed" "det det" "act act"; gap: 6px 10px; padding: 10px 12px; }
  #req-tbl tbody tr:not(:has(> td[colspan])) > td { display: block; padding: 0; border: 0; text-align: left; min-width: 0; }
  #req-tbl tbody tr:not(:has(> td[colspan])) > td[data-label]::before { content: none; }
  #req-tbl tbody tr > td:nth-child(1) { grid-area: emp; }
  #req-tbl tbody tr > td:nth-child(2) { grid-area: type; }
  #req-tbl tbody tr > td:nth-child(3) { grid-area: det; font-size: 12.5px; line-height: 1.45; }
  #req-tbl tbody tr > td:nth-child(4) { grid-area: filed; text-align: right; font-size: 11.5px; align-self: center; }
  #req-tbl tbody tr:not(:has(> td[colspan])) > td:nth-child(4)::before { content: 'Filed '; display: inline; font-weight: 400; }
  #req-tbl tbody tr > td:nth-child(5) { grid-area: st; text-align: right; align-self: start; justify-self: end; }
  #req-tbl tbody tr > td:nth-child(6) { grid-area: act; border-top: 1px solid var(--c-border); padding-top: 8px; margin-top: 2px; }
  #req-tbl tbody tr > td:nth-child(6):not(:has(button, input)) { display: none; }
  #req-tbl .cell-with-ava { gap: 8px; }
  #req-tbl .cell-with-ava .td-secondary { font-size: 11.5px; }
  #req-tbl td .ot-hrs-wrap { justify-content: flex-start; }
  #req-tbl .req-acts { justify-content: flex-end; flex-wrap: wrap; }
  #req-tbl .req-acts .req-acts-col, #req-tbl .req-acts .an-acts.req-acts-col { flex-direction: row; flex-wrap: wrap; width: 100%; }
  #req-tbl .req-acts .req-acts-col .btn { width: auto; flex: 1 1 auto; }
  #req-tbl .req-brk { flex: 1 1 100%; }
}
@media (max-width: 640px) {
  #req-tbl .act-cell .action-btn { width: 36px; min-width: 36px; height: 36px; }
  #req-tbl .act-cell .btn-sm, #req-tbl .act-cell .btn, #req-tbl .act-cell input { height: 36px; min-height: 36px; }
}

/* ── ALERTS WITH BOLD WORDS IN THEM ─────────────────────────────────────────
   .alert is a flex row, so a sentence like "Turns this form into a real
   <strong>approved leave</strong>: ..." became three flex items: three narrow
   columns side by side (Convert to Leave, 28 alerts in all). Run the text as
   ordinary flowing text and hang the icon in the left padding instead. An
   alert shown by script (style="display:flex") holds icon + one span and is
   unaffected. */
.alert:has(> i:first-child):not([style*="display:flex"]):not([style*="display: flex"]) { display: block; position: relative; padding-left: 36px; }
.alert:has(> i:first-child):not([style*="display:flex"]):not([style*="display: flex"]) > i:first-child { position: absolute; left: 14px; top: 12px; margin: 0; }

/* Performance review criteria on a phone: criterion and its rating side by side.
   As a label/value card the 40% "Criteria" label left the description a
   one-letter-wide column (each word printed downwards). */
@media (max-width: 1023px) {
  table.data-tbl.perf-tbl:has(thead) tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; padding: 10px 12px; align-items: start; }
  table.data-tbl.perf-tbl:has(thead) tbody td { display: block !important; padding: 0; border: 0; text-align: left; }
  table.data-tbl.perf-tbl:has(thead) tbody td[data-label]::before { content: none; }
}

/* ── PHONE ROW-CARDS, COMPACT (owner, 5 Nov: "the cards in mobile view is too
   large") ── every table that becomes label/value cards under 1024px: rows
   were 9px/14px with a 13.5px value, so a 6-column record was ~300px tall.
   Tighter rows, smaller type, the same information. */
@media (max-width: 640px) {
  table.data-tbl:has(thead) tbody { padding: 8px; gap: 8px; }
  table.data-tbl:has(thead) tbody tr { padding: 0; }
  table.data-tbl:has(thead) tbody td { padding: 6px 12px; gap: 10px; font-size: 12.5px; min-height: 0; }
  table.data-tbl:has(thead) tbody td[data-label]::before { font-size: 11px; font-weight: 600; flex-basis: 36%; }
  table.data-tbl:has(thead) tbody td .td-primary { font-size: 13px; }
  table.data-tbl:has(thead) tbody td .td-secondary { font-size: 11.5px; }
  table.data-tbl:has(thead) tbody td .badge { font-size: 10.5px; padding: 2px 7px; }
  table.data-tbl:has(thead) tbody td .action-btn { width: 34px; min-width: 34px; height: 34px; }
  table.data-tbl:has(thead) tbody td .ava, table.data-tbl:has(thead) tbody td .avatar { width: 28px; height: 28px; font-size: 11px; }
}

/* A labelled cell holding two or more blocks (Violations > Details: the punch,
   then what approving would mean, then the month's count) laid each block out
   as its own column beside the label, three words wide. Stack them on the
   value side instead, right-aligned like every other value. */
@media (max-width: 1023px) {
  table.data-tbl:has(thead) tbody td[data-label]:has(> div + div) { display: grid; grid-template-columns: 36% minmax(0, 1fr); column-gap: 10px; row-gap: 2px; align-items: start; }
  table.data-tbl:has(thead) tbody td[data-label]:has(> div + div)::before { grid-row: 1 / span 12; }
  table.data-tbl:has(thead) tbody td[data-label]:has(> div + div) > * { grid-column: 2; }
  table.data-tbl:has(thead) tbody td.td-prose[data-label]:has(> div + div) { grid-template-columns: minmax(0, 1fr); }
  table.data-tbl:has(thead) tbody td.td-prose[data-label]:has(> div + div)::before { grid-row: auto; }
  table.data-tbl:has(thead) tbody td.td-prose[data-label]:has(> div + div) > * { grid-column: 1; }
}

/* Assign Deliveries on a phone: five controls in one row were each ~50px wide
   ("F..", "T.."). Two rows: from / to, then note / rate / remove. */
@media (max-width: 640px) {
  .drv-line { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 84px 36px !important; padding-bottom: 6px; border-bottom: 1px solid var(--c-border); }
  .drv-line .drv-from { grid-column: 1 / 3; }
  .drv-line .drv-to { grid-column: 3 / 5; }
  .drv-line .drv-note { grid-column: 1 / 3; }
}

/* Modals on a phone: tighter chrome, and a footer that wraps. Three footer
   buttons (Performance review: Cancel / Save draft / Release to employee) ran
   past the left edge and Cancel was cut off. */
@media (max-width: 480px) {
  .modal-header { padding: 14px 16px 12px; }
  .modal-title { font-size: 15px; }
  .modal-body { padding: 14px 16px; }
  .modal-footer { padding: 10px 16px; flex-wrap: wrap; }
  .modal-footer > .btn { flex: 1 1 auto; justify-content: center; }
}

/* SHORT RECORDS IN A POP-UP (Leave Balances, Manage Schedules) on a phone: one
   line per value made every employee / schedule a 200px card, four to a
   screen. .tbl-mini: the first cell is the title, the rest sit in one row
   with a small label over each value, the row's buttons at its end. */
@media (max-width: 640px) {
  table.data-tbl.tbl-mini:has(thead) tbody tr { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px 10px; padding: 8px 12px; }
  table.data-tbl.tbl-mini:has(thead) tbody td { display: flex !important; flex-direction: column; align-items: flex-start; justify-content: flex-end;
    flex: 1 1 0; min-width: 0; padding: 0; border: 0; gap: 2px; text-align: left; }
  table.data-tbl.tbl-mini:has(thead) tbody td:first-child { flex: 1 1 100%; }
  table.data-tbl.tbl-mini:has(thead) tbody td:last-child { flex: 0 0 auto; flex-direction: row; gap: 6px; align-self: flex-end; }
  table.data-tbl.tbl-mini.sched-mini:has(thead) tbody td { white-space: nowrap; flex: 0 1 auto; }
  table.data-tbl.tbl-mini.sched-mini:has(thead) tbody td:first-child { flex: 1 1 100%; }
  table.data-tbl.tbl-mini.sched-mini:has(thead) tbody td:nth-last-child(2) { flex: 1 1 0; white-space: normal; }
  table.data-tbl.tbl-mini.sched-mini:has(thead) tbody td:last-child { flex: 0 0 auto; }
  table.data-tbl.tbl-mini:has(thead) tbody td[data-label]::before { flex: none; font-size: 10.5px; }
  table.data-tbl.tbl-mini:has(thead) tbody td:first-child::before { content: none; }
  table.data-tbl.tbl-mini:has(thead) tbody td .field-input { width: 100% !important; height: 34px; }
}

/* Violations > Pending / Penalties on a phone: the Requests card (name, type +
   date, details, buttons) instead of five label/value lines. */
@media (max-width: 1023px) {
  #vio-tbl.vio-cards tbody tr:not(:has(> td[colspan])) { display: grid; grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "emp type" "det det" "filed act"; gap: 6px 10px; padding: 10px 12px; align-items: center; }
  #vio-tbl.vio-cards tbody tr:not(:has(> td[colspan])) > td { display: block; padding: 0; border: 0; text-align: left; min-width: 0; }
  #vio-tbl.vio-cards tbody tr:not(:has(> td[colspan])) > td[data-label]::before { content: none; }
  #vio-tbl.vio-cards tbody tr > td:nth-child(1) { grid-area: emp; }
  #vio-tbl.vio-cards tbody tr > td:nth-child(2) { grid-area: type; justify-self: end; }
  #vio-tbl.vio-cards tbody tr > td:nth-child(3) { grid-area: det; font-size: 12.5px; line-height: 1.45; }
  #vio-tbl.vio-cards tbody tr > td:nth-child(4) { grid-area: filed; font-size: 11.5px; }
  #vio-tbl.vio-cards tbody tr > td:nth-child(5) { grid-area: act; justify-self: end; }
}

/* FILTER BARS ON A PHONE: 40px controls with 10px gaps put Attendance's
   search, two selects, two dates and four buttons in six rows - 370px before
   the first record. Smaller controls, and the trailing actions share a row. */
@media (max-width: 640px) {
  .filters { padding: 10px 12px; gap: 8px; }
  .filters .filter-select, .filters .search-input, .filters .btn { min-height: 36px; height: 36px; font-size: 13px; }
  .filters > .btn { flex: 1 1 0; justify-content: center; }
  .filters .filters-end { display: flex; gap: 8px; flex: 1 1 100%; margin-left: 0; }
  .filters .filters-end .btn { flex: 1 1 0; justify-content: center; padding: 0 8px; font-size: 12px; white-space: nowrap; }
}
/* ATTENDANCE RECORDS on a phone: eight label/value lines per record became a
   two-column card - name and status, then schedule | punches, timing | hours,
   payroll | buttons - each value under a small label. */
@media (max-width: 1023px) {
  table.data-tbl:has(> #att-body) tbody tr:not(:has(> td[colspan])) { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "emp st" "sch att" "tim tot" "pay act"; gap: 8px 12px; padding: 10px 12px; }
  table.data-tbl:has(> #att-body) tbody tr:not(:has(> td[colspan])) > td { display: block !important; padding: 0; border: 0; text-align: left; min-width: 0; }
  table.data-tbl:has(> #att-body) tbody tr:not(:has(> td[colspan])) > td[data-label]::before { display: block; margin-bottom: 2px; font-size: 10.5px; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(1) { grid-area: emp; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(1)::before,
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(4)::before,
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(8)::before { content: none !important; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(2) { grid-area: sch; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(3) { grid-area: att; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(4) { grid-area: st; justify-self: end; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(5) { grid-area: tim; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(6) { grid-area: tot; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(7) { grid-area: pay; align-self: end; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(8) { grid-area: act; justify-self: end; align-self: end; }
  table.data-tbl:has(> #att-body) tbody tr > td:nth-child(8) > * { justify-content: flex-end; }
}

/* PAYROLL REGISTER on a phone: eleven label/value lines per employee (~380px).
   Name and net pay on top, the other figures three to a row under small
   labels. */
@media (max-width: 640px) {
  table.data-tbl.reg-tbl:has(thead) tbody tr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px 10px; padding: 10px 12px; }
  table.data-tbl.reg-tbl:has(thead) tbody td { display: block !important; padding: 0; border: 0; text-align: left; min-width: 0; }
  table.data-tbl.reg-tbl:has(thead) tbody td[data-label]::before { display: block; margin-bottom: 1px; font-size: 10.5px; }
  table.data-tbl.reg-tbl:has(thead) tbody td:first-child { grid-column: 1 / 3; grid-row: 1; }
  table.data-tbl.reg-tbl:has(thead) tbody td:first-child::before { content: none; }
  table.data-tbl.reg-tbl:has(thead) tbody td:last-child { grid-column: 3; grid-row: 1; text-align: right; align-self: center; }
}

/* CASH ADVANCE / LOAN LEDGER on a phone: a 340px minimum card in a ~310px
   column ran off the right edge. Tighter card, the filter chips scroll in one
   row, Export takes its own row. */
@media (max-width: 640px) {
  .loan-grid { gap: 10px; }
  .loan-card2 { padding: 12px 14px; gap: 10px; border-radius: var(--radius); }
  .loan-avatar { width: 34px; height: 34px; font-size: 12px; border-radius: 9px; }
  #req-loans-host > div:first-child { gap: 8px; }
  #req-loans-host > div:first-child > .req-tabs { flex: 1 1 100%; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  #req-loans-host > div:first-child > .req-tabs .req-tab { white-space: nowrap; flex: 0 0 auto; }
  #req-loans-host #loan-export { margin-left: 0 !important; flex: 1 1 100%; justify-content: center; }
}

/* The ledger got both generic .card-child insets (20px padding AND 20px
   margin): 40px each side, narrower than every other list in the card. */
.card > #req-loans-host { margin-left: 0; margin-right: 0; padding-top: 12px; padding-bottom: 16px; }
@media (max-width: 640px) { .card > #req-loans-host { padding-left: 12px; padding-right: 12px; } }
