/* ============================================================================
   PLATFORM BRAND — gold #BF9B2E + grey #6D6E71  (owner's decision, 2 Aug 2026)
   ----------------------------------------------------------------------------
   These values REPLACE the old navy #1B3C8B / blue #2E7DF0 theme. That theme is
   retired. Earlier notes in this project said "never change navy/blue" — the
   owner overruled that himself: "Golden color code: #bf9b2e, Grey color code:
   #6d6e71 is my new color theme, and set as we change in super admin, it should
   reflect everywhere in every level." DO NOT "restore" navy/blue.

   These literals are only the boot-time fallback. The live values are written
   onto document.documentElement by applyPlatformBrand() in app.js, from the ONE
   source of truth: master site_content.brand (Super Admin -> Homepage CMS ->
   Brand & Colours). Keep the two in step so an un-scripted page looks right.

   --primary      the owner's gold, exactly as given. Fills, gradients, borders.
   --primary-d    shade(--primary, 0.28) — the dark end of the gradient.
   --primary-ink  the same gold darkened until it is legible as TEXT on white
                  (4.7:1). Raw #BF9B2E on white is only 2.6:1, which fails
                  WCAG AA, so brand-coloured *text* uses this instead. It is a
                  shade of his colour, not a different colour.
   --navy         kept as a NAME only, for the hundreds of existing rules that
                  reference it; its VALUE is now the owner's grey.
   --blue         likewise a legacy alias; its value is now the gold.
   --brand-dark   the near-black grey that replaces the old #0e1b34 navy on the
                  footer, carousel, promo band and detail-page hero.
   Status colours (green/red/amber/grey) are deliberately NOT brand-driven — a
   brand colour must never repaint "Expired" red.
   ========================================================================== */
:root{
  --primary:#BF9B2E; --primary-d:#8A7021; --primary-ink:#8A7021;
  --primary-grad:linear-gradient(135deg,#8A7021,#BF9B2E);
  --navy:#6D6E71; --blue:#BF9B2E; --sky:#D9BA57; --wordmark-1:#6D6E71; --wordmark-2:#BF9B2E;
  --brand-dark:#292A2B;
  --bg:#f7f8fc; --surface:#ffffff; --border:#eceef5; --text:#0f172a; --muted:#94a3b8;
  --sidebar:#ffffff; --sidebar-text:#475569; --danger:#ef4444; --warn:#f59e0b;
  --ok:#10b981; --info:#06b6d4; --radius:16px; --shadow:0 1px 3px rgba(16,24,40,.06);
}
[data-theme="dark"]{
  --bg:#0b1020; --surface:#121a2f; --border:#1e293b; --text:#e2e8f0; --muted:#64748b;
  --sidebar:#0f1626; --sidebar-text:#94a3b8; --shadow:0 1px 3px rgba(0,0,0,.4);
  /* On a dark surface the raw gold is already legible, so ink goes back to it. */
  --primary-ink:#D9BA57;
}
*{box-sizing:border-box;margin:0;padding:0}
/* One stack for the whole product — see fonts.css. It is a variable rather
   than a literal list because the same value is needed in a few other places,
   and because a font change should be one edit, not a search-and-replace.
   `font-synthesis:none` stops the browser inventing a fake bold or italic when
   a real one exists in the variable file; the faked version is always uglier. */
body{font-family:var(--font-brand);font-synthesis:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:var(--bg);color:var(--text);font-size:14px}
/* RTL no longer needs its own font: unicode-range already routes Arabic script
   to the Arabic face per character, and Urdu is handled by html[lang="ur"]. */
[dir="rtl"]{font-family:var(--font-brand)}
a{color:inherit;text-decoration:none}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
input,select,textarea{font-family:inherit;font-size:14px}
/* Form controls take the brand EVERYWHERE (owner, 2 Aug: "in drop down, hover
   colour will be gold" — "everywhere"). accent-color paints the tick in a
   checkbox, the dot in a radio, the range thumb and the caret; the two option
   rules paint the highlighted row in an open <select> list.
   Honest limit: the open list of a native <select> is drawn partly by the
   operating system. Chrome and Edge on Windows honour these rules; Safari and
   some Linux builds keep their own highlight and there is no CSS that can
   override them — the only way would be to replace every <select> in the
   product with a custom widget, which is a far bigger change than a colour. */
input,select,textarea,button{accent-color:var(--primary)}
select option:hover,
select option:focus,
select option:checked{background:var(--primary);color:var(--brand-dark)}
select option{background:var(--surface);color:var(--text)}
/* Text selection and scrollbars are brand surfaces too — "everywhere". */
::selection{background:var(--primary);color:var(--brand-dark)}
::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--primary) 55%,#c7ccd8)}
::-webkit-scrollbar-thumb:hover{background:var(--primary)}
.hidden{display:none!important}

/* ---- Login ---- */
.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;background:var(--primary-grad);padding:20px}
.login-card{background:var(--surface);border-radius:24px;padding:40px;width:100%;max-width:400px;box-shadow:0 20px 60px rgba(0,0,0,.25)}
.login-logo{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.login-logo .mark{width:44px;height:44px;border-radius:12px;background:var(--primary-grad);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:20px}
.login-logo b{font-size:20px;background:var(--primary-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.login-card h2{margin:18px 0 4px;font-size:22px}
.login-card p.sub{color:var(--muted);margin-bottom:22px}
.field{margin-bottom:16px}
.field label{display:block;font-weight:600;margin-bottom:6px;font-size:13px}
.field input,.field select,.field textarea{width:100%;padding:11px 13px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text)}
.field input:focus,.field select:focus{outline:none;border-color:var(--primary)}
.btn{display:inline-flex;align-items:center;gap:8px;justify-content:center;padding:11px 18px;border-radius:12px;font-weight:600;background:var(--primary-grad);color:#fff;transition:.15s}
.btn:hover{filter:brightness(1.05)}
.btn.block{width:100%}
.btn.ghost{background:transparent;border:1px solid var(--border);color:var(--text)}
.btn.sm{padding:7px 12px;font-size:13px;border-radius:9px}
.btn.danger{background:linear-gradient(135deg,#ef4444,#f87171)}
.login-err{background:#fef2f2;color:#b91c1c;padding:10px 12px;border-radius:10px;margin-bottom:14px;font-size:13px}
.login-hint{margin-top:16px;text-align:center;color:var(--muted);font-size:12px}

/* ---- App shell ---- */
.app{display:flex;min-height:100vh}
.sidebar{width:250px;background:var(--sidebar);border-right:1px solid var(--border);flex-shrink:0;position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column}
.brand{display:flex;align-items:center;gap:10px;padding:20px 18px}
.brand .mark{width:40px;height:40px;border-radius:11px;background:var(--primary-grad);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800}
.brand b{font-size:16px;background:var(--primary-grad);-webkit-background-clip:text;background-clip:text;color:transparent;display:block;line-height:1.1}
.brand small{color:var(--muted);font-size:13px}
.nav-group{padding:6px 12px}
.nav-group h6{color:var(--muted);font-size:12px;letter-spacing:.08em;font-weight:700;padding:12px 10px 6px}
.nav-item{display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:10px;color:var(--sidebar-text);font-weight:500;margin-bottom:2px}
.nav-item svg{width:18px;height:18px;flex-shrink:0}
.nav-item{transition:background .15s ease,color .15s ease,transform .12s ease,box-shadow .15s ease}
.nav-item:hover{background:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary-ink);transform:translateX(3px)}
.nav-item:hover svg{color:var(--primary-ink)}
.nav-item.active{background:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary-ink);font-weight:600}
.sidebar-user{margin-top:auto;padding:14px 18px;border-top:1px solid var(--border);display:flex;align-items:center;gap:10px}
.avatar{width:38px;height:38px;border-radius:50%;background:var(--primary-grad);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;flex-shrink:0}
.main{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{display:flex;align-items:center;gap:14px;padding:14px 24px;background:var(--surface);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:20}
/* min-width:0 is not optional here. A flex child will not shrink below its own
   text by default, so a long page title pushed the language, bell, theme and
   account controls straight out of the bar and they piled on top of each other.
   Applies at every width — a long title did the same on a narrow laptop. */
.topbar h1{font-size:17px;font-weight:600;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.icon-btn{width:38px;height:38px;border-radius:11px;border:1px solid var(--border);display:flex;align-items:center;justify-content:center;color:var(--muted)}
.icon-btn:hover{color:var(--text)}
.chip{display:flex;align-items:center;gap:8px;border:1px solid var(--border);border-radius:11px;padding:6px 10px}
.chip small{color:var(--muted);font-size:13px;line-height:1}
.content{padding:22px 24px;flex:1}
.hamburger{display:none}
.ham-label{display:none}
.sidebar-backdrop{display:none}

/* ---- Banner ---- */
.trial-banner{background:#fff1f2;border:1px solid #fecdd3;border-radius:14px;padding:14px 18px;display:flex;align-items:center;gap:10px;margin-bottom:20px;color:#be123c}
[data-theme="dark"] .trial-banner{background:rgba(190,18,60,.12)}
.trial-banner b{color:#e11d48}
.trial-banner .btn{margin-left:auto;background:var(--primary-grad)}

/* ---- Page head ---- */
.page-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;gap:12px;flex-wrap:wrap}
.page-head h2{font-size:24px;font-weight:700}
.page-head .muted{color:var(--muted);font-size:15px;margin-top:2px}
/* `class="muted"` appears 327 times in app.js but the ONLY rule for it was the
   one above, scoped to .page-head. Everywhere else — the card sub-labels like
   "tap to open employees" — it fell back to the browser's default <small> size
   (~11px) in plain dark text. Sized here so those grow with everything else,
   and coloured grey since 30 Jul: the owner approved recolouring all ~289
   labels so "muted" finally means what it says. The class is used ONLY inside
   the logged-in app (verified — no public page renders it), so the homepage
   and login screen are untouched. Inline styles and more specific rules still
   win, so anything deliberately coloured stays as it was. */
.muted{color:var(--muted)}
small.muted{font-size:13px}

/* ---- Cards & grid ---- */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:20px}
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;box-shadow:var(--shadow)}
.kpi-card{cursor:pointer;transition:transform .08s ease,box-shadow .12s ease,border-color .12s ease}
.kpi-card:hover{transform:translateY(-1px);box-shadow:0 6px 18px rgba(16,24,40,.12);border-color:var(--primary)}
.kpi-card.kpi-on{outline:2px solid var(--primary);outline-offset:-2px;border-color:var(--primary)}
tr.trow.clickable{cursor:pointer}
tr.trow.clickable:hover{background:color-mix(in srgb,var(--primary) 6%,transparent)}
.trow-arrow{color:var(--muted);font-weight:700;width:24px;text-align:center}
tr.trow-detail>td{background:var(--bg);border-top:none}
.stat{display:flex;align-items:flex-start;justify-content:space-between}
.stat .label{color:var(--muted);font-size:13px;font-weight:700;letter-spacing:.05em;text-transform:uppercase}
.stat .value{font-size:30px;font-weight:800;margin-top:8px}
.stat .ic{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:900px){.grid-2{grid-template-columns:1fr}}

/* ---- Tables ---- */
.panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
.panel-head{padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:10px}
.panel-head h3{font-size:15px}
.panel-head small{color:var(--muted)}
table{width:100%;border-collapse:collapse}
th{text-align:left;color:var(--muted);font-size:13px;letter-spacing:.05em;text-transform:uppercase;padding:14px 20px;font-weight:700;border-bottom:1px solid var(--border)}
td{padding:14px 20px;border-bottom:1px solid var(--border)}
tr:last-child td{border-bottom:none}
tbody tr:hover{background:var(--bg)}
.empty{text-align:center;color:var(--muted);padding:44px 20px}
.emp-cell{display:flex;align-items:center;gap:11px}
.emp-cell .avatar{width:36px;height:36px;font-size:13px}
.emp-cell small{color:var(--muted);display:block;font-size:14px}

/* ---- Badges ---- */
.badge{display:inline-flex;align-items:center;padding:4px 11px;border-radius:20px;font-size:12px;font-weight:600}
.badge.green{background:#ecfdf5;color:#059669}
.badge.amber{background:#fffbeb;color:#d97706}
.badge.red{background:#fef2f2;color:#dc2626}
.badge.blue{background:#eff6ff;color:#2563eb}
.badge.gray{background:#f1f5f9;color:#64748b}
[data-theme="dark"] .badge.green{background:rgba(5,150,105,.15)}
[data-theme="dark"] .badge.amber{background:rgba(217,119,6,.15)}
[data-theme="dark"] .badge.red{background:rgba(220,38,38,.15)}
[data-theme="dark"] .badge.blue{background:rgba(37,99,235,.15)}
[data-theme="dark"] .badge.gray{background:rgba(100,116,139,.15)}

/* ---- Toolbar / filters ---- */
.toolbar{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:16px;display:flex;gap:12px;flex-wrap:wrap;margin-bottom:18px;align-items:center;box-shadow:var(--shadow)}
.toolbar input,.toolbar select{padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text)}
.toolbar input{flex:1;min-width:180px}

/* ---- Modal ---- */
.modal-bg{position:fixed;inset:0;background:rgba(15,23,42,.5);display:flex;align-items:center;justify-content:center;z-index:50;padding:20px}
.modal{background:var(--surface);border-radius:20px;width:100%;max-width:720px;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,.3)}
.modal-head{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid var(--border)}
.modal-head h3{font-size:18px}
.modal-body{padding:24px}
.modal-foot{padding:16px 24px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:10px}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:600px){.form-grid{grid-template-columns:1fr}}
.form-grid .full{grid-column:1/-1}
.section-title{font-weight:700;font-size:15px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin:6px 0 2px;grid-column:1/-1}

/* ---- Charts (CSS bars) ---- */
.bar-row{display:flex;align-items:center;gap:12px;margin-bottom:12px}
.bar-row .bl{width:120px;font-size:15px;color:var(--muted)}
.bar-track{flex:1;height:10px;background:var(--bg);border-radius:6px;overflow:hidden}
.bar-fill{height:100%;background:var(--primary-grad);border-radius:6px}
.bar-row .bv{font-weight:700;width:40px;text-align:right}

/* ---- Org chart ---- */
.org-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px}
.org-card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px;text-align:center;box-shadow:var(--shadow)}
.org-card .avatar{margin:0 auto 10px;width:50px;height:50px}
.org-card b{display:block}
.org-card small{color:var(--muted)}

.toast{position:fixed;bottom:24px;right:24px;background:var(--text);color:var(--surface);padding:13px 18px;border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.3);z-index:100;font-weight:500;animation:slideup .25s}
@keyframes slideup{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}

@media(max-width:820px){
  .sidebar{position:fixed;left:0;top:0;z-index:40;width:280px;max-width:86vw;transform:translateX(-100%);transition:transform .22s ease;box-shadow:0 0 40px rgba(0,0,0,.28)}
  .sidebar.open{transform:translateX(0)}
  .sidebar-backdrop{display:block;position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:39;opacity:0;pointer-events:none;transition:opacity .2s}
  .sidebar-backdrop.show{opacity:1;pointer-events:auto}
  .hamburger{display:inline-flex;align-items:center;gap:7px;width:auto;padding:0 14px;height:38px;background:var(--primary-grad);color:#fff;border:none}
  .ham-label{display:inline;font-size:13px;font-weight:600}
  /* ---- The header, two rows instead of one overflowing row ----------
     Seven controls plus a title never fitted across 360px; they overlapped.
     Nothing is removed — the controls keep row one and the page title takes a
     row of its own underneath, which is also how the approved design reads.
     syncStickyOffsets() measures the real height, so everything sticky below
     follows automatically. */
  .topbar{padding:10px 12px;gap:8px;row-gap:8px;flex-wrap:wrap;align-items:center}
  .topbar h1#pageTitle{
    flex:1 1 100%;            /* its own row */
    order:9;                  /* always after the controls */
    font-size:21px;font-weight:700;letter-spacing:-.02em;
    white-space:normal;overflow:visible;line-height:1.2;margin:2px 0 0;
  }
  .topbar .icon-btn{width:36px;height:36px;flex:none;padding:0}
  .topbar .chip{min-height:36px;padding:0 9px;font-size:12px;gap:5px;flex:none}
  .topbar .chip small{font-size:11.5px}
  .topbar #themeBtn small{display:none}
  .topbar #locChip small{max-width:52px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .topbar #langSel{max-width:96px;min-height:36px}
  .topbar .hamburger{flex:none;padding:0 11px;height:36px;min-height:36px}
  .topbar #userBtn .avatar{width:26px !important;height:26px !important;font-size:10px !important}
  .chip .who{display:none}
  /* The page-head repeats the title the topbar now shows large. One is enough. */
  .content > .page-head > div > h2{font-size:20px}
  .content{padding:16px}
}

/* ---- Notifications ---- */
.notif-badge{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:var(--danger);color:#fff;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;border:2px solid var(--surface)}
.notif-panel{position:fixed;top:calc(var(--topbar-h,64px) + 6px);right:24px;width:360px;max-width:calc(100vw - 32px);max-height:70vh;overflow:hidden;background:var(--surface);border:1px solid var(--border);border-radius:16px;box-shadow:0 20px 50px rgba(0,0,0,.2);z-index:60;display:flex;flex-direction:column}
[dir="rtl"] .notif-panel{right:auto;left:24px}
.notif-head{padding:14px 18px;border-bottom:1px solid var(--border);font-weight:700;display:flex;align-items:center;gap:8px}
.notif-list{overflow-y:auto}
.notif-item{display:flex;gap:12px;padding:13px 18px;border-bottom:1px solid var(--border);cursor:pointer;align-items:flex-start}
.notif-item:hover{background:var(--bg)}
.notif-item:last-child{border-bottom:none}
.notif-item b{display:block;font-size:13px;font-weight:600;line-height:1.3}
.notif-item small{color:var(--muted);font-size:14px}
.notif-ic{width:34px;height:34px;border-radius:10px;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.notif-ic svg{width:17px;height:17px}
.notif-ic.danger{background:#fef2f2;color:#dc2626}
.notif-ic.warning{background:#fffbeb;color:#d97706}
.notif-ic.info{background:#eff6ff;color:#2563eb}
[data-theme="dark"] .notif-ic.danger{background:rgba(220,38,38,.15)}
[data-theme="dark"] .notif-ic.warning{background:rgba(217,119,6,.15)}
[data-theme="dark"] .notif-ic.info{background:rgba(37,99,235,.15)}

/* ---- Payslip ---- */
.pay-top{display:flex;justify-content:space-between;align-items:flex-start;padding-bottom:16px;border-bottom:2px solid var(--border);margin-bottom:16px}
.pay-table{width:100%}
.pay-table td{padding:10px 4px;border-bottom:1px solid var(--border)}
.pay-total td{border-top:2px solid var(--text);border-bottom:none;font-size:16px;padding-top:14px}

/* ---- Print ---- */
/* ==== Printing / Save as PDF ====
   Everything except the document being printed is removed from the layout, and
   every height / overflow limit is lifted so long reports flow across as many
   A4 pages as they need instead of being cut off at the window edge. */
@media print{
  html,body{background:#fff !important;margin:0 !important;padding:0 !important;
    height:auto !important;max-height:none !important;overflow:visible !important}
  body.printing > *{display:none !important}
  body.printing .print-target{display:block !important}

  .print-target{position:static !important;inset:auto !important;z-index:auto !important;
    background:#fff !important;padding:0 !important;margin:0 !important;
    width:auto !important;max-width:none !important;min-height:0 !important;
    height:auto !important;max-height:none !important;overflow:visible !important;
    align-items:flex-start !important;justify-content:flex-start !important;backdrop-filter:none !important}
  .print-target .modal{position:static !important;width:100% !important;max-width:none !important;min-width:0 !important;
    height:auto !important;max-height:none !important;overflow:visible !important;
    box-shadow:none !important;border:none !important;border-radius:0 !important;
    background:#fff !important;margin:0 !important;transform:none !important;animation:none !important}
  .print-target .modal-head,.print-target .modal-foot{display:none !important}
  .print-target .modal-body{max-height:none !important;height:auto !important;overflow:visible !important;padding:0 !important}

  /* Inner scroll panes must expand fully, or their content is lost. */
  .print-target .ts-scroll,.print-target .asg-list,.print-target .cal-scroll,
  .print-target .a4-report .modal-body,.print-target [style*="overflow"]{
    max-height:none !important;height:auto !important;overflow:visible !important}
  .print-target .ts-scroll,.print-target .ts-months{border:none !important}

  /* Controls are not part of a document. */
  .print-target button,.print-target .icon-btn,.print-target select,
  .print-target .ts-picker,.print-target .cal-pop{display:none !important}

  /* Tables: repeat headings on every page and never split a row in half. */
  .print-target table{width:100% !important;border-collapse:collapse !important;page-break-inside:auto}
  .print-target thead{display:table-header-group}
  .print-target tfoot{display:table-footer-group}
  .print-target tr{page-break-inside:avoid;break-inside:avoid}
  .print-target thead th{position:static !important}
  .print-target .a4-head{page-break-after:avoid}

  /* AUTOFIT PRINT — content is bounded by the page, never the other way round.
     Fixed table layout makes columns share the available width, and long words
     wrap instead of pushing the table off the edge of the paper. */
  .print-target,.print-target *{box-sizing:border-box !important;max-width:100% !important}
  .print-target table{table-layout:fixed !important;width:100% !important}
  .print-target th,.print-target td{
    overflow-wrap:anywhere !important;word-break:break-word !important;
    white-space:normal !important;text-overflow:clip !important}
  /* Images must not exceed the page, but their intended size is respected —
     forcing height:auto here blew the company logo up to full size. */
  .print-target img{max-width:100% !important}
  .print-target .af-logo{height:50px !important;max-width:180px !important;object-fit:contain !important}
  .print-target .a4-head img{height:44px !important;max-width:160px !important;object-fit:contain !important}
  .print-target .brand-logo{height:34px !important;width:auto !important}
  .print-target .a4-head{flex-wrap:wrap !important;gap:8px}
  .print-target .a4-head img{max-height:44px !important;width:auto !important}
  /* Nothing in a printed document may refuse to wrap. */
  .print-target *:not(pre):not(code){white-space:normal !important}
  .print-target pre,.print-target code{white-space:pre-wrap !important}

  /* Fallback page fit (portrait) if setPrintPage() has not run. The 1.5 cm side
     margins live in the padding so they hold even at @page margin 0. */
  .print-target .modal{box-sizing:border-box !important;width:210mm !important;max-width:210mm !important;
    padding:0 15mm !important;margin:0 !important}
  .print-target .modal-body,.print-target .a4-report,.print-target .af-doc{
    width:100% !important;max-width:100% !important;padding-left:0 !important;padding-right:0 !important}

  /* Keep the brand colours and status pills in the PDF. */
  .print-target *{-webkit-print-color-adjust:exact !important;print-color-adjust:exact !important}
}

/* ---- Sectioned employee form ---- */
.form-section{border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:16px;background:var(--bg)}
.form-section-head{display:flex;align-items:center;gap:10px;margin-bottom:12px;font-size:14px}
.form-section-head .req{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--danger);background:#fef2f2;padding:2px 8px;border-radius:20px}
[data-theme="dark"] .form-section-head .req{background:rgba(220,38,38,.15)}
.upload-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.upload-name{font-size:14px;color:var(--muted);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.form-section .field label{font-size:12px}

/* ---- A4 alert report ---- */
.a4-head{display:flex;justify-content:space-between;align-items:flex-start;padding-bottom:14px;border-bottom:2px solid var(--border);margin-bottom:8px}
.a4-report .pay-table th{font-size:13px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;text-align:left;padding:8px 6px;border-bottom:1px solid var(--border)}
select.chip{border:1px solid var(--border);border-radius:11px;padding:6px 10px;background:var(--surface);color:var(--text);font-size:12px}
@media print{ @page{ size:A4; margin:15mm; } }

/* ---- Chat ---- */
.chat-log{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px;min-height:180px}
.chat-msg{display:flex}
.chat-msg.admin{justify-content:flex-start}
.chat-msg.employee{justify-content:flex-end}
.chat-msg .bubble{max-width:75%;padding:9px 13px;border-radius:14px;font-size:14px;position:relative}
/* Numbered steps were arriving from the assistant correctly and then being
   rendered as one solid paragraph, because the bubble collapsed every newline.
   The answer was right; the display destroyed it. pre-wrap keeps the line
   breaks while still wrapping long lines. */
.chat-msg .bubble{white-space:pre-wrap;line-height:1.65}
/* A list of steps needs room. A 75% column turns "1. Open Payroll" into two
   lines and the numbering stops being scannable. */
.chat-msg.admin .bubble{max-width:92%}
.chat-msg.admin .bubble{background:var(--bg);border:1px solid var(--border);border-bottom-left-radius:4px}
.chat-msg.employee .bubble{background:var(--primary-grad);color:#fff;border-bottom-right-radius:4px}
.chat-msg .bubble small{display:block;font-size:10px;opacity:.7;margin-top:3px;text-align:right}
/* The line under Ameera's chat when she is continuing an earlier conversation.
   It exists so persistence is VISIBLE — memory that works invisibly is
   indistinguishable from memory that has quietly stopped working — and so the
   only control that erases stored employee data is always in reach. */
.ai-mem-bar{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:7px 12px;margin-top:8px;border-radius:10px;background:#f1f5f9;
  font-size:12.5px;color:var(--muted)}
.ai-mem-bar button{background:none;border:none;padding:0;font:inherit;font-weight:600;
  color:var(--danger);cursor:pointer;text-decoration:underline}
.chat-input{display:flex;gap:8px;padding:12px;border-top:1px solid var(--border)}
.chat-input input{flex:1;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text)}
.chat-thread.active{background:color-mix(in srgb,var(--primary) 10%,transparent)}

/* ---- Employee portal ---- */
.portal{max-width:640px;margin:0 auto;min-height:100vh;background:var(--bg)}
.portal-bar{display:flex;align-items:center;gap:10px;padding:14px 18px;background:var(--surface);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:10}
.portal-tabs{display:flex;gap:6px;overflow-x:auto;padding:10px 14px;background:var(--surface);border-bottom:1px solid var(--border);position:sticky;top:var(--topbar-h,64px);z-index:9}
.ptab{padding:8px 14px;border-radius:20px;font-weight:600;font-size:15px;color:var(--muted);white-space:nowrap;border:1px solid transparent}
.ptab.active{background:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary-ink)}
.portal-body{padding:16px}
.p-doc{display:flex;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid var(--border);font-size:14px}
.p-doc:last-child{border-bottom:none}
.p-doc span:first-child{color:var(--muted)}

/* ---- "After see message- Click here" — the broadcast acknowledgement ----
   Styled on the mobile Fix button (.e2-fix, §3.38): the same 34px row height,
   10px radius, 6px icon gap, 11.5px bold brand face and gold --primary-grad.

   DELIBERATELY NOT inside the ≤820px block, and deliberately NOT prefixed with
   body.soft. .e2-fix lives in the phone rail and exists only there; this button
   is on the employee portal, which the same people open on a laptop at the desk
   and on a phone on site, and it has to look like itself on both. The 340px cap
   is the only difference the two sizes need: on a phone the message card is
   narrower than that, so the button fills the row exactly as .e2-fix does, and
   on a desktop it stops short of an absurd full-width bar.

   Once pressed it can never come back (there is no un-read route), so the
   disabled state is not a greyed-out failure — it is the settled green "Seen"
   receipt, the same --ok wash .e2-pill.e2-ok uses. */
.pm-ackb{margin-top:8px;display:flex;gap:8px}
/* `.am-ack` is Ameera's reminder button. It is deliberately a SEPARATE class
   name carrying an identical look, not a second `.pm-ack`: the acknowledgement
   button on a message is what broadcast-test and announcement-test select with
   `.pm-ack`, and putting a second one of those on the same screen would have
   them pressing the reminder instead of the message. Same styling, different
   handle — the look is shared here so the two cannot drift apart visually. */
.pm-ack,.am-ack{flex:1;max-width:340px;min-height:34px;padding:0 14px;border:none;border-radius:10px;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;text-align:center;
  font:700 11.5px var(--font-brand),sans-serif;background:var(--primary-grad);color:#fff;transition:.15s}
.pm-ack svg,.am-ack svg{flex:none}
.pm-ack:disabled,.pm-ack.pm-seen,.am-ack:disabled{background:rgba(16,185,129,.13);color:var(--ok);cursor:default;box-shadow:none}

/* ---- DataSync brand ---- */
.mark svg{width:64%;height:64%;display:block}
.wordmark{font-weight:800;background:none!important;-webkit-text-fill-color:initial!important;color:var(--wordmark-1);letter-spacing:-.5px}
.wordmark span{color:var(--wordmark-2)}
.tagline{display:block;color:var(--muted);font-size:12px;letter-spacing:.22em;text-transform:uppercase;margin-top:3px}
[data-theme="dark"]{--wordmark-1:#C9CACB; --wordmark-2:#D9BA57}

/* ---- Uploaded brand logo ---- */
.brand-logo{display:block;height:auto;max-width:100%;object-fit:contain}
.brand-logo-lg{width:220px;margin:0 auto 6px}
.brand-logo-side{width:170px}
[data-theme="dark"] .brand-logo{background:#fff;border-radius:10px;padding:8px 12px}
.portal-bar .brand-logo{display:inline-block;vertical-align:middle}

/* ---- Tabs (sub-modules) ---- */
.tabbar{display:flex;gap:6px;margin-bottom:16px;border-bottom:1px solid var(--border);flex-wrap:wrap}
.tab{padding:9px 16px;font-weight:600;font-size:16px;color:var(--muted);border-bottom:2px solid transparent;margin-bottom:-1px}
.tab.active{color:var(--primary-ink);border-bottom-color:var(--primary)}
.tab:hover{color:var(--text)}

/* ---- 24/7 action cards ---- */
a.card:hover{border-color:var(--primary);box-shadow:0 4px 16px rgba(191,155,46,.12);transition:.15s}

/* ---- chat attach ---- */
.chat-input .btn.ghost{padding:8px 12px}

/* ---- Employee portal: left docked drawer ---- */
.portal-bar #pHam{margin-right:4px}
.portal-backdrop{position:fixed;inset:0;background:rgba(15,23,42,.5);z-index:39;opacity:0;pointer-events:none;transition:opacity .2s}
.portal-backdrop.show{opacity:1;pointer-events:auto}
.portal-drawer{position:fixed;left:0;top:0;height:100vh;width:264px;max-width:82vw;background:var(--surface);border-right:1px solid var(--border);z-index:40;transform:translateX(-100%);transition:transform .22s ease;box-shadow:0 0 40px rgba(0,0,0,.28);display:flex;flex-direction:column;overflow-y:auto}
.portal-drawer.open{transform:translateX(0)}
.portal-drawer-head{display:flex;align-items:center;justify-content:space-between;padding:16px 16px 8px}
.portal-nav{flex:1;padding:8px 10px;display:flex;flex-direction:column;gap:2px}
.pnav-item{display:flex;align-items:center;gap:12px;padding:11px 12px;border-radius:10px;color:var(--sidebar-text);font-weight:500;font-size:16px;text-align:left}
.pnav-item svg{width:18px;height:18px;flex-shrink:0}
.pnav-item:hover{background:var(--bg)}
.pnav-item.active{background:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary-ink);font-weight:600}
.portal-drawer-foot{display:flex;align-items:center;gap:10px;padding:14px 16px;border-top:1px solid var(--border)}
[dir="rtl"] .portal-drawer{left:auto;right:0;border-right:none;border-left:1px solid var(--border);transform:translateX(100%)}
[dir="rtl"] .portal-drawer.open{transform:translateX(0)}

/* ===== DataSync public landing / register ===== */
.ds-landing{min-height:100vh;background:linear-gradient(180deg,#f5f8ff 0%,#eef3fc 100%);color:#0f172a;overflow-y:auto}
.ds-nav{display:flex;align-items:center;justify-content:space-between;padding:18px 6vw;background:#fff;box-shadow:0 1px 0 rgba(15,23,42,.06);position:sticky;top:0;z-index:5;flex-wrap:wrap;gap:12px}
.ds-logo{height:40px;width:auto}
.ds-nav-cta{display:flex;gap:10px;flex-wrap:wrap}
.btn.ghost{background:transparent;color:var(--navy);border:1.5px solid #ddd8ca}
.btn.ghost:hover{background:#f6f2e8}
.btn.lg{padding:14px 26px;font-size:15px;border-radius:14px}
.btn.outline{background:#fff;color:var(--navy);border:1.5px solid var(--blue)}
.btn.outline:hover{background:#f6f2e8}
.ds-hero{max-width:900px;margin:0 auto;padding:70px 6vw 40px;text-align:center}
.ds-hero h1{font-size:clamp(30px,5vw,52px);line-height:1.1;font-weight:800;letter-spacing:-.02em;margin:0 0 18px}
.ds-hero h1 span{background:var(--primary-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.ds-hero p{font-size:clamp(15px,2vw,19px);color:#475569;max-width:660px;margin:0 auto 30px;line-height:1.6}
.ds-hero-cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
.ds-features{max-width:1080px;margin:20px auto;padding:20px 6vw 60px;display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:20px}
.ds-feat{background:#fff;border:1px solid #ece9e1;border-radius:18px;padding:26px 22px;box-shadow:0 6px 24px rgba(109,110,113,.05)}
.ds-feat-ic{font-size:30px;margin-bottom:12px}
.ds-feat h3{margin:0 0 8px;font-size:17px;color:var(--navy)}
.ds-feat p{margin:0;color:#5b6b85;font-size:14px;line-height:1.55}
.ds-foot{text-align:center;padding:26px 6vw;color:#64748b;font-size:13px;background:#fff;border-top:1px solid #eef2f9}
.ds-foot a{color:var(--blue)}
.ds-form-wrap{display:flex;justify-content:center;padding:40px 5vw 60px}
.ds-form-card{width:100%;max-width:540px;background:#fff;border:1px solid #ece9e1;border-radius:22px;padding:36px 32px;box-shadow:0 16px 50px rgba(109,110,113,.10)}
.ds-form-logo{text-align:center;margin-bottom:14px}
.ds-form-card h2{text-align:center;font-size:24px;margin:0 0 8px;color:var(--navy)}
.ds-form-lead{text-align:center;color:#5b6b85;font-size:14px;line-height:1.6;margin:0 0 22px}
.ds-form-card .field{margin-bottom:14px}
.ds-thanks{text-align:center;padding:52px 32px}
.ds-thanks-ic{font-size:56px;margin-bottom:10px}
.ds-thanks h2{color:var(--navy)}
.ds-thanks p{color:#475569;line-height:1.7;margin:6px 0 26px}
@media(max-width:640px){.ds-nav{padding:14px 5vw}.ds-hero{padding:44px 5vw 26px}.ds-form-card{padding:28px 20px}}

/* ===== DataSync v2 corporate landing ===== */
html{scroll-behavior:smooth}
.ds2{background:#fff;color:#0f172a}
.ds2 img{display:block;max-width:100%}
.ds2-logo{height:38px;width:auto}
.ds2-nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:18px;padding:14px 5vw;background:rgba(255,255,255,.92);backdrop-filter:blur(8px);box-shadow:0 1px 0 rgba(15,23,42,.07)}
.ds2-links{display:flex;gap:24px}
.ds2-links a{color:#334155;font-weight:500;font-size:14.5px}
.ds2-links a:hover{color:var(--blue)}
.ds2-cta{display:flex;gap:10px}
.btn.light{background:#fff;color:var(--navy)}
.btn.outline-light{background:transparent;color:#fff;border:1.5px solid rgba(255,255,255,.7)}
.btn.outline-light:hover{background:rgba(255,255,255,.12)}
.ds2-eyebrow{display:inline-block;font-size:12px;font-weight:700;letter-spacing:.12em;color:var(--blue);margin-bottom:12px}
.ds2-hero{display:grid;grid-template-columns:1.05fr .95fr;gap:48px;align-items:center;max-width:1180px;margin:0 auto;padding:64px 5vw 48px}
.ds2-hero h1{font-size:clamp(32px,4.6vw,54px);line-height:1.08;font-weight:800;letter-spacing:-.02em;margin:0 0 18px}
.ds2-hero h1 span{background:var(--primary-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.ds2-hero-txt p{font-size:17px;color:#4a5a72;line-height:1.65;margin:0 0 26px;max-width:540px}
.ds2-hero-cta{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:16px}
.ds2-hero-trust{color:#64748b;font-size:13.5px}
.ds2-hero-img{position:relative}
.ds2-hero-img>img{width:100%;border-radius:22px;box-shadow:0 26px 60px rgba(109,110,113,.18)}
.ds2-badge{position:absolute;left:-14px;bottom:26px;background:#fff;border-radius:14px;padding:12px 16px;box-shadow:0 12px 30px rgba(15,23,42,.14);display:flex;flex-direction:column}
.ds2-badge b{color:var(--navy);font-size:15px}.ds2-badge span{color:#64748b;font-size:12px}
.ds2-stats{max-width:1080px;margin:10px auto;padding:26px 5vw;display:grid;grid-template-columns:repeat(4,1fr);gap:18px;text-align:center}
.ds2-stats>div{padding:14px}
.ds2-stats b{display:block;font-size:30px;font-weight:800;background:var(--primary-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.ds2-stats span{color:#64748b;font-size:13.5px}
.ds2-head{max-width:660px;margin:0 auto 34px;text-align:center}
.ds2-head h2{font-size:clamp(26px,3.4vw,38px);font-weight:800;letter-spacing:-.02em;margin:0 0 10px;color:#0f172a}
.ds2-head p{color:#5b6b85;font-size:16px;line-height:1.6;margin:0}
.ds2-services,.ds2-how,.ds2-quotes{max-width:1160px;margin:0 auto;padding:66px 5vw}
.ds2-services{background:linear-gradient(180deg,#faf8f3,#fff)}
.ds2-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:22px}
.ds2-card{background:#fff;border:1px solid #ece9e1;border-radius:18px;padding:28px 24px;box-shadow:0 8px 26px rgba(109,110,113,.05);transition:.18s}
.ds2-card:hover{transform:translateY(-4px);box-shadow:0 18px 40px rgba(109,110,113,.12)}
.ds2-ic{width:52px;height:52px;border-radius:14px;background:linear-gradient(135deg,#f0ead9,#e5dbbf);display:flex;align-items:center;justify-content:center;font-size:26px;margin-bottom:16px}
.ds2-card h3{margin:0 0 8px;font-size:18px;color:var(--navy)}
.ds2-card p{margin:0;color:#5b6b85;font-size:14.5px;line-height:1.6}
.ds2-about{display:grid;grid-template-columns:1fr 1.05fr;gap:48px;align-items:center;max-width:1160px;margin:0 auto;padding:40px 5vw 70px}
.ds2-about-img img{width:100%;border-radius:22px;box-shadow:0 24px 54px rgba(109,110,113,.16)}
.ds2-about-txt h2{font-size:clamp(26px,3.2vw,36px);font-weight:800;letter-spacing:-.02em;margin:0 0 14px}
.ds2-about-txt p{color:#4a5a72;font-size:16px;line-height:1.65;margin:0 0 18px}
.ds2-checks{list-style:none;padding:0;margin:0 0 24px}
.ds2-checks li{position:relative;padding:8px 0 8px 32px;color:#334155;font-size:15px}
.ds2-checks li:before{content:"✓";position:absolute;left:0;top:7px;width:22px;height:22px;border-radius:50%;background:var(--primary-grad);color:#fff;font-size:13px;display:flex;align-items:center;justify-content:center}
.ds2-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:24px}
.ds2-step{text-align:center;padding:20px}
.ds2-num{width:56px;height:56px;margin:0 auto 16px;border-radius:50%;background:var(--primary-grad);color:#fff;font-size:24px;font-weight:800;display:flex;align-items:center;justify-content:center}
.ds2-step h3{margin:0 0 8px;color:var(--navy);font-size:18px}
.ds2-step p{margin:0;color:#5b6b85;font-size:14.5px;line-height:1.6}
.ds2-quotes{background:linear-gradient(180deg,#fff,#faf8f3)}
.ds2-quote{background:#fff;border:1px solid #ece9e1;border-radius:18px;padding:26px 24px;box-shadow:0 8px 26px rgba(109,110,113,.05)}
.ds2-quote p{color:#334155;font-size:15.5px;line-height:1.7;margin:0 0 18px}
.ds2-person{display:flex;align-items:center;gap:12px}
.ds2-person img{width:46px;height:46px;border-radius:50%;object-fit:cover}
.ds2-person b{display:block;color:var(--navy);font-size:14px}.ds2-person span{color:#64748b;font-size:12.5px}
.ds2-band{background:var(--primary-grad);color:#fff;text-align:center;padding:70px 5vw}
.ds2-band h2{font-size:clamp(26px,3.4vw,40px);font-weight:800;margin:0 0 12px}
.ds2-band p{max-width:560px;margin:0 auto 26px;font-size:16.5px;opacity:.92;line-height:1.6}
.ds2-band-cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
.ds2-foot{background:var(--brand-dark);color:#c7d2e6;padding:56px 5vw 22px}
.ds2-foot-cols{max-width:1120px;margin:0 auto;display:grid;grid-template-columns:1.6fr 1fr 1fr 1.2fr;gap:30px}
.ds2-foot h4{color:#fff;font-size:15px;margin:0 0 14px}
.ds2-foot a{display:block;color:#a9b8d4;font-size:14px;padding:5px 0}
.ds2-foot a:hover{color:#fff}
.ds2-foot-brand img{height:34px;filter:brightness(0) invert(1);margin-bottom:14px}
.ds2-foot-brand p{color:#9fb0cf;font-size:14px;line-height:1.6;max-width:280px}
.ds2-foot-brand span{display:block;margin-top:8px;color:#9fb0cf;font-size:13px}
.ds2-foot-bar{max-width:1120px;margin:34px auto 0;padding-top:18px;border-top:1px solid rgba(255,255,255,.1);color:#8ea1c4;font-size:13px;text-align:center}
@media(max-width:860px){.ds2-links{display:none}.ds2-hero,.ds2-about{grid-template-columns:1fr;gap:30px}.ds2-hero-img{order:-1}.ds2-stats{grid-template-columns:repeat(2,1fr)}.ds2-foot-cols{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.ds2-cta .btn{padding:9px 12px;font-size:13px}.ds2-foot-cols{grid-template-columns:1fr}}

/* ===== DataSync v3 — dropdown, pricing, reviews, recommended ===== */
.ds2-drop{position:relative}
.ds2-menu{position:absolute;top:130%;left:50%;transform:translateX(-50%);min-width:260px;background:#fff;border:1px solid #ece9e1;border-radius:14px;box-shadow:0 20px 46px rgba(15,23,42,.16);padding:8px;display:none;z-index:30}
.ds2-drop:hover .ds2-menu{display:block}
.ds2-menu a{display:block;padding:9px 12px;border-radius:9px;color:#334155;font-size:13.5px;font-weight:500}
.ds2-menu a:hover{background:#f6f2e8;color:var(--blue)}
.ds2-card{position:relative;cursor:default}
.ds2-card-link{display:inline-block;margin-top:12px;color:var(--blue);font-weight:600;font-size:13.5px;opacity:0;transform:translateY(4px);transition:.18s}
.ds2-card:hover .ds2-card-link{opacity:1;transform:none}
.ds2-grid.three{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.ds2-reco{background:var(--primary-grad);color:#fff;padding:64px 5vw;text-align:center}
.ds2-reco-in{max-width:760px;margin:0 auto}
.ds2-eyebrow.light{color:#e5dbbf}
.ds2-reco h2{font-size:clamp(24px,3.2vw,36px);font-weight:800;margin:0 0 14px}
.ds2-reco p{font-size:16.5px;line-height:1.65;opacity:.94;margin:0 0 24px}
.ds2-pricing{max-width:1160px;margin:0 auto;padding:70px 5vw}
.ds2-plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:24px;align-items:stretch}
.ds2-plan{position:relative;background:#fff;border:1.5px solid #ece9e1;border-radius:20px;padding:32px 26px;display:flex;flex-direction:column;box-shadow:0 8px 26px rgba(109,110,113,.05)}
.ds2-plan.featured{border-color:var(--blue);box-shadow:0 22px 50px rgba(191,155,46,.20);transform:translateY(-6px)}
.ds2-plan-tag{position:absolute;top:-13px;left:50%;transform:translateX(-50%);background:var(--primary-grad);color:#fff;font-size:12px;font-weight:700;letter-spacing:.04em;padding:6px 16px;border-radius:20px}
.ds2-plan h3{margin:0 0 4px;font-size:20px;color:var(--navy)}
.ds2-plan-sub{color:#64748b;font-size:13.5px;margin-bottom:16px}
.ds2-price{font-size:34px;font-weight:800;color:#0f172a;margin-bottom:18px}
.ds2-price small{font-size:15px;font-weight:500;color:#64748b}
.ds2-price .cust{background:var(--primary-grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.ds2-plan ul{list-style:none;padding:0;margin:0 0 22px;flex:1}
.ds2-plan li{position:relative;padding:8px 0 8px 26px;color:#334155;font-size:14px;border-bottom:1px solid #f1f5fb}
.ds2-plan li:before{content:"✓";position:absolute;left:0;top:8px;color:var(--blue);font-weight:800}
.ds2-plan .btn{font-size:13.5px}
.ds2-plan-note{text-align:center;color:#94a3b8;font-size:13px;margin-top:24px}
.ds2-reviews{max-width:1160px;margin:0 auto;padding:70px 5vw;background:linear-gradient(180deg,#faf8f3,#fff)}
.ds2-rating-line{color:#475569}.ds2-rating-line b{color:var(--navy)}
.ds2-review{background:#fff;border:1px solid #ece9e1;border-radius:16px;padding:24px 22px;box-shadow:0 8px 24px rgba(109,110,113,.05)}
.ds2-stars{color:#f5a623;font-size:16px;letter-spacing:2px;margin-bottom:10px}
.ds2-review p{color:#334155;font-size:14.5px;line-height:1.65;margin:0 0 16px}
.ds2-rev-by b{display:block;color:var(--navy);font-size:14px}.ds2-rev-by span{color:#64748b;font-size:12.5px}

/* ===== DataSync v4 — carousel, promo, mega-menu, offer, contact, ask widget ===== */
.ds2-carousel{position:relative;overflow:hidden;background:var(--brand-dark)}
.ds2-track{display:flex;transition:transform .6s cubic-bezier(.4,0,.2,1)}
.ds2-slide{min-width:100%;position:relative;height:clamp(360px,52vw,560px);overflow:hidden}
.ds2-slide>img{width:100%;height:100%;object-fit:cover;filter:brightness(.55)}
.ds2-slide-cap{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;gap:12px;max-width:760px;margin:0 auto;padding:0 6vw;text-align:center;align-items:center;color:#fff}

/* ===== Hero entrance ========================================================
   The picture drops in from above while the words rise from below, so the two
   meet in the middle. Each caption line follows the one before it rather than
   arriving together — a short stagger is what makes it read as deliberate
   rather than as a page still loading.

   Only the ACTIVE slide animates. The class is removed and re-added on every
   change so the sequence replays, and the slide is hidden from assistive tech
   while off-screen so a screen reader is not read three headlines at once. */
.ds2-slide>img{
  transform:translateY(-7%) scale(1.08);
  opacity:0;
  will-change:transform,opacity;
}
.ds2-slide.on>img{
  animation:heroDrop 1150ms cubic-bezier(.16,.84,.28,1) both;
}
@keyframes heroDrop{
  from{transform:translateY(-7%) scale(1.08);opacity:0}
  to  {transform:translateY(0)    scale(1);   opacity:1}
}

/* Each block starts below its resting place and lifts into it. */
.ds2-slide-cap>*{
  transform:translateY(38px);
  opacity:0;
  will-change:transform,opacity;
}
.ds2-slide.on .ds2-slide-cap>*{
  animation:heroRise 780ms cubic-bezier(.16,.84,.28,1) both;
}
/* The stagger. Deliberately small steps — anything longer and the last line
   arrives after the reader has already moved on. */
.ds2-slide.on .ds2-slide-cap>*:nth-child(1){animation-delay:220ms}
.ds2-slide.on .ds2-slide-cap>*:nth-child(2){animation-delay:330ms}
.ds2-slide.on .ds2-slide-cap>*:nth-child(3){animation-delay:440ms}
.ds2-slide.on .ds2-slide-cap>*:nth-child(4){animation-delay:550ms}
.ds2-slide.on .ds2-slide-cap>*:nth-child(n+5){animation-delay:660ms}
@keyframes heroRise{
  from{transform:translateY(38px);opacity:0}
  to  {transform:translateY(0);   opacity:1}
}

/* Motion of this size is exactly what a vestibular disorder reacts to. With
   reduced motion requested, everything simply appears — no movement at all. */
@media (prefers-reduced-motion:reduce){
  .ds2-slide>img,.ds2-slide-cap>*{transform:none;opacity:1}
  .ds2-slide.on>img,.ds2-slide.on .ds2-slide-cap>*{animation:none}
}
.ds2-slide-cap h2{font-size:clamp(24px,3.8vw,44px);font-weight:800;line-height:1.12;margin:0}
.ds2-slide-cap p{font-size:clamp(14px,1.8vw,18px);opacity:.94;max-width:620px;margin:0}
.ds2-arrow{position:absolute;top:50%;transform:translateY(-50%);width:46px;height:46px;border-radius:50%;background:rgba(255,255,255,.85);color:var(--navy);font-size:26px;font-weight:700;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 18px rgba(0,0,0,.2)}
.ds2-arrow.prev{left:18px}.ds2-arrow.next{right:18px}
.ds2-dots{position:absolute;bottom:18px;left:50%;transform:translateX(-50%);display:flex;gap:8px}
.ds2-dot{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.5);cursor:pointer}
.ds2-dot.on{background:#fff;width:26px;border-radius:6px}
.ds2-promo{background:linear-gradient(100deg,#ff7a18,#ffb038);color:#fff;padding:8px 5vw}
.ds2-promo-in{max-width:1080px;margin:0 auto;display:flex;align-items:center;gap:22px;padding:20px 0;flex-wrap:wrap;justify-content:center;text-align:center}
.ds2-promo-badge{background:var(--brand-dark);color:var(--primary);font-weight:800;font-size:12px;letter-spacing:.1em;padding:8px 14px;border-radius:8px;animation:dsPulse 1.6s infinite}
@keyframes dsPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.06)}}
.ds2-promo-txt h2{margin:0;font-size:clamp(20px,3vw,30px);font-weight:800}
.ds2-promo-txt h2 span{background:var(--brand-dark);padding:2px 10px;border-radius:8px;color:var(--primary)}
.ds2-promo-txt p{margin:4px 0 0;font-size:14px;opacity:.95}
.ds2-mega{min-width:560px;display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:16px}
.ds2-mega-col h5{margin:0 0 6px;color:var(--navy);font-size:13px;letter-spacing:.04em;text-transform:uppercase}
.ds2-mega-col a{padding:7px 8px;font-size:13px}
.ds2-offer{max-width:1120px;margin:0 auto;padding:66px 5vw}
.ds2-offer-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:22px}
.ds2-offer-card{background:#fff;border:1px solid #ece9e1;border-radius:18px;padding:26px 24px;box-shadow:0 8px 26px rgba(109,110,113,.05)}
.ds2-offer-card h3{margin:0 0 14px;color:var(--navy);font-size:18px;padding-bottom:10px;border-bottom:2px solid #f0ead9}
.ds2-offer-card ul{list-style:none;padding:0;margin:0}
.ds2-offer-card li{position:relative;padding:8px 0 8px 24px;color:#334155;font-size:14.5px}
.ds2-offer-card li:before{content:"›";position:absolute;left:4px;color:var(--blue);font-weight:800}
.ds2-grid.cards .ds2-card{padding:0;overflow:hidden}
.ds2-card-img{position:relative;height:158px}
.ds2-card-img img{width:100%;height:100%;object-fit:cover}
.ds2-card-ic{position:absolute;left:16px;bottom:-22px;width:48px;height:48px;border-radius:14px;background:#fff;box-shadow:0 8px 20px rgba(15,23,42,.14);display:flex;align-items:center;justify-content:center;font-size:24px}
.ds2-card-b{padding:32px 22px 24px}
.ds2-card-b h3{margin:0 0 8px;font-size:17px;color:var(--navy)}
.ds2-card-b p{margin:0;color:#5b6b85;font-size:14px;line-height:1.6}
.ds2-contact{max-width:1080px;margin:0 auto;padding:66px 5vw}
.ds2-contact-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:22px}
.ds2-contact-card{background:#fff;border:1px solid #ece9e1;border-radius:18px;padding:28px 24px;text-align:center;box-shadow:0 8px 26px rgba(109,110,113,.05)}
.ds2-ci{font-size:30px;margin-bottom:10px}
.ds2-contact-card h4{margin:0 0 8px;color:var(--navy);font-size:16px}
.ds2-contact-card p{margin:0;color:#475569;font-size:15px;line-height:1.6}
.ds2-contact-card a{color:var(--blue)}
/* Ask anything Free widget */
.ds2-ask{position:fixed;top:16px;right:16px;z-index:60}
.ds2-ask-btn{display:inline-flex;align-items:center;gap:8px;background:var(--primary-grad);color:#fff;font-weight:700;font-size:14px;padding:11px 18px;border-radius:30px;box-shadow:0 10px 26px rgba(191,155,46,.4);cursor:pointer}
.ds2-ask-dot{width:9px;height:9px;border-radius:50%;background:#43e08a;box-shadow:0 0 0 0 rgba(67,224,138,.6);animation:dsDot 1.6s infinite}
@keyframes dsDot{0%{box-shadow:0 0 0 0 rgba(67,224,138,.6)}70%{box-shadow:0 0 0 8px rgba(67,224,138,0)}100%{box-shadow:0 0 0 0 rgba(67,224,138,0)}}
.ds2-ask-panel{position:absolute;top:56px;right:0;width:min(360px,92vw);background:#fff;border-radius:18px;box-shadow:0 24px 60px rgba(15,23,42,.28);overflow:hidden;display:none;flex-direction:column}
.ds2-ask-panel.open{display:flex}
.ds2-ask-head{background:var(--primary-grad);color:#fff;padding:14px 16px;position:relative}
.ds2-ask-head b{display:block;font-size:15px}.ds2-ask-head span{font-size:12px;opacity:.9}
.ds2-ask-x{position:absolute;top:10px;right:12px;background:transparent;color:#fff;font-size:22px;line-height:1;cursor:pointer}
.ds2-ask-body{padding:14px;max-height:340px;overflow-y:auto;display:flex;flex-direction:column;gap:8px;background:#faf8f3}
.ds2-msg{padding:9px 13px;border-radius:14px;font-size:13.5px;line-height:1.5;max-width:85%}
.ds2-msg.bot{background:#fff;border:1px solid #ece9e1;color:#334155;align-self:flex-start;border-bottom-left-radius:4px}
.ds2-msg.me{background:var(--primary-grad);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}
.ds2-msg.typing{opacity:.6;font-size:20px;letter-spacing:2px;padding:4px 13px}
.ds2-ask-form{display:flex;gap:8px;padding:12px;border-top:1px solid #eef2f9;background:#fff}
.ds2-ask-form input{flex:1;padding:11px 12px;border:1px solid #e2e8f0;border-radius:12px;font-size:14px}
.ds2-ask-form input:focus{outline:none;border-color:var(--primary)}
.ds2-ask-form .btn{padding:11px 16px}
@media(max-width:640px){.ds2-mega{min-width:0;grid-template-columns:1fr}.ds2-ask-btn span:not(.ds2-ask-dot){display:inline}.ds2-promo-in{flex-direction:column}}

/* ===== DataSync v5 — promo navy, logo, ask bottom-right, detail pages ===== */
/* 1. Promo in logo navy (same as footer) */
.ds2-promo{background:var(--brand-dark)}
.ds2-promo-badge{background:var(--primary);color:var(--brand-dark)}
.ds2-promo-txt h2 span{background:var(--primary);color:var(--brand-dark)}
.ds2-promo .btn.light{background:var(--primary);color:var(--brand-dark)}
/* 4. Bigger header logo (two steps up) */
.ds2-nav .ds2-logo-lg{height:60px}   /* was 58 — he asked for 2 more */

.ds-form-logo .ds2-logo-lg{height:52px}
/* 3. Ask anything Free -> bottom-right floating */
.ds2-ask{top:auto;bottom:22px;right:22px}
.ds2-ask-panel{top:auto;bottom:64px}
/* 5. Detail pages */
.ds2-detailpage .ds2-nav{position:static}
.ds2-dhero{position:relative;height:clamp(300px,40vw,460px);background:var(--brand-dark);overflow:hidden}
.ds2-dhero>img{width:100%;height:100%;object-fit:cover;filter:brightness(.5)}
.ds2-dhero-cap{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;gap:12px;max-width:1080px;margin:0 auto;padding:0 5vw;color:#fff}
.ds2-dhero-cap h1{font-size:clamp(28px,4vw,48px);font-weight:800;margin:0;line-height:1.1}
.ds2-dhero-cap p{font-size:clamp(15px,2vw,19px);opacity:.95;max-width:620px;margin:0}
.ds2-dbody{max-width:1080px;margin:0 auto;padding:56px 5vw;display:grid;grid-template-columns:1.6fr .9fr;gap:40px;align-items:start}
.ds2-dmain h2{font-size:22px;color:var(--navy);margin:26px 0 12px}
.ds2-dmain h2:first-child{margin-top:0}
.ds2-dmain p{color:#4a5a72;font-size:16px;line-height:1.7;margin:0 0 8px}
.ds2-dgallery{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:22px}
.ds2-dgallery img{width:100%;height:200px;object-fit:cover;border-radius:16px;box-shadow:0 12px 30px rgba(109,110,113,.12)}
.ds2-dside{position:sticky;top:20px}
.ds2-dcard{background:#fff;border:1px solid #ece9e1;border-radius:20px;padding:28px 24px;box-shadow:0 16px 44px rgba(109,110,113,.12)}
.ds2-dcard h3{margin:0 0 8px;color:var(--navy);font-size:19px}
.ds2-dcard p{color:#5b6b85;font-size:14.5px;line-height:1.6;margin:0 0 18px}
.ds2-dphone{display:block;text-align:center;margin-top:14px;color:var(--navy);font-weight:700;font-size:15px}
@media(max-width:820px){.ds2-dbody{grid-template-columns:1fr}.ds2-dside{position:static}.ds2-nav .ds2-logo-lg{height:48px}}

/* Fix: mega-menu must be hidden until hover (its display:grid was overriding display:none) */
.ds2-menu.ds2-mega{display:none}
.ds2-drop.mega:hover .ds2-menu.ds2-mega{display:grid;grid-template-columns:repeat(3,1fr)}
@media(max-width:640px){.ds2-drop.mega:hover .ds2-menu.ds2-mega{grid-template-columns:1fr}}

/* ===== Subscription banners + blocked screen ===== */
.sub-banner{display:flex;align-items:center;gap:12px;padding:12px 16px;border-radius:12px;margin-bottom:16px;font-size:14px;flex-wrap:wrap}
.sub-banner span{flex:1;min-width:200px}
.sub-banner .btn.sm{padding:8px 14px;font-size:13px}
.sub-banner.general{background:#fff7ed;border:1px solid #fed7aa;color:#9a3412}
.sub-banner.critical{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}
.sub-banner.grace{background:#fee2e2;border:1px solid #f87171;color:#991b1b}
.sub-banner.critical .btn.sm,.sub-banner.grace .btn.sm{background:#dc2626;color:#fff}
.sub-blocked{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(180deg,#1b1020,#2a0e14);padding:24px}
.sub-blocked-card{max-width:520px;text-align:center;background:#fff;border-radius:22px;padding:44px 34px;box-shadow:0 30px 70px rgba(0,0,0,.4);border-top:6px solid #dc2626}
.sub-blocked-ic{font-size:54px;margin-bottom:8px}
.sub-blocked-card h1{color:#b91c1c;font-size:26px;margin:0 0 14px}
.sub-blocked-msg{color:#b91c1c;font-size:17px;font-weight:600;line-height:1.6;margin:0 0 20px}
.sub-blocked-contact{color:#475569;font-size:14px;margin-bottom:24px}
.sub-blocked-card .btn{background:#334155}

/* Super Admin impersonation bar + tenant tags */
.imp-bar{background:var(--primary-grad);color:#fff;padding:9px 16px;display:flex;align-items:center;gap:12px;font-size:13.5px}
.imp-bar .btn.sm{background:#fff;color:var(--navy);padding:6px 12px}
.row-actions .btn.sm{margin:2px 2px}
.tag{display:inline-block;padding:3px 9px;border-radius:20px;font-size:12px;font-weight:600}

/* Set-01/02: inline field errors + profile menu */
.field-err{color:#dc2626;font-size:12px;margin-top:4px}
.inp-err{border-color:#dc2626 !important;box-shadow:0 0 0 2px rgba(220,38,38,.12)}
/* Amber, not red: an ID number that doesn't match the country's usual format is a
   warning worth seeing, but it never blocks saving — real documents vary. */
.inp-warn{border-color:#d97706 !important;box-shadow:0 0 0 2px rgba(217,119,6,.12)}
.prof-menu{position:fixed;z-index:200;background:var(--card,#fff);border:1px solid var(--border,#e2e8f0);border-radius:12px;box-shadow:0 18px 46px rgba(15,23,42,.22);padding:6px;min-width:210px;display:flex;flex-direction:column}
.prof-menu button{text-align:left;padding:10px 12px;border-radius:9px;font-size:14px;color:var(--text,#0f172a);background:transparent;border:none;cursor:pointer}
.prof-menu button:hover{background:var(--bg,#f1f5f9)}

/* Step-01: per-file upload chips with green bar + delete */
.doc-list{display:flex;flex-direction:column;gap:6px;margin-top:2px;width:100%}
.doc-chip{display:flex;align-items:center;gap:8px;background:var(--bg,#f1f5f9);border:1px solid var(--border,#e2e8f0);border-radius:9px;padding:5px 9px;font-size:13px}
.doc-num{width:20px;height:20px;border-radius:50%;background:var(--primary-grad);color:#fff;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.doc-fn{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.doc-bar{width:56px;height:6px;border-radius:4px;background:#e2e8f0;overflow:hidden;flex:0 0 auto}
.doc-bar i{display:block;height:100%;width:100%;background:#22c55e}
.doc-rm{border:none;background:transparent;color:#dc2626;cursor:pointer;font-size:15px;line-height:1;flex:0 0 auto}

/* Inline add-new (+) beside Designation / Department selects */
.ref-add{display:flex;gap:6px;align-items:stretch}
.ref-add select{flex:1;min-width:0}
.ref-add-btn{flex:0 0 auto;padding:0 10px;display:inline-flex;align-items:center;justify-content:center}
.ref-add-btn svg{width:16px;height:16px}

/* Type-to-search permission picker (tenant create + user create) */
.perm-picker{border:1px solid var(--border,#e5e7eb);border-radius:10px;overflow:hidden}
.perm-search{display:flex;gap:8px;align-items:center;padding:8px;border-bottom:1px solid var(--border,#e5e7eb);background:color-mix(in srgb,var(--primary) 4%,transparent)}
.perm-search .perm-q{flex:1;padding:8px 10px;border:1px solid var(--border,#e5e7eb);border-radius:8px;font-size:13px}
.perm-all{display:flex;align-items:center;gap:5px;font-size:14px;white-space:nowrap;color:var(--muted);cursor:pointer}
.perm-list{max-height:260px;overflow:auto;padding:6px 10px}
.perm-gtitle{font-size:13px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.03em;margin:8px 0 4px}
.perm-item{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:6px;font-size:13px;cursor:pointer}
.perm-item:hover{background:color-mix(in srgb,var(--primary) 7%,transparent)}
.perm-item input{width:16px;height:16px}

/* Homepage: sticky nav on ALL pages + dark-blue selectable hovers + Beyond HR hover */
.ds2-detailpage .ds2-nav{position:sticky;top:0}
.ds2-links a{padding:6px 10px;border-radius:8px;transition:background .15s,color .15s}
/* Menu hover is the LOGO gold (owner, 2 Aug: "menu hover will be golden same
   as logo"). It was --navy, which the brand change repointed to grey, so the
   hover pill went grey. The text on it is --brand-dark, not white: white on
   #BF9B2E is 2.6:1 and fails to read. Dark on gold is 7.4:1 and keeps the
   background EXACTLY the gold he gave. */
.ds2-links a:hover{color:var(--brand-dark);background:var(--primary)}
.ds2-menu a:hover{background:var(--primary);color:var(--brand-dark)}
.ds2-offer-card li{cursor:pointer;border-radius:8px;transition:background .15s,color .15s;padding-right:8px}
/* The "What We Offer" list hovers with the same gold, at 12% so it reads as a
   tint rather than a filled pill. Its TEXT uses --primary-ink, the darkened
   gold, because plain #BF9B2E on white is 2.6:1 and cannot be read. */
.ds2-offer-card li:hover{background:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary-ink)}
.ds2-offer-card li:hover:before{color:var(--primary-ink)}

/* Force the selectable hover on dropdown + mega submenu items (beats earlier
   rules). Gold, same as the top-level menu — this !important rule is why the
   dropdown items stayed grey after the top-level ones were changed. */
.ds2 .ds2-menu a, .ds2 .ds2-mega-col a{cursor:pointer;transition:background .15s,color .15s}
.ds2 .ds2-menu a:hover,
.ds2 .ds2-mega-col a:hover,
.ds2 .ds2-menu.ds2-mega a:hover{background:var(--primary)!important;color:var(--brand-dark)!important;border-radius:8px}

/* Fix dropdown hover GAP so the submenu is reachable (menu was 10px below the link, breaking hover) */
.ds2-drop .ds2-menu{top:100%;margin-top:10px}
.ds2-drop .ds2-menu::before{content:"";position:absolute;top:-14px;left:0;right:0;height:16px;background:transparent}

/* ===== Responsive action buttons — wrap instead of clipping (super + tenant consoles, all tables) ===== */
.panel{overflow-x:auto}
td.row-actions, .row-actions{white-space:normal !important}
td[style*="nowrap"]{white-space:normal !important}
.row-actions .btn, td.row-actions .btn{margin:3px}
table td .btn.sm, table td .btn{margin:2px}
table td .icon-btn{margin:2px}
.modal{max-width:min(92vw,720px)}
.modal-foot{flex-wrap:wrap}
@media(max-width:1100px){ .content{padding:16px} }

/* Hold-to-record voice mic button */
.mic-btn{touch-action:none;user-select:none}
.mic-btn.recording{background:var(--danger)!important;color:#fff!important;border-color:var(--danger)!important;animation:micpulse 1s infinite}
@keyframes micpulse{0%,100%{transform:scale(1);box-shadow:0 0 0 0 rgba(239,68,68,.5)}50%{transform:scale(1.06);box-shadow:0 0 0 8px rgba(239,68,68,0)}}

/* Ask-anything-Free bot quick-reply chips */
.ds2-chips{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0}
.ds2-chip{background:#f4efe2;color:var(--primary-ink);border:1px solid #e3dcc8;border-radius:16px;padding:7px 12px;font-size:13px;cursor:pointer;font-weight:600;transition:background .15s,color .15s}
.ds2-chip:hover{background:var(--primary);color:var(--brand-dark)}

/* Sidebar menu type-to-search */
.nav-search{padding:8px 14px 4px}
.nav-search input{width:100%;padding:9px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:13px}
.nav-search input:focus{outline:none;border-color:var(--primary)}

/* Meeting "tap to join" prompt */
.meet-prompt{position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:300;width:min(460px,94vw)}
.meet-card{background:var(--surface,#fff);border:1px solid var(--border,#e2e8f0);border-radius:16px;box-shadow:0 18px 46px rgba(15,23,42,.28);padding:14px 16px;display:flex;align-items:center;gap:12px;animation:slideup .25s}
.meet-ic{width:44px;height:44px;border-radius:12px;background:color-mix(in srgb,var(--primary) 14%,transparent);display:flex;align-items:center;justify-content:center;font-size:22px;flex:0 0 auto}

/* Attendee hover tooltip (meetings) */
.att-tip{position:absolute;z-index:9999;min-width:180px;max-width:240px;max-height:260px;overflow:auto;background:var(--card,#fff);color:var(--text,#0f172a);border:1px solid var(--border,#e2e8f0);border-radius:10px;box-shadow:0 10px 30px rgba(16,24,40,.18);padding:10px 12px;font-size:13px;line-height:1.5}
.att-tip b{display:block;margin-bottom:6px;font-size:12px;color:var(--muted,#64748b)}
.att-tip div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---- Application form (leave / resignation) — sample pattern ---- */
.af-doc{background:#fff;color:#111827;font-size:12.5px;line-height:1.5;padding:2px;-webkit-print-color-adjust:exact;print-color-adjust:exact}
.af-topbar{height:9px;background:var(--primary);border-radius:3px;margin-bottom:14px}
.af-head{display:flex;align-items:center;justify-content:space-between;gap:16px;padding-bottom:10px;border-bottom:2px solid var(--primary)}
.af-logo{height:50px;max-width:180px;object-fit:contain}
.af-co{text-align:right}
.af-co b{font-size:15px;color:var(--primary-d)}
.af-co-sub{color:#667085;font-size:11.5px;margin-top:2px}
.af-titlerow{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin:16px 0 4px}
.af-title{font-size:20px;font-weight:800;color:var(--primary-d);letter-spacing:.3px}
.af-ref{text-align:right;color:#667085;font-size:11.5px;line-height:1.6;white-space:nowrap}
.af-sec{background:var(--primary-d);color:#fff;font-weight:700;font-size:12.5px;letter-spacing:.4px;padding:7px 12px;border-radius:3px;margin:14px 0 0}
.af-tbl{width:100%;border-collapse:collapse}
.af-tbl td{padding:7px 10px;border-bottom:1px solid #eef0f4;vertical-align:top}
.af-tbl td.af-l{width:34%;color:#667085;font-weight:600}
.af-tbl td.af-v{font-weight:700;color:#111827}
.af-para{margin:14px 2px;text-align:justify;color:#222}
.af-decl{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;margin:22px 2px 4px}
.af-decl-t{color:#475467;max-width:52%;font-size:11.5px}
.af-sign{text-align:center;min-width:240px}
.af-sign img{height:52px;display:block;margin:0 auto -2px}
.af-sign-l{border-top:1px solid #334155;padding-top:4px;font-size:11.5px;color:#667085}
.af-approve{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-top:10px}
.af-app{border:1px solid #e2e6ee;border-radius:6px;padding:10px 12px;background:#f8fafc}
.af-app b{color:var(--primary-d);font-size:12px;display:block;margin-bottom:6px}
.af-app-r{color:#475467;font-size:11.5px;margin-top:8px}
.af-box{display:inline-block;width:11px;height:11px;border:1.5px solid #475467;vertical-align:middle;margin:0 3px}
.af-note{color:#98a2b3;font-size:11px;margin-top:14px;border-top:1px solid #eef0f4;padding-top:8px}
.af-foot{display:flex;justify-content:space-between;color:#98a2b3;font-size:10.5px;margin-top:22px;border-top:1px solid #eef0f4;padding-top:8px}
@media print{ .a4-report .modal-head{display:none} .af-doc{padding:0} }

/* ---- Report auto-fit (A4 landscape) ---- */
.rep-fit td,.rep-fit th{word-break:break-word;padding:5px 7px}
.a4-report .a4-head{-webkit-print-color-adjust:exact;print-color-adjust:exact}

/* ---- Analytics ---- */
.an-row{display:flex;align-items:center;gap:12px;margin-bottom:12px}
.an-l{width:130px;font-size:15px;color:var(--muted);flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.an-track{flex:1;height:14px;background:color-mix(in srgb,var(--muted) 16%,transparent);border-radius:8px;overflow:hidden}
.an-fill{height:100%;border-radius:8px;transition:width .5s ease}
.an-v{width:44px;text-align:right;font-weight:700;font-size:13px}
.an-dot{display:inline-block;width:10px;height:10px;border-radius:3px;vertical-align:middle;margin-right:3px}

/* ---- Attendance calendar picker ---- */
.ac-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.ac-emp{display:flex;flex-direction:column;align-items:flex-start;gap:1px;padding:8px 12px;border:1px solid var(--border);border-radius:10px;background:var(--surface);cursor:pointer;text-align:left;transition:.15s;min-width:150px}
.ac-emp:hover{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 7%,transparent)}
.ac-emp b{font-size:13px}
.ac-emp small{color:var(--muted);font-size:13px}
/* ---- Calendar modal ---- */
.cal-ctrl{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.cal-legend{font-size:14px;color:var(--muted);display:inline-flex;align-items:center;gap:4px}
.cal-dot{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:5px;font-size:10px;font-weight:800;color:#fff;margin-left:6px}
.cal-dot.pres{background:#10b981}.cal-dot.abs{background:#ef4444}.cal-dot.lv{background:#f59e0b}.cal-dot.hol{background:#7c3aed}.cal-dot.off{background:#64748b}
.cal-summary{margin:6px 0 12px;display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
.cal-h{text-align:center;font-size:13px;font-weight:700;color:var(--muted);padding:4px 0}
.cal-cell{position:relative;aspect-ratio:1/1;border:1px solid var(--border);border-radius:9px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:13px;min-height:38px}
.cal-cell.empty{border:none;background:transparent}
.cal-cell.pres{background:color-mix(in srgb,#10b981 12%,transparent);border-color:color-mix(in srgb,#10b981 40%,transparent)}
.cal-cell.abs{background:color-mix(in srgb,#ef4444 12%,transparent);border-color:color-mix(in srgb,#ef4444 40%,transparent)}
.cal-cell.lv{background:color-mix(in srgb,#f59e0b 12%,transparent);border-color:color-mix(in srgb,#f59e0b 40%,transparent)}
.cal-cell.hol{background:color-mix(in srgb,#7c3aed 12%,transparent);border-color:color-mix(in srgb,#7c3aed 40%,transparent)}
.cal-cell.off{background:color-mix(in srgb,#64748b 10%,transparent);border-color:color-mix(in srgb,#64748b 34%,transparent)}
.cal-d{font-size:14px;color:var(--text)}
.cal-mk{font-size:11px;font-weight:800;line-height:1;margin-top:1px}
.cal-mk.pres{color:#10b981}.cal-mk.abs{color:#ef4444}.cal-mk.lv{color:#f59e0b}.cal-mk.hol{color:#7c3aed}.cal-mk.off{color:#64748b}
/* attendance report table headers */
.af-report-tbl th{background:var(--primary-d);color:#fff;font-size:11px;text-align:left;padding:7px 10px;letter-spacing:.3px}
.af-report-tbl td{padding:5px 10px}
@media (max-width:520px){ .cal-cell{min-height:32px;font-size:11px} .cal-modal{max-width:100%} }

/* ---- Attendance check-in status ---- */
.att-status{margin-top:10px;font-size:13px;min-height:18px}
.att-err{color:var(--danger);font-weight:600}
.att-ok{color:#10b981;font-weight:600}

/* ---- Profile inline attendance calendar (filled circles) ---- */
.pcal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin-top:10px}
.pcal-h{text-align:center;font-size:13px;font-weight:700;color:var(--muted);padding:2px 0}
.pcal-cell{position:relative;aspect-ratio:1/1;min-height:38px;display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:9px;border:1px solid var(--border)}
.pcal-cell.empty{border:none;background:transparent}
.pcal-cell.pres{background:color-mix(in srgb,#10b981 12%,transparent);border-color:color-mix(in srgb,#10b981 40%,transparent)}
.pcal-cell.abs{background:color-mix(in srgb,#ef4444 12%,transparent);border-color:color-mix(in srgb,#ef4444 40%,transparent)}
.pcal-cell.lv{background:color-mix(in srgb,#f59e0b 12%,transparent);border-color:color-mix(in srgb,#f59e0b 40%,transparent)}
.pcal-cell.hol{background:color-mix(in srgb,#7c3aed 12%,transparent);border-color:color-mix(in srgb,#7c3aed 40%,transparent)}
.pcal-cell.off{background:color-mix(in srgb,#64748b 10%,transparent);border-color:color-mix(in srgb,#64748b 34%,transparent)}
.pcal-d{font-size:15px;color:var(--text)}
.pcal-mk{font-size:11px;font-weight:800;line-height:1;margin-top:1px}
.pcal-mk.pres{color:#10b981}.pcal-mk.abs{color:#ef4444}.pcal-mk.lv{color:#f59e0b}.pcal-mk.hol{color:#7c3aed}.pcal-mk.off{color:#64748b}

/* ---- Click-to-mark popover on the attendance calendar ---- */
.cal-cell.clickable { cursor: pointer; transition: transform .08s ease, box-shadow .12s ease; }
.cal-cell.clickable:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(16,24,40,.14); outline: 2px solid var(--primary); outline-offset: -2px; }
.cal-cell.clickable:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.cal-cell.fut { opacity: .45; cursor: not-allowed; }

.cal-pop {
  position: absolute; z-index: 4000; width: 268px; max-width: calc(100vw - 16px);
  background: var(--card, #fff); color: var(--text); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: 0 18px 48px rgba(16,24,40,.22); overflow: hidden;
}
.cal-pop-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px; border-bottom: 1px solid var(--border); font-size: 13px; }
.cal-pop-body { padding: 12px; }
.cal-pop-opts { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.cal-opt { padding: 9px 8px; border-radius: 9px; border: 1px solid var(--border); background: transparent;
  color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; transition: background .12s, border-color .12s; }
.cal-opt:hover { background: rgba(191,155,46,.08); border-color: var(--primary); }
.cal-opt.pres.on { background: #10b981; border-color: #10b981; color: #fff; }
.cal-opt.abs.on  { background: var(--danger); border-color: var(--danger); color: #fff; }
.cal-opt.lv.on   { background: #f59e0b; border-color: #f59e0b; color: #fff; }
.cal-opt:disabled { opacity: .6; cursor: default; }
.cal-opt.log { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 40%, transparent); background: color-mix(in srgb, var(--primary) 8%, transparent); }
.cal-opt.log:hover { background: color-mix(in srgb, var(--primary) 16%, transparent); border-color: var(--primary); }
.cal-pop-times { display: flex; gap: 8px; margin-top: 10px; }
.cal-pop-times label { flex: 1; font-size:13px; color: var(--muted); display: flex; flex-direction: column; gap: 3px; }
.cal-pop-times input { padding: 7px 8px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg, transparent); color: var(--text); font-size: 13px; width: 100%; }
.cal-pop-err { margin-top: 9px; font-size: 12px; color: var(--danger); }
.cal-pop-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 12px; border-top: 1px solid var(--border); }
@media (max-width: 520px) { .cal-pop { width: calc(100vw - 24px); } }

/* ==== Compact auto-fitting attendance calendar (admin modal + employee view) ==== */
/* Smaller window, everything visible without scrolling, on every device. */
.modal.cal-modal{
  width:min(392px, calc(100vw - 24px)); max-width:min(392px, calc(100vw - 24px));
  border-radius:18px; overflow:hidden;
}
.cal-modal .modal-head.cal-head{
  background:var(--primary-grad, linear-gradient(135deg,#8A7021,#BF9B2E));
  color:#fff; padding:12px 14px; border-bottom:none;
}
.cal-modal .cal-head h3{color:#fff;font-size:15px;margin:0;line-height:1.25;font-weight:700}
.cal-modal .cal-head h3 small{display:block;font-weight:400;font-size:11px;opacity:.85;color:#fff}
.cal-modal .cal-head .icon-btn{color:#fff;background:rgba(255,255,255,.16);border:none;width:28px;height:28px;flex:0 0 auto}
.cal-modal .cal-head .icon-btn:hover{background:rgba(255,255,255,.28)}

.cal-modal .modal-body.cal-body{padding:12px 14px 14px;overflow:visible}
.cal-modal .cal-ctrl{display:flex;align-items:center;justify-content:center;gap:6px;margin:0 0 8px;flex-wrap:nowrap}
.cal-modal .cal-ctrl .chip{flex:1 1 0;min-width:0;text-align:center;font-size:13px;font-weight:600;padding:6px 8px}
.cal-nav{
  flex:0 0 auto;width:30px;height:30px;border-radius:9px;border:1px solid var(--border);
  background:transparent;color:var(--primary-ink);font-size:19px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;transition:background .12s;
}
.cal-nav:hover{background:color-mix(in srgb,var(--primary) 12%,transparent)}

.cal-modal .cal-legend{display:flex;align-items:center;justify-content:center;gap:3px;font-size:11px;margin-bottom:6px}
.cal-modal .cal-summary{margin:0 0 6px;justify-content:center;gap:5px}
.cal-modal .cal-summary .badge{font-size:11px;padding:2px 8px}
.cal-hint{font-size:12.5px;color:var(--muted);text-align:center;line-height:1.35;margin-bottom:8px}

/* The grid never overflows: seven equal columns that shrink to the window. */
.cal-modal .cal-grid{grid-template-columns:repeat(7,minmax(0,1fr));gap:4px;width:100%}
.cal-modal .cal-h{font-size:10px;padding:1px 0}
.cal-modal .cal-cell{min-height:0;border-radius:8px;font-size:12px;min-width:0}
.cal-modal .cal-d{font-size:13.5px;font-weight:600}
.cal-modal .cal-mk{font-size:9.5px;margin-top:0}
.cal-modal .modal-foot{padding:10px 14px}
.cal-modal .modal-foot .btn{font-size:13px;padding:8px 12px}

@media (max-width:400px){
  .cal-modal .cal-ctrl .chip{font-size:12px;padding:5px 4px}
  .cal-modal .cal-grid{gap:3px}
  .cal-modal .cal-d{font-size:13px}
  .cal-hint{font-size:10px}
}

/* Employee's own calendar — compact, and it must fit on a phone.
   These four rules used to sit outside the media query above, so the phone-only
   `min-height:0` applied at every width. With `aspect-ratio:1/1` each cell then
   grew as tall as the column was wide — around 85px on a handset — and six rows
   of that pushed the last week of the month below the fold. The cap keeps a
   whole month on one screen. */
.pcal-grid{grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;width:100%}
.pcal-cell{min-height:34px;max-height:54px}
@media (max-width:400px){
  .pcal-grid{gap:4px}
  .pcal-cell{min-height:30px;max-height:44px;min-width:0}
  .pcal-d{font-size:14px}
  .pcal-h{font-size:10px}
}

/* ---- Task "Assign to": search + tick-based employee picker ---- */
.asg{border:1px solid var(--border);border-radius:12px;overflow:hidden;background:var(--card,transparent)}
.asg-bar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--border);flex-wrap:wrap}
.asg-bar input{flex:1 1 180px;min-width:0;padding:8px 10px;border:1px solid var(--border);border-radius:9px;
  background:var(--bg,transparent);color:var(--text);font-size:13px}
.asg-chips{display:flex;flex-wrap:wrap;gap:5px;padding:8px 8px 0}
.asg-chips:empty{display:none}
.asg-chip{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;
  background:color-mix(in srgb,var(--primary) 12%,transparent);color:var(--primary-ink);
  border:1px solid color-mix(in srgb,var(--primary) 35%,transparent);border-radius:20px;padding:3px 6px 3px 10px}
.asg-chip button{border:none;background:transparent;color:inherit;cursor:pointer;font-size:12px;line-height:1;padding:0 2px}
.asg-list{max-height:210px;overflow-y:auto;padding:8px}
.asg-row{display:flex;align-items:center;gap:9px;padding:7px 8px;border-radius:9px;cursor:pointer;
  transition:background .12s}
.asg-row:hover{background:color-mix(in srgb,var(--primary) 7%,transparent)}
.asg-row.on{background:color-mix(in srgb,var(--primary) 12%,transparent)}
.asg-row input{width:16px;height:16px;flex:0 0 auto;cursor:pointer;accent-color:var(--primary)}
.asg-name{display:flex;flex-direction:column;min-width:0}
.asg-name b{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.asg-name small{font-size:13px;color:var(--muted)}
.asg-none{padding:14px;text-align:center;color:var(--muted);font-size:15px}
.asg-tag{display:inline-block;font-size:11px;font-weight:600;background:color-mix(in srgb,var(--primary) 10%,transparent);
  color:var(--primary-ink);border-radius:20px;padding:2px 8px;margin:1px 2px 1px 0;white-space:nowrap}
@media (max-width:520px){ .asg-list{max-height:170px} .asg-bar input{flex:1 1 100%} }

/* ---- Timesheet: clickable rows + history window ---- */
.ts-row{cursor:pointer;transition:background .12s}
.ts-row:hover{background:color-mix(in srgb,var(--primary) 8%,transparent)}
.ts-hist .ts-meta{display:flex;flex-wrap:wrap;gap:14px;font-size:14.5px;color:var(--muted);
  padding:10px 12px;border:1px solid var(--border);border-radius:10px;margin-bottom:10px}
.ts-hist .ts-meta b{color:var(--text);font-weight:600;margin-right:4px}
.ts-hist .ts-sum{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
.ts-hist .ts-scroll{max-height:52vh;overflow:auto;border:1px solid var(--border);border-radius:10px}
.ts-hist .ts-scroll table{width:100%;border-collapse:collapse}
.ts-hist .ts-scroll thead th{position:sticky;top:0;background:var(--surface,#fff);z-index:1;font-size:13px;
  text-transform:uppercase;letter-spacing:.03em;color:var(--muted);padding:8px 6px;border-bottom:1px solid var(--border)}
.ts-hist .ts-scroll td{padding:8px 6px;border-bottom:1px solid var(--border);font-size:12.5px}
@media (max-width:640px){ .ts-hist .ts-meta{gap:8px;font-size:11.5px} .ts-hist .ts-scroll{max-height:46vh} }

/* ---- Timesheet detail window: period picker + daily rows ---- */
.ts-picker{display:flex;align-items:center;gap:7px;flex-wrap:wrap;padding:9px 10px;border:1px solid var(--border);
  border-radius:11px;margin-bottom:10px;background:color-mix(in srgb,var(--primary) 4%,transparent)}
.ts-picker .chip{padding:6px 9px;font-size:12.5px}
.ts-sep{width:1px;height:20px;background:var(--border)}
.ts-range{display:flex;align-items:center;gap:5px;font-size:13.5px;color:var(--muted)}
.ts-range input{padding:5px 7px;border:1px solid var(--border);border-radius:8px;background:var(--surface,transparent);
  color:var(--text);font-size:12.5px}
.ts-pill{display:inline-block;font-size:11px;font-weight:700;border-radius:20px;padding:2px 9px;white-space:nowrap}
.ts-pill.present{background:color-mix(in srgb,#10b981 16%,transparent);color:#0b8f68}
.ts-pill.absent{background:color-mix(in srgb,#ef4444 16%,transparent);color:#c72d2d}
.ts-pill.leave{background:color-mix(in srgb,#f59e0b 18%,transparent);color:#a4690a}
.ts-pill.off,.ts-pill.holiday,.ts-pill.before{background:color-mix(in srgb,#94a3b8 20%,transparent);color:var(--muted)}
.tsd.off td,.tsd.holiday td,.tsd.before td{opacity:.72}
.tsd.half td{background:color-mix(in srgb,#f59e0b 9%,transparent)}
.ts-months{margin-top:12px;border:1px solid var(--border);border-radius:10px;padding:10px}
.ts-months table{width:100%;border-collapse:collapse;margin-top:6px}
.ts-months th{font-size:12.5px;text-transform:uppercase;color:var(--muted);text-align:left;padding:5px 6px;border-bottom:1px solid var(--border)}
.ts-months td{font-size:12px;padding:6px;border-bottom:1px solid var(--border)}
@media (max-width:640px){ .ts-picker{gap:6px} .ts-sep{display:none} .ts-range{flex:1 1 45%} }

/* ==== Public site: mobile drawer menu ==== */
/* Three-line button — only on the sizes where the desktop links are hidden. */
.ds2-burger{display:none;flex-direction:column;justify-content:center;gap:5px;width:42px;height:38px;
  padding:0 9px;border:1px solid rgba(15,23,42,.14);border-radius:10px;background:#fff;cursor:pointer;flex:0 0 auto}
.ds2-burger span{display:block;height:2px;width:100%;background:var(--navy,#6D6E71);border-radius:2px;
  transition:transform .2s ease,opacity .2s ease}
.ds2-burger.on span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.ds2-burger.on span:nth-child(2){opacity:0}
.ds2-burger.on span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

.pubm-backdrop{position:fixed;inset:0;background:rgba(15,23,42,.5);opacity:0;pointer-events:none;
  transition:opacity .22s ease;z-index:998}
.pubm-backdrop.on{opacity:1;pointer-events:auto}
.pubm{position:fixed;top:0;right:0;height:100dvh;width:min(86vw,340px);background:#fff;z-index:999;
  transform:translateX(102%);transition:transform .26s cubic-bezier(.4,0,.2,1);
  box-shadow:-14px 0 40px rgba(15,23,42,.22);display:flex;flex-direction:column;overflow-y:auto}
.pubm.open{transform:translateX(0)}
.pubm-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;
  border-bottom:1px solid rgba(15,23,42,.08);position:sticky;top:0;background:#fff;z-index:2}
.pubm-logo{height:34px;width:auto}
.pubm-nav{display:flex;flex-direction:column;padding:10px 8px}
.pubm-nav a{padding:12px 14px;border-radius:10px;color:#0f172a;font-weight:600;font-size:15px;cursor:pointer}
.pubm-nav a:hover{background:rgba(191,155,46,.08);color:var(--navy,#6D6E71)}
.pubm-sec{padding:6px 16px 10px;border-top:1px solid rgba(15,23,42,.07)}
.pubm-sec h4{font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;margin:12px 0 6px}
.pubm-group{margin-bottom:8px}
.pubm-group h5{font-size:12.5px;color:#334155;margin:8px 0 4px;font-weight:700}
.pubm-group a,.pubm-sec > .pubm-group a{display:block;padding:7px 10px;border-radius:8px;color:#475569;
  font-size:13.5px;cursor:pointer}
.pubm-group a:hover{background:rgba(191,155,46,.08);color:var(--navy,#6D6E71)}
.pubm-foot{margin-top:auto;display:flex;flex-direction:column;gap:9px;padding:14px 16px 22px;
  border-top:1px solid rgba(15,23,42,.08);background:#fafbfc}
.pubm-foot .btn{width:100%;justify-content:center}
.pubm-foot .chip{width:100%}

/* Compact sticky header for the login / register pages, which had none. */
.ds2-nav-mini{padding:10px 16px;gap:10px}
.ds2-nav-mini .ds2-logo-lg{height:40px}

/* The header stays visible on every public page, including detail pages. */
.ds2-nav{position:sticky !important;top:0;z-index:600}
.ds2-detailpage .ds2-nav{position:sticky !important;top:0}

@media(max-width:860px){
  .ds2-burger{display:flex}
  /* On phones the drawer carries these, so the header stays uncluttered. */
  .ds2-nav .ds2-cta > .btn,.ds2-nav .ds2-cta > select{display:none}
  .ds2-nav{padding:10px 14px;gap:8px}

  /* ---- Phone header: menu LEFT · logo CENTRE · Login RIGHT --------------
     Four different headers use .ds2-nav (landing, legal, detail, public nav)
     and every one of them nests the burger inside .ds2-cta. `display:contents`
     dissolves that wrapper so its children become flex items of the header
     itself — which is what lets the burger move to the far left with `order`
     WITHOUT editing four blocks of markup.

     The logo sits IN THE FLOW, immediately after the burger — his call, and the
     safer one. Pinning it to the exact centre put it on a collision course with
     the Login button: the two are independent, so any label longer than
     expected overlapped it. Left-aligned there is nothing to collide with, and
     `margin-right:auto` on the logo does all the work of pushing Login to the
     far edge. No absolute positioning, nothing to keep clear of.

     `content:url(...)` swaps the image on the same <img> element, so all five
     headers pick up the banner with no markup change. Where a browser does not
     support it the original mark simply stays — nothing breaks. */
  .ds2-nav{min-height:60px}                     /* same height as before */
  .ds2-cta{display:contents}
  .ds2-burger{order:-1;flex:0 0 auto}
  .ds2-nav .ds2-logo-lg{
    content:url('/img/logo-banner.png');
    order:0;height:34px;width:auto;object-fit:contain;
    /* The reserve covers the burger (42), the "Login My Company" button (~141
       at 13px), the page padding (28) and the gaps (16) — 227, rounded to 232
       for slack. On a 320px phone the banner shrinks rather than pushing the
       button off the edge; from 360px up it renders at its natural width. */
    max-width:calc(100vw - 232px);
    margin-right:auto;                          /* pushes Login to the edge */
  }
  /* ⛔ HOME1 — THE HOMEPAGE NAV WRITES ITS HEIGHT INLINE, AND INLINE WINS.
     His design canvas prints the logo as style="...height:46px..." on the
     element itself, so the 34px above never reached it and the banner would
     have rendered a third taller on the homepage than on every other public
     page. The banner rule was dead there entirely until 21 Aug, when the nav
     was given its .ds2-nav class — this is the other half of that fix. The
     !important is not decoration: nothing but !important outranks a style
     attribute. Scoped to [data-sec="nav"] so only the homepage nav is touched. */
  [data-sec="nav"].ds2-nav .ds2-logo-lg{height:34px !important}
  /* Matches navLogin / lLogin / dLogin. The drawer's own login is unaffected
     because this is scoped inside .ds2-nav. */
  .ds2-nav .ds2-cta > [id$="Login"]{
    display:inline-flex;order:9;flex:0 0 auto;white-space:nowrap;
    padding:8px 12px;font-size:13px;
  }
}
@media(min-width:861px){ .pubm,.pubm-backdrop{display:none} }


/* ============================================================
   SOFT EMBOSS (neumorphic) THEME
   Scoped to body.soft — applied only after sign-in, so the public
   marketing site keeps its own look. Shadows are tinted with each
   company's brand colour and every value has a dark counterpart.
   ============================================================ */
body.soft{
  --bg:#eceef6;                       /* soft light grey page, as in the reference */
  --surface:#eceef6;                  /* blocks share the page colour; the shadow makes the shape */
  --nm-light:rgba(255,255,255,.95);
  /* ⛔ 17 Aug 2026 — PINNED TO A LITERAL BLUE. DO NOT DERIVE THIS FROM THE BRAND.
     This used to be color-mix(in srgb, var(--primary) 20%, #a7b0c6). Nobody ever
     edited the shadow — but --primary was rebranded from BLUE #2E7DF0 to GOLD
     #BF9B2E, and the shadow silently followed it:
         blue  #2E7DF0 20% + #a7b0c6  ->  #8FA6CE   cool, and deep enough to read
         gold  #BF9B2E 20% + #a7b0c6  ->  #ACACA8   flat warm grey, washed out
     The owner spotted it as "the shadow is not like the first theme" and was
     right. It is now a fixed value, so the NEXT rebrand cannot drag it again.
     Measured against his own screenshot: this recipe renders the card edge at
     #A9BAD9 and his sample reads #A8BAD9 — one unit apart on one channel.

     Pinning it also FIXED A SECOND BUG. The old code needed color-mix(), so
     Android WebViews without it fell back to #b3bcd2 — a different shadow from
     everyone else's, on exactly the cheap phones the workers use. A literal hex
     needs no fallback, so every device now renders the identical emboss. */
  --nm-dark:#8FA6CE;
  /* THE LIGHT SOURCE, as a number. +1 = lit from the top-left. In right-to-left
     languages the layout mirrors but the shadows do not, so every card ends up
     lit from the wrong side — the one thing that makes a mirrored neumorphic UI
     look broken without anyone being able to say why. Flipped below. */
  --nm-x:1;
  /* ⛔ 17 Aug 2026 — ONE SHADOW STRENGTH FOR THE WHOLE SYSTEM (his choice, "no 3").
     This was 6px 6px 14px — more than twice the depth used on tiles and buttons,
     which is what made cards and panels read as too heavy next to them. He was
     shown six numbered samples and chose the TILE strength for everything, so
     --nm-out is now identical to --nm-out-sm by design, not by accident.
     Both are kept as separate names because 48 rules use one and 7 use the
     other, and collapsing them would be a rename with no benefit. */
  --nm-out:calc(3px * var(--nm-x)) 3px 8px var(--nm-dark), calc(-3px * var(--nm-x)) -3px 8px var(--nm-light);
  --nm-out-sm:calc(3px * var(--nm-x)) 3px 8px var(--nm-dark), calc(-3px * var(--nm-x)) -3px 8px var(--nm-light);
  --nm-in:inset calc(3px * var(--nm-x)) 3px 7px var(--nm-dark), inset calc(-3px * var(--nm-x)) -3px 7px var(--nm-light);
  --border:rgba(150,162,190,.26);
  --sidebar:#eceef6;
  --radius:18px;
  background:var(--bg);
}
/* ⛔ THE color-mix() OVERRIDE THAT USED TO SIT HERE WAS REMOVED, 17 Aug 2026.
   It read: body.soft{ --nm-dark:color-mix(in srgb, var(--primary) 20%, #a7b0c6) }
   That single line is what tied the emboss to the brand colour and turned the
   shadow grey when the brand went from blue to gold. --nm-dark is now a literal
   above. Do not reintroduce a brand-derived shadow here — if a future theme
   needs a different emboss, give it its own fixed hex. */
[dir="rtl"] body.soft{ --nm-x:-1 }
[data-theme="dark"] body.soft, body.soft[data-theme="dark"]{ }
[data-theme="dark"] body.soft{
  --bg:#232838; --surface:#232838; --sidebar:#232838;
  --nm-light:rgba(255,255,255,.055);
  --nm-dark:rgba(0,0,0,.55);
  --border:rgba(255,255,255,.07);
}

/* ---- Blocks ---- */
body.soft .card,body.soft .panel,body.soft .modal,body.soft .stat,
body.soft .form-section,body.soft .ts-months,body.soft .asg,body.soft .cal-pop{
  background:var(--surface) !important;
  border:none !important;
  border-radius:var(--radius) !important;
  box-shadow:var(--nm-out) !important;
}
body.soft .panel-head,body.soft .modal-head,body.soft .modal-foot{
  border-color:var(--border) !important;
}

/* ---- Buttons: raised, pressed on tap ---- */
body.soft .btn,body.soft .chip,body.soft .icon-btn,body.soft .tab,
body.soft .cal-nav,body.soft .pnav-item,body.soft .cal-opt{
  border:none !important;
  border-radius:12px !important;
  box-shadow:var(--nm-out-sm) !important;
  transition:box-shadow .16s ease, transform .12s ease, filter .15s ease !important;
}
body.soft .btn:active,body.soft .chip:active,body.soft .icon-btn:active,
body.soft .tab:active,body.soft .cal-nav:active,body.soft .cal-opt:active{
  box-shadow:var(--nm-in) !important;
  transform:translateY(1px);
}
body.soft .btn.ghost,body.soft .chip,body.soft .icon-btn,body.soft .tab,body.soft .cal-nav{
  background:var(--surface) !important;color:var(--text) !important;
}
/* WHITE ICON ON A WHITE BUTTON (owner, 5 Aug: "everywhere you made it white
   which is not visible").
   `.btn svg,.btn.ghost svg{color:#fff}` at ~2174 is correct for the DEFAULT
   look, where a ghost button is the gold gradient with white text. But the
   line directly above repaints every ghost button, chip, icon-btn and tab in
   the SOFT theme — which is the whole logged-in app — as a LIGHT surface with
   dark text. It changes the BUTTON's colour and never the icon's, so the icon
   stayed #fff and vanished.
   This was invisible until 5 Aug because those buttons carried EMOJI, and an
   emoji ignores `color`. The moment they became real line icons the bug
   appeared everywhere at once. The emoji were hiding it.
   Specificity here is (0,3,1) against that rule's (0,2,1), so it wins on merit
   and needs no !important — and the default (non-soft) theme is untouched. */
body.soft .btn.ghost svg, body.soft .chip svg,
body.soft .icon-btn svg, body.soft .tab svg, body.soft .cal-nav svg{
  color:inherit; stroke:currentColor;
}
body.soft .btn.ghost svg *, body.soft .chip svg *,
body.soft .icon-btn svg *, body.soft .tab svg *{ stroke:currentColor; }
/* The exceptions keep their own colour: a Delete icon must stay red, and a
   ghost button carrying an inline danger/warn colour already sets `color`,
   which `inherit` above now correctly follows. */
body.soft .tab.active{box-shadow:var(--nm-in) !important;color:var(--primary-ink) !important;font-weight:700}
body.soft .pnav-item.active{box-shadow:var(--nm-in) !important}

/* ---- Inputs and dropdowns: carved in ---- */
body.soft .field input,body.soft .field select,body.soft .field textarea,
body.soft input[type="text"],body.soft input[type="number"],body.soft input[type="date"],
body.soft input[type="time"],body.soft input[type="month"],body.soft input[type="password"],
body.soft input[type="email"],body.soft input[type="search"],
body.soft select,body.soft textarea,body.soft .asg-bar input,
body.soft .ts-range input,body.soft .cal-pop-times input{
  background:var(--surface) !important;
  border:none !important;
  border-radius:12px !important;
  box-shadow:var(--nm-in) !important;
  color:var(--text) !important;
}
body.soft input:focus,body.soft select:focus,body.soft textarea:focus{
  outline:none !important;
  box-shadow:var(--nm-in), 0 0 0 2px color-mix(in srgb,var(--primary) 45%,transparent) !important;
}

/* ---- Calendar cells ---- */
body.soft .cal-cell,body.soft .pcal-cell{
  background:var(--surface) !important;border:none !important;box-shadow:var(--nm-out-sm) !important;
}
body.soft .cal-cell.empty,body.soft .pcal-cell.empty{box-shadow:none !important;background:transparent !important}
body.soft .cal-cell.pres,body.soft .cal-cell.abs,body.soft .cal-cell.lv,
body.soft .pcal-cell.pres,body.soft .pcal-cell.abs,body.soft .pcal-cell.lv{box-shadow:var(--nm-in) !important}
body.soft .cal-cell.clickable:hover{box-shadow:var(--nm-out) !important;transform:translateY(-1px);outline:none !important}

/* ---- Softer table lines ---- */
body.soft table th{border-color:var(--border) !important}
body.soft table td{border-color:var(--border) !important}

/* ============ Phase 2: thinner blocks, single-line rows ============ */
body.soft .card,body.soft .stat{padding:13px 15px !important}
body.soft .panel-head{padding:11px 15px !important}
body.soft .modal-body{padding:16px 18px !important}
body.soft .modal-foot{padding:12px 16px !important}
body.soft .btn{padding:9px 15px !important}
body.soft .btn.sm{padding:6px 11px !important}
body.soft .field{margin-bottom:12px !important}
body.soft .field input,body.soft .field select,body.soft .field textarea{padding:10px 12px !important}

/* One line per row; long values shorten with an ellipsis instead of wrapping. */
body.soft table th,body.soft table td{
  padding:8px 10px !important;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:260px;
}
/* Documents and reports still wrap where wrapping is the point. */
body.soft .af-tbl td,body.soft .af-tbl th,body.soft .pay-table td,body.soft .pay-table th,
body.soft td.wrap,body.soft th.wrap{white-space:normal;max-width:none;overflow:visible}

/* Text fits its block or button rather than spilling out. */
body.soft .btn,body.soft .chip,body.soft .tab{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body.soft .card b,body.soft .stat b{overflow-wrap:anywhere}

/* Paper never gets the emboss. */
@media print{ .print-target,.print-target *{box-shadow:none !important} }

/* ============================================================
   SOFT EMBOSS on the public site — hybrid, deliberately partial.
   The calm content sections get the embossed look so the site and
   the app feel like one product. The hero photography, the dark
   promo band and the primary call-to-action stay bold and high
   contrast, because emboss cannot render over images and would
   weaken the buttons that drive sign-ups.
   ============================================================ */
.ds2{
  --nm-light:rgba(255,255,255,.95);
  /* Same pinning as body.soft, and for the same reason — var(--blue) was ALSO
     rebranded to the gold, so the public site's emboss had drifted grey too.
     A visitor must not meet one shadow on hrdatasync.com and a different one
     the moment he signs in. Kept byte-identical to the app's value. */
  --nm-dark:#8FA6CE;
  --nm-out:3px 3px 8px var(--nm-dark), -3px -3px 8px var(--nm-light);
  --nm-out-sm:3px 3px 8px var(--nm-dark), -3px -3px 8px var(--nm-light);
  --nm-in:inset 3px 3px 7px var(--nm-dark), inset -3px -3px 7px var(--nm-light);
  background:#eceef6;                     /* the soft grey, matching the app */
}
/* Neutral sections adopt the soft page colour so the emboss reads. */
.ds2-features,.ds2-offer,.ds2-contact,.ds2-how,.ds2-about,.ds2-stats{background:#eceef6}
.ds2-reviews{background:linear-gradient(180deg,#e7eaf4,#eceef6)}

/* Content cards: embossed. */
.ds2-card,.ds2-offer-card,.ds2-review,.ds2-contact-card{
  background:#eceef6 !important;
  border:none !important;
  box-shadow:var(--nm-out) !important;
}
.ds2-card:hover,.ds2-offer-card:hover,.ds2-contact-card:hover{
  box-shadow:8px 8px 18px var(--nm-dark), -8px -8px 18px var(--nm-light) !important;
  transform:translateY(-2px);
}

/* Secondary controls: raised, and pressed when tapped. */
.ds2-nav,.ds2-burger,.ds2-nav .btn.ghost,.ds2-arrow,.ds2-chip{
  border:none !important;
  box-shadow:var(--nm-out-sm) !important;
}
.ds2-nav{background:#eceef6 !important;backdrop-filter:none !important}
.ds2-burger{background:#eceef6 !important}
.ds2-burger:active,.ds2-nav .btn.ghost:active,.ds2-arrow:active{box-shadow:var(--nm-in) !important;transform:translateY(1px)}

/* Public form fields: carved in, same as the app. */
.ds2 input,.ds2 select,.ds2 textarea,.ds2-contact input,.ds2-contact textarea{
  background:#eceef6 !important;border:none !important;border-radius:12px !important;
  box-shadow:var(--nm-in) !important;
}
.ds2 input:focus,.ds2 select:focus,.ds2 textarea:focus{
  outline:none !important;
  box-shadow:var(--nm-in), 0 0 0 2px color-mix(in srgb,var(--blue,#BF9B2E) 45%,transparent) !important;
}

/* The mobile drawer matches the app's blocks. */
.pubm{background:#eceef6 !important;box-shadow:-14px 0 40px rgba(15,23,42,.22) !important}
.pubm-head,.pubm-foot{background:#eceef6 !important}

/* DELIBERATELY LEFT BOLD — these sell the product:
   hero carousel (photography), the dark promo band, and the primary CTA. */
.ds2-carousel,.ds2-slide,.ds2-promo{box-shadow:none !important}
.ds2 .btn:not(.ghost){
  background:var(--primary-grad) !important;
  color:#fff !important;
  box-shadow:0 8px 20px color-mix(in srgb,var(--blue,#BF9B2E) 38%,transparent) !important;
}
.ds2 .btn:not(.ghost):active{transform:translateY(1px)}
.ds2 .btn.lg.light{background:#fff !important;color:var(--navy,#6D6E71) !important}

/* ============================================================================
   HOME1 — SUPPORT FOR HIS DESIGN CANVAS (21 Aug 2026)
   ----------------------------------------------------------------------------
   ⛔ THIS IS DELIBERATELY SMALL. An earlier version of this layer was 894 lines
   of my own .ds2-* rules, written by reading his design and re-typing it as
   CSS. He rejected that and he was right: the page now renders HIS markup, with
   HIS inline styles, so 144 of those rules were describing something that no
   longer exists. What is left is only what his markup CANNOT carry inline:
     · @keyframes, which an inline style attribute cannot declare
     · :hover and :active, for the same reason - his design tool wrote them as
       style-hover attributes, which no browser has ever understood
     · which of the ten hero scenes is showing
     · what an empty picture slot looks like before he fills it in the CMS
   Everything else - every colour, gap, radius and shadow - is his, inline, and
   is not repeated here. If a value needs changing it changes in his design
   file, and HOME-PORT/build-homeV2.cjs regenerates the page.
   ========================================================================== */

/* His twenty-five animations, copied value for value. The timings ARE the
   design: v-drag holds at 55% then vanishes at 70% so the folder reads as
   dropped rather than bounced. */
@keyframes v-glow{0%{opacity:.4;transform:scale(1)}50%{opacity:.85;transform:scale(1.06)}100%{opacity:.4;transform:scale(1)}}
@keyframes v-pulse{0%{opacity:.35}50%{opacity:1}100%{opacity:.35}}
@keyframes v-float{0%{transform:translateY(0)}50%{transform:translateY(-12px)}100%{transform:translateY(0)}}
@keyframes v-cube{0%{transform:rotateX(-20deg) rotateY(0)}100%{transform:rotateX(-20deg) rotateY(360deg)}}
@keyframes v-ring{0%{transform:rotate(0);opacity:.25}50%{opacity:.65}100%{transform:rotate(360deg);opacity:.25}}
@keyframes v-shimmer{0%{background-position:-250% 0}100%{background-position:250% 0}}
@keyframes v-sweep{0%{transform:translateX(-130%)}100%{transform:translateX(240%)}}
@keyframes v-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
@keyframes v-pricepop{0%{transform:scale(1)}50%{transform:scale(1.045)}100%{transform:scale(1)}}
@keyframes v-orb{0%{transform:translate(0,0)}50%{transform:translate(28px,-22px)}100%{transform:translate(0,0)}}
@keyframes v-radar{0%{transform:scale(.55);opacity:.85}100%{transform:scale(1.6);opacity:0}}
@keyframes v-drag{0%{transform:translate(0,0) rotate(-4deg)}45%{transform:translate(118px,54px) rotate(3deg)}55%{transform:translate(118px,54px) rotate(3deg);opacity:.9}70%{opacity:0}71%{transform:translate(0,0) rotate(-4deg);opacity:0}85%{opacity:1}100%{transform:translate(0,0) rotate(-4deg)}}
@keyframes v-barloop{0%{transform:scaleY(.45)}50%{transform:scaleY(1)}100%{transform:scaleY(.45)}}
@keyframes v-fill{0%{width:6%}100%{width:96%}}
@keyframes v-pin{0%{transform:translate(0,0)}35%{transform:translate(86px,34px)}70%{transform:translate(150px,-24px)}100%{transform:translate(0,0)}}
@keyframes v-docout{0%{transform:translate(0,0) scale(.9) rotate(-6deg);opacity:0}18%{opacity:1}70%{transform:translate(96px,-58px) scale(1) rotate(4deg);opacity:1}100%{transform:translate(150px,-88px) scale(1.02) rotate(8deg);opacity:0}}
@keyframes v-dial{0%{transform:rotate(0)}100%{transform:rotate(300deg)}}
@keyframes v-stack{0%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-14px) rotate(2deg)}100%{transform:translateY(0) rotate(-3deg)}}
@keyframes v-tickin{0%{transform:scale(.6);opacity:0}40%{transform:scale(1);opacity:1}100%{transform:scale(1);opacity:1}}
@keyframes v-msgin{0%{transform:translateY(10px);opacity:0}30%{transform:translateY(0);opacity:1}92%{opacity:1}100%{opacity:.25}}
@keyframes v-dot{0%{opacity:.2;transform:scale(.85)}50%{opacity:1;transform:scale(1)}100%{opacity:.2;transform:scale(.85)}}
@keyframes v-reveal{0%{clip-path:inset(0 100% 0 0)}18%{clip-path:inset(0 0 0 0)}100%{clip-path:inset(0 0 0 0)}}
@keyframes v-blockpass{0%{transform:translateX(-100%)}18%{transform:translateX(100%)}100%{transform:translateX(100%)}}
@keyframes v-badge{0%{box-shadow:6px 6px 16px color-mix(in srgb,#2E7DF0 20%,#a7b0c6),-4px -4px 12px rgba(255,255,255,.9),0 0 0 0 rgba(191,155,46,.4)}50%{box-shadow:6px 6px 16px color-mix(in srgb,#2E7DF0 20%,#a7b0c6),-4px -4px 12px rgba(255,255,255,.9),0 0 0 10px rgba(191,155,46,0)}100%{box-shadow:6px 6px 16px color-mix(in srgb,#2E7DF0 20%,#a7b0c6),-4px -4px 12px rgba(255,255,255,.9),0 0 0 0 rgba(191,155,46,0)}}
@keyframes v-marquee{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}

/* Which scene is on. His ten scene cards are stacked in one 352px window. */
.hv-scene{display:none}
.hv-scene.on{display:flex}

/* The headline wipes open behind a gold bar, one line at a time. */
.hv-line{position:relative;display:block;overflow:hidden}
.hv-line > span{display:block;animation:v-reveal 9s cubic-bezier(.65,0,.35,1) infinite}
.hv-line::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(191,155,46,.9),rgba(138,114,34,.9));
  animation:v-blockpass 9s cubic-bezier(.65,0,.35,1) infinite;
}

/* ⛔ AN EMPTY PICTURE SLOT IS A QUIET GREY PANEL, NOT A BROKEN-IMAGE ICON.
   His instruction: leave the photos blank, he fills them in the CMS. A missing
   src would otherwise draw the browser's torn-page glyph on a marketing page. */
.hp-slot{background:#dfe3ee}
.hp-slot:not([src]),.hp-slot[src=""]{background:#dfe3ee}

/* His hover and active states, lifted out of the style-hover attributes that
   only his design tool reads. Generated - do not hand-edit. */
.hph1:hover{color:#8a7222}
.hph2:hover{color:#8a7222}
.hph3:hover{color:#8a7222}
.hph4:hover{color:#8a7222}
.hph5:hover{color:#8a7222}
.hph6:hover{color:#8a7222}
.hph7:hover{filter:brightness(1.06)}
.hph8:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph9:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph10:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph11:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph12:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph13:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph14:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph15:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph16:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph17:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph18:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph19:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph20:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph21:hover{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}
.hph22:hover{color:#8a7222}
.hph23:hover{color:#8a7222}
.hph24:hover{color:#8a7222}
.hph25:hover{color:#8a7222}
.hph26:hover{color:#8a7222}
.hph27:hover{color:#8a7222}
.hph28:hover{color:#8a7222}
.hph29:hover{color:#8a7222}
.hph30:hover{color:#8a7222}
.hph31:hover{color:#8a7222}
.hph32:hover{color:#8a7222}
.hph33:hover{color:#8a7222}
.hph34:hover{color:#8a7222}
.hph35:hover{color:#8a7222}
.hpa1:active{box-shadow:inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),inset -4px -4px 10px rgba(255,255,255,.95)}

/* ============================================================================
   THE PHONE — from his own Mobile.dc.html, measured, not guessed.
   ----------------------------------------------------------------------------
   ⛔ ONE PAGE, TWO LOOKS — NOT TWO PAGES. His two design files say the SAME
   THING: 226 blocks of text on the desktop, 215 on the phone, and the handful
   that differ are "Hover any block" becoming "Tap any block", a shortened
   button, and a bar of buttons pinned to the bottom. Nothing is added and
   nothing is dropped. Serving two documents would mean maintaining both
   forever, guessing from a user-agent string which one a foldable gets, and
   giving Google two URLs for one page.

   ⚠️ HIS MOBILE FILE ALSO DRAWS A FAKE PHONE — a 390px rounded shell with
   "9:41" and a battery glyph at the top. That is his design tool's frame, not
   his website, and it is deliberately NOT ported.

   Every number below is lifted from that file:
     hero headline 38px/1.02 · hero text 15px · badge 12px 20px
     module card 18px, radius 22 · panel radius 24, padding 18 · stage 400px
     promo band 30px 20px, the price at 36px · bottom bar 14px 18px 22px
   The breakpoint is 860px because that is where this app already swaps the nav
   for a burger; a second, different breakpoint would show a phone layout with
   a desktop menu on top of it for 40 pixels.
   ========================================================================== */
@media (max-width:860px){
  /* every grid becomes one column — except his 27-dot broadcast wall, which is
     nine across at any size and is what that scene IS. */
  .ds2 .hp-grid{grid-template-columns:1fr !important;gap:14px !important}
  .ds2 [data-sec] > div{padding-left:20px !important;padding-right:20px !important}
  .ds2 [data-sec="hero"]{padding:18px 20px 10px !important}
  .ds2 [data-sec="promo"]{padding:30px 20px !important}
  .ds2 [data-sec="foot"]{padding:28px 20px !important}

  /* the hero, at his phone sizes */
  .ds2 [data-sec="hero"] > div{grid-template-columns:1fr !important;gap:20px !important}
  .ds2 .hv-head{font-size:38px !important;line-height:1.02 !important;margin-top:18px !important}
  .ds2 .hv-sub{font-size:15px !important;margin-top:16px !important;max-width:none !important}
  .ds2 [data-sec="hero"] [style*="border-radius:999px"][style*="linear-gradient(135deg,#bf9b2e"]{
    padding:12px 20px !important;font-size:12.5px !important;letter-spacing:.18em !important;
  }
  /* the two hero buttons go full width and stack */
  .ds2 [data-sec="hero"] [style*="margin-top:32px"]{flex-direction:column !important;align-items:stretch !important;gap:10px !important}
  .ds2 #heroRegister,.ds2 #heroLogin{text-align:center;padding:16px 20px !important;font-size:15px !important}

  /* the panel */
  .ds2 .hv-panel{border-radius:24px !important;padding:18px !important}
  .ds2 .hv-stage{height:400px !important}
  .ds2 .hv-tabs{font-size:9px !important;gap:4px !important}
  .ds2 .hv-ameera{right:-4px !important;top:-14px !important;padding:10px 14px !important}

  /* the offer band and its price */
  .ds2 [data-sec="promo"] [style*="font-size:50px"]{font-size:36px !important}
  .ds2 [data-sec="promo"] [style*="font-size:40px"]{font-size:24px !important}
  .ds2 [data-sec="promo"] [style*="font-size:30px"]{font-size:17px !important}

  /* cards */
  .ds2 .hp-grid > div{padding:18px !important;border-radius:22px !important}
  .ds2 .hp-grid-mod > div > div:first-child{height:120px !important;margin:-18px -18px 14px !important}

  /* the big headings */
  .ds2 [style*="font-size:54px"],.ds2 [style*="font-size:52px"],
  .ds2 [style*="font-size:48px"],.ds2 [style*="font-size:46px"],
  .ds2 [style*="font-size:44px"]{font-size:30px !important;line-height:1.06 !important}

  /* ⛔ THE BAR HE PINNED TO THE BOTTOM. On a phone the top buttons scroll away
     within a screen and a visitor who decides halfway down has nothing to press.
     His file solves it with a sticky bar; this is that bar, his measurements. */
  .hp-bottom{
    position:sticky;bottom:0;z-index:30;
    display:flex;align-items:center;gap:10px;
    padding:14px 18px 22px;
    background:rgba(236,238,246,.94);backdrop-filter:blur(10px);
    box-shadow:0 -8px 20px rgba(15,23,42,.08);
  }
  .hp-bottom > .r{
    flex:1;padding:15px;border-radius:14px;text-align:center;
    font-size:14px;font-weight:800;color:#fff;
    background:linear-gradient(135deg,#bf9b2e,#8a7222);cursor:pointer;
  }
  .hp-bottom > .l{
    padding:15px 18px;border-radius:14px;
    font-size:14px;font-weight:800;color:#8a7222;background:#eceef6;cursor:pointer;
    box-shadow:4px 4px 10px color-mix(in srgb,#2E7DF0 16%,#a7b0c6),-3px -3px 8px rgba(255,255,255,.95);
  }
}
/* Above 860px the bar is simply not there — the nav buttons are in view. */
.hp-bottom{display:none}
@media (max-width:860px){ .hp-bottom{display:flex} }

/* ⛔ STILLNESS IS A SETTING, NOT A PREFERENCE TO OVERRULE. Twenty-five looping
   animations in front of someone who gets migraines is a real problem, and the
   operating system already knows who they are. Colours, shadows and layout all
   stay; only the movement stops. wireHeroV2() checks the same switch and does
   not start its timer. */
@media (prefers-reduced-motion:reduce){
  .ds2 *,.ds2 *::before,.ds2 *::after{animation:none !important;transition-duration:.01ms !important}
}
/* ⛔ END OF THE HOME1 LAYER. */

/* The login card keeps its coloured backdrop, but sits embossed on it. */
.login-wrap{background:#eceef6 !important}
.login-card{
  background:#eceef6 !important;border:none !important;
  box-shadow:9px 9px 22px color-mix(in srgb, var(--primary) 18%, #a7b0c6), -9px -9px 22px rgba(255,255,255,.95) !important;
}
[data-theme="dark"] .login-wrap{background:#232838 !important}
[data-theme="dark"] .login-card{
  background:#232838 !important;
  box-shadow:9px 9px 22px rgba(0,0,0,.55), -9px -9px 22px rgba(255,255,255,.055) !important;
}

/* Dashboard request blocks (Expenses / Help Desk / Complaints / Resignations) */
.req-card{cursor:pointer;transition:transform .12s ease, box-shadow .16s ease}
.req-card:hover{transform:translateY(-2px)}
body.soft .req-card:hover{box-shadow:8px 8px 18px var(--nm-dark), -8px -8px 18px var(--nm-light) !important}
.req-card:active{transform:translateY(0)}
body.soft .req-card:active{box-shadow:var(--nm-in) !important}
.req-card .value{font-size:26px;font-weight:800;line-height:1.15}
.req-card small{display:block;margin-top:2px;font-size:13px}
/* The expiry badges are buttons now. */
button.badge{font:inherit;font-size:12px;font-weight:600}
button.badge[disabled]{opacity:.6}

/* ---- Frozen page heading + filter bar ----
   The top bar is already sticky; the page heading freezes under it and the
   filter bar freezes under that, so the table scrolls beneath them. Offsets are
   measured in JS (--topbar-h / --head-stick) because the heights change with
   screen width and text wrapping. */
.content > .page-head{
  position:sticky; top:var(--topbar-h,56px); z-index:16;
  background:var(--bg); padding:10px 0 8px; margin-top:-10px;
}
.content > .sticky-filter{
  position:sticky; top:var(--head-stick,120px); z-index:15;
  background:var(--bg);
}
body.soft .content > .page-head,body.soft .content > .sticky-filter{background:var(--bg)}
/* Freezing needs room, so drop it on very short screens. */
@media(max-height:520px){
  .content > .page-head,.content > .sticky-filter{position:static}
}

/* ---- Live Chat: find an employee to start a conversation ---- */
.lc-results{margin-top:8px;display:flex;flex-direction:column;gap:5px;max-height:230px;overflow-y:auto}
.lc-results:empty{display:none}
.lc-hit{display:flex;flex-direction:column;align-items:flex-start;gap:1px;padding:8px 11px;border-radius:10px;
  background:var(--surface);border:1px solid var(--border);cursor:pointer;text-align:left;width:100%}
.lc-hit:hover{background:color-mix(in srgb,var(--primary) 9%,transparent)}
.lc-hit b{font-size:13.5px;font-weight:600;line-height:1.25}
.lc-hit small{font-size:13px;color:var(--muted)}
.lc-none{padding:9px 11px;font-size:14.5px;color:var(--muted)}
body.soft .lc-hit{border:none;box-shadow:var(--nm-out-sm)}
body.soft .lc-hit:active{box-shadow:var(--nm-in)}

/* ---- Managed dropdown (Document Type) with + / Edit ---- */
.mng-row{display:flex;gap:6px;align-items:stretch}
.mng-row select{flex:1;min-width:0}
.mng-btn{flex:0 0 auto;padding:0 11px;min-width:38px;display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:700}
.mng-btn svg{width:15px;height:15px}
.dt-list{display:flex;flex-direction:column;gap:8px;max-height:320px;overflow-y:auto}
.dt-row{display:flex;gap:7px;align-items:center}
.dt-row .dt-name{flex:1;min-width:0;padding:8px 10px;border:1px solid var(--border);border-radius:9px;
  background:var(--bg);color:var(--text);font-size:13.5px}
body.soft .dt-row .dt-name{border:none;box-shadow:var(--nm-in)}

/* Missing-documents summary badges */
.md-sum{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}

/* ---- Add Employee: document picker (tick to open a section) ---- */
.doc-picker{display:flex;flex-wrap:wrap;gap:8px}
.doc-pick{display:inline-flex;align-items:center;gap:7px;padding:8px 12px;border:1px solid var(--border);
  border-radius:20px;cursor:pointer;font-size:13px;font-weight:500;user-select:none;background:var(--surface)}
.doc-pick input{width:15px;height:15px;accent-color:var(--primary);cursor:pointer}
.doc-pick:has(input:checked){background:color-mix(in srgb,var(--primary) 12%,transparent);
  border-color:color-mix(in srgb,var(--primary) 45%,transparent);color:var(--primary-ink)}
body.soft .doc-pick{border:none;box-shadow:var(--nm-out-sm)}
body.soft .doc-pick:has(input:checked){box-shadow:var(--nm-in)}

/* Sidebar hover in the soft-emboss theme: a raised, tinted feel */
body.soft .nav-item:hover{background:color-mix(in srgb,var(--primary) 14%,transparent);
  box-shadow:var(--nm-out-sm);transform:translateX(2px)}
body.soft .nav-item.active{box-shadow:var(--nm-in)}
/* The public/menu-search and portal nav get the same feedback */
body.soft .pnav-item:hover{background:color-mix(in srgb,var(--primary) 12%,transparent);transform:translateX(2px)}
.pnav-item{transition:background .15s ease,transform .12s ease}
.pnav-item:hover{background:color-mix(in srgb,var(--primary) 10%,transparent)}

/* ---- Compact dashboard stat cards (shorter, denser) ---- */
.cards{gap:12px;margin-bottom:14px}
.card.stat{padding:12px 14px;align-items:center}
.card.stat .value{font-size:24px;margin-top:2px;line-height:1.1}
.card.stat .label{font-size:12.5px}
.card.stat .ic{width:34px;height:34px;border-radius:10px}
.card.stat .ic svg{width:18px;height:18px}
.card.stat small{font-size:13px;margin-top:1px}
.req-card{padding:12px 14px !important}
body.soft .card.stat{padding:12px 14px !important}
body.soft .req-card{padding:12px 14px !important}
/* Tighter columns so the row fits more cards and less empty space */
.cards{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}

/* ---- Live recording waveform ---- */
.rec-wave{display:flex;align-items:center;gap:2px;flex:1;min-width:0;height:38px;padding:0 12px;
  border-radius:12px;background:color-mix(in srgb,var(--danger) 8%,var(--surface));
  border:1px solid color-mix(in srgb,var(--danger) 30%,transparent);overflow:hidden}
body.soft .rec-wave{border:none;box-shadow:var(--nm-in)}
.rec-wave .rec-dot{width:9px;height:9px;border-radius:50%;background:var(--danger);flex:0 0 auto;
  margin-right:6px;animation:recpulse 1s ease-in-out infinite}
@keyframes recpulse{0%,100%{opacity:1}50%{opacity:.25}}
.rec-wave i{display:block;width:3px;height:4px;flex:0 0 auto;border-radius:2px;
  background:var(--danger);transition:height .06s linear;align-self:center}
.rec-wave.idle i{animation:recidle 1s ease-in-out infinite}
.rec-wave.idle i:nth-child(3n){animation-delay:.15s}
.rec-wave.idle i:nth-child(3n+1){animation-delay:.3s}
@keyframes recidle{0%,100%{height:4px}50%{height:16px}}
.rec-wave .rec-time{margin-left:8px;font-size:12px;font-weight:600;color:var(--danger);
  flex:0 0 auto;font-variant-numeric:tabular-nums}
.rec-wave .rec-stop{margin-left:8px;flex:0 0 auto;padding:6px 14px;border-radius:9px;
  background:var(--danger);color:#fff;font-weight:600;font-size:13px;cursor:pointer}
.mic-btn.recording{background:var(--danger) !important;color:#fff !important}

/* ---- Live Chat: clear history / select messages ---- */
.chat-selbar{display:flex;align-items:center;gap:8px;padding:8px 12px;font-size:13px;
  background:color-mix(in srgb,var(--danger) 8%,var(--surface));border-bottom:1px solid var(--border)}
.chat-msg{position:relative}
.chat-msg .msg-tick{display:none;position:absolute;top:50%;transform:translateY(-50%);left:2px;
  width:20px;height:20px;border-radius:50%;border:1.5px solid var(--muted);color:transparent;
  font-size:12px;line-height:17px;text-align:center;cursor:pointer;background:var(--surface);z-index:2}
.chat-log.selecting .chat-msg{cursor:pointer;padding-left:26px}
.chat-log.selecting .chat-msg .msg-tick{display:block}
.chat-msg.picked .msg-tick{background:var(--primary);border-color:var(--primary);color:#fff}
.chat-log.selecting .chat-msg.picked .bubble{outline:2px solid var(--primary);outline-offset:1px}

/* ---- Homepage editor (super admin) ---- */
.se-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}
.se-card{border:1px solid var(--border);border-radius:12px;padding:12px}
body.soft .se-card{border:none;box-shadow:var(--nm-out-sm)}
.se-card b{font-size:13px;display:block;margin-bottom:8px}
.se-img{display:flex;flex-direction:column;gap:6px}
.se-thumb{height:90px;border-radius:9px;overflow:hidden;background:color-mix(in srgb,var(--muted) 14%,transparent);
  display:flex;align-items:center;justify-content:center;font-size:13px;color:var(--muted)}
.se-thumb img{width:100%;height:100%;object-fit:cover}
.se-feat{border:1px solid var(--border);border-radius:12px;padding:12px;margin-bottom:12px}
body.soft .se-feat{border:none;box-shadow:var(--nm-out-sm)}
.se-feat .se-img{margin-top:8px;max-width:220px}

/* Homepage editor: resizable text areas + current-image tag */
.se-ta{width:100%;min-height:52px;resize:vertical;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font:inherit;font-size:14px;line-height:1.45}
body.soft .se-ta{border:none;box-shadow:var(--nm-in)}
.se-thumb{position:relative}
.se-deftag{position:absolute;bottom:4px;right:4px;background:rgba(15,23,42,.7);color:#fff;font-size:9px;padding:1px 6px;border-radius:8px;letter-spacing:.03em}

/* Homepage editor: repeatable items */
.se-item{border:1px solid var(--border);border-radius:12px;padding:12px;margin-bottom:12px}
body.soft .se-item{border:none;box-shadow:var(--nm-out-sm)}
.se-item-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px}
.se-item .se-img{max-width:230px;margin-top:8px}
.se-sublist{display:flex;flex-direction:column;gap:6px;margin:8px 0}
.se-subrow{display:flex;gap:6px;align-items:center}
.se-subrow input{flex:1}

/* Install-as-app banner */
.install-bar{position:fixed;left:12px;right:12px;bottom:12px;max-width:520px;margin:0 auto;z-index:5000;display:flex;align-items:center;gap:12px;padding:12px 14px;border-radius:14px;background:var(--surface,#fff);box-shadow:0 12px 40px rgba(15,23,42,.28);border:1px solid var(--border,#e5e9f2)}
body.soft .install-bar{border:none;box-shadow:6px 6px 16px var(--nm-dark),-6px -6px 16px var(--nm-light)}
.install-bar .ib-ic{font-size:22px;flex:0 0 auto}
.install-bar .ib-tx{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.3}
.install-bar .ib-tx b{font-size:14px}
.install-bar .ib-tx small{font-size:12px;color:var(--muted,#667085)}
.install-bar .ib-share{display:inline-block;transform:translateY(1px)}
.install-bar .ib-x{flex:0 0 auto;width:28px;height:28px;border-radius:8px;background:transparent;color:var(--muted,#667085);font-size:15px;cursor:pointer;border:none}
.install-bar .ib-x:hover{background:color-mix(in srgb,var(--muted) 15%,transparent)}
@media print{.install-bar{display:none!important}}

/* Homepage editor: live previews + emoji picker */
.se-live-row{display:flex;gap:16px;flex-wrap:wrap}
.se-fields{flex:1;min-width:260px}
.se-preview{flex:0 0 240px;max-width:280px}
.se-plabel{font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);display:block;margin-bottom:6px}
.se-stat-demo{background:var(--surface);border-radius:12px;padding:16px;text-align:center;box-shadow:var(--nm-out-sm,0 2px 8px rgba(0,0,0,.08))}
.se-stat-demo b{display:block;font-size:26px;font-weight:800;color:var(--primary-ink)}
.se-stat-demo span{font-size:14px;color:var(--muted)}
.se-slide-demo{height:150px;border-radius:12px;padding:14px;display:flex;flex-direction:column;justify-content:flex-end;gap:3px;background:var(--primary-grad);background-size:cover;background-position:center;color:#fff;position:relative;overflow:hidden}
.se-slide-demo::before{content:'';position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,.05),rgba(0,0,0,.55))}
.se-slide-demo>*{position:relative;z-index:1}
.se-slide-demo .sd-eyebrow{font-size:9px;letter-spacing:.08em;text-transform:uppercase;opacity:.9}
.se-slide-demo .sd-title{font-size:16px;font-weight:800;line-height:1.15}
.se-slide-demo .sd-sub{font-size:11px;opacity:.92;line-height:1.3;max-height:30px;overflow:hidden}
.se-emoji-row{display:flex;align-items:center;gap:8px}
.se-emoji-in{max-width:70px;text-align:center;font-size:18px}
.se-emoji-cur{font-size:24px}
.se-emoji-pick{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px;max-width:340px}
.se-emoji-b{width:30px;height:30px;border-radius:8px;border:1px solid var(--border);background:var(--surface);font-size:16px;cursor:pointer;line-height:1}
.se-emoji-b:hover{background:color-mix(in srgb,var(--primary) 14%,transparent)}
body.soft .se-emoji-b{border:none;box-shadow:var(--nm-out-sm)}
@media(max-width:640px){.se-preview{flex:1 1 100%}}

/* Homepage editor: menu builder */
.se-submenu{margin-top:10px;padding-left:12px;border-left:2px solid var(--border)}
.se-subitem{margin-bottom:6px}
.se-page{margin-top:10px;padding:10px;border-radius:10px;background:color-mix(in srgb,var(--primary) 5%,transparent)}
.se-item-head select{max-width:200px}

/* ---- Cookie consent banner (UAE PDPL / GDPR-style) ---- */
.ck-consent{position:fixed;left:16px;right:16px;bottom:16px;z-index:4000;max-width:940px;margin:0 auto;background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:16px;box-shadow:0 18px 50px rgba(15,23,42,.22);padding:16px 18px;animation:ckUp .3s ease}
@keyframes ckUp{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}
.ck-main{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.ck-txt{flex:1;min-width:240px}
.ck-txt b{display:block;font-size:15px;margin-bottom:3px}
.ck-txt span{font-size:14.5px;color:var(--muted);line-height:1.45}
.ck-txt a{color:var(--primary-ink);text-decoration:underline}
.ck-acts{display:flex;gap:8px;flex-wrap:wrap}
.ck-btn{border-radius:10px;padding:9px 16px;font-size:13px;font-weight:700;cursor:pointer;border:1px solid var(--border);white-space:nowrap;transition:filter .12s ease,background .12s ease}
.ck-btn.ghost{background:transparent;color:var(--text)}
.ck-btn.ghost:hover{background:var(--bg)}
.ck-btn.primary{background:var(--primary);color:#fff;border-color:var(--primary)}
.ck-btn.primary:hover{filter:brightness(1.06)}
.ck-opts{margin-top:14px;border-top:1px solid var(--border);padding-top:12px;display:flex;flex-direction:column;gap:10px}
.ck-opt{display:flex;align-items:center;justify-content:space-between;gap:14px;font-size:13px}
.ck-opt span{display:flex;flex-direction:column}
.ck-opt small{color:var(--muted);font-size:13.5px}
.ck-opt input{width:18px;height:18px;accent-color:var(--primary);flex:none}
.ck-opts-acts{display:flex;justify-content:flex-end;margin-top:2px}
.ck-reopen{cursor:pointer;color:inherit;text-decoration:underline}
@media(max-width:560px){.ck-acts{width:100%}.ck-btn{flex:1;text-align:center}}
body.soft .ck-consent{box-shadow:var(--nm-out);border:none}
body.soft .ck-btn{border:none;box-shadow:var(--nm-out-sm)}
body.soft .ck-btn.primary{box-shadow:var(--nm-out-sm)}

/* ---- Legal / Privacy Policy page ---- */
.ds2-legal{max-width:820px;margin:0 auto;padding:38px 22px 64px}
.ds2-legal h1{font-size:30px;margin:6px 0 2px;line-height:1.2;color:#0f172a}
.ds2-legal-upd{color:#64748b;font-size:13px;margin-bottom:22px}
.ds2-legal-body{font-size:15px;line-height:1.75;color:#334155}
.ds2-legal-body h2{font-size:19px;margin:26px 0 8px;color:var(--navy)}
.ds2-legal-body p{margin:0 0 12px;color:#334155}
.ds2-legal-body ul{margin:0 0 14px;padding-left:20px}
.ds2-legal-body li{margin:5px 0;color:#334155}
.ds2-legal-body b{color:#1f2a44}
.ds2-legal-body a{color:var(--primary-ink);text-decoration:underline}
.ds2-legal-foot{margin-top:32px}

/* ---- Upload progress bar (green) ---- */
.upbar{position:fixed;left:0;right:0;top:0;z-index:5000;display:flex;justify-content:center;pointer-events:none;transform:translateY(-120%);transition:transform .25s ease}
.upbar.on{transform:translateY(0)}
.upbar-box{margin-top:14px;background:var(--surface);border:1px solid var(--border);box-shadow:0 12px 34px rgba(15,23,42,.18);border-radius:12px;padding:10px 14px;min-width:280px;max-width:92vw;display:flex;align-items:center;gap:12px}
.upbar-label{font-size:12.5px;font-weight:700;color:var(--text);white-space:nowrap}
.upbar-track{flex:1;height:12px;background:color-mix(in srgb,#16a34a 14%,var(--border));border-radius:7px;overflow:hidden;position:relative}
.upbar-fill{height:100%;width:8%;border-radius:7px;background:repeating-linear-gradient(90deg,#22c55e 0,#22c55e 14px,#16a34a 14px,#16a34a 22px);background-size:200% 100%;transition:width .2s ease;animation:upStripe 1s linear infinite}
@keyframes upStripe{from{background-position:0 0}to{background-position:44px 0}}
.upbar-pct{font-size:12px;font-weight:800;color:#16a34a;min-width:34px;text-align:right}
body.soft .upbar-box{border:none;box-shadow:var(--nm-out)}

/* ---- Help Desk unseen ticket indicator ---- */
.unread-dot{display:inline-block;width:9px;height:9px;border-radius:50%;background:#ef4444;margin-right:7px;vertical-align:middle;box-shadow:0 0 0 3px color-mix(in srgb,#ef4444 22%,transparent)}
tr.tk-unseen{background:color-mix(in srgb,#f59e0b 8%,transparent)}
tr.tk-unseen b{font-weight:800}

/* ---- Document preview (floating window, auto-fit; view-only) ---- */
.prev-modal{max-width:min(94vw,920px);width:auto;display:flex;flex-direction:column;max-height:92vh}
.prev-title{max-width:64%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:15px}
.prev-body{flex:1;min-height:220px;max-height:calc(92vh - 66px);overflow:auto;display:flex;align-items:center;justify-content:center;padding:12px;background:var(--bg);border-radius:0 0 16px 16px}
.prev-img{max-width:100%;max-height:calc(92vh - 96px);width:auto;height:auto;object-fit:contain;border-radius:8px}
.prev-frame{width:min(88vw,840px);height:calc(92vh - 96px);border:none;border-radius:8px;background:#fff}

/* ---- Colorful action icons (view / documents / edit / deactivate / delete) ---- */
.act-ic{width:18px;height:18px;object-fit:contain;vertical-align:middle;display:inline-block;pointer-events:none}
/* Smaller, tighter action buttons wherever these icons are used */
.icon-btn:has(.act-ic){width:28px !important;height:28px !important;padding:3px;margin:0 1px}
.btn.sm:has(.act-ic){padding-top:5px;padding-bottom:5px}

/* ---- Document Expiry: active filter card ---- */
.card.req-card.active{outline:2px solid var(--primary);outline-offset:-2px}
body.soft .card.req-card.active{box-shadow:var(--nm-in) !important}

/* Child-establishment filter (Master Admin) */
.est-filter{border-color:color-mix(in srgb,var(--primary) 40%,var(--border));font-weight:600}
.est-filter:focus{outline:2px solid var(--primary);outline-offset:-2px}

/* Permanent-delete warning */
.perm-warn{border:2px solid var(--danger)}
.perm-warn-ic{width:64px;height:64px;border-radius:50%;background:color-mix(in srgb,var(--danger) 14%,transparent);
  color:var(--danger);font-size:34px;font-weight:800;display:flex;align-items:center;justify-content:center;margin:0 auto;
  animation:permPulse .9s ease-in-out infinite alternate}
@keyframes permPulse{from{transform:scale(1);box-shadow:0 0 0 0 color-mix(in srgb,var(--danger) 35%,transparent)}
  to{transform:scale(1.06);box-shadow:0 0 0 12px transparent}}

/* Sidebar logo acts as a home button */
.brand-logo-side{transition:transform .12s ease,opacity .12s ease}
.brand-logo-side:hover{transform:scale(1.04);opacity:.9}
.brand-logo-side:focus-visible{outline:2px solid var(--primary);outline-offset:3px;border-radius:6px}

/* ===== Public: Subscription plans page ("Start with 0 Cost") =================
   Layout follows the familiar SaaS pricing pattern — a highlighted middle card
   flanked by the others — but the palette stays ours: brand blue for the popular
   card and CTAs, with green reserved for the "included" ticks. Colours come from
   the CSS variables the CMS already overrides, so a brand-colour change in the
   console recolours this page too. */
.dsp-wrap{max-width:1180px;margin:0 auto;padding:48px 5vw 72px}
.dsp-head{text-align:center;margin-bottom:34px}
.dsp-head h1{font-size:clamp(30px,5vw,46px);line-height:1.15;margin:0 0 12px;letter-spacing:-.02em}
.dsp-sub{color:#475569;font-size:16px;max-width:640px;margin:0 auto 18px}
.dsp-badges{display:flex;flex-wrap:wrap;gap:10px 18px;justify-content:center}
.dsp-chip{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;font-weight:600;color:#0f172a}
.dsp-tick{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;flex:0 0 18px;
  border-radius:50%;background:var(--ok,#10b981);color:#fff;font-size:11px;font-weight:800;line-height:1}

.dsp-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:18px;align-items:stretch;margin-bottom:14px}
.dsp-card{display:flex;flex-direction:column;gap:10px;background:#fff;border:1px solid rgba(15,23,42,.10);
  border-radius:18px;padding:26px 22px;position:relative;transition:transform .16s ease,box-shadow .16s ease}
.dsp-card:hover{transform:translateY(-3px);box-shadow:0 12px 30px rgba(15,23,42,.10)}
.dsp-card.popular{border:2px solid var(--blue,#BF9B2E);box-shadow:0 14px 40px rgba(191,155,46,.16)}
.dsp-badge{position:absolute;top:16px;right:16px;background:color-mix(in srgb,var(--blue,#BF9B2E) 14%,transparent);
  color:var(--blue,#BF9B2E);font-size:10.5px;font-weight:800;letter-spacing:.08em;padding:4px 9px;border-radius:6px}
.dsp-name{font-size:19px;font-weight:700;margin:0}
.dsp-price{display:flex;align-items:baseline;gap:4px;margin-top:4px}
.dsp-cur{font-size:20px;font-weight:700;color:#0f172a}
.dsp-amt{font-size:40px;font-weight:800;letter-spacing:-.03em;line-height:1}
.dsp-price-txt{font-size:20px;font-weight:700;color:#0f172a;display:block}
.dsp-period{color:#64748b;font-size:15.5px}
.dsp-band{color:#475569;font-size:14px;font-weight:600;margin-top:2px}
.dsp-cta{margin-top:auto;width:100%;justify-content:center}
.dsp-foot{text-align:center;color:#64748b;font-size:15.5px;margin:6px 0 44px}

.dsp-incl{background:#f8fafc;border:1px solid rgba(15,23,42,.07);border-radius:20px;padding:32px 28px}
.dsp-incl h2{font-size:22px;margin:0 0 20px;text-align:center}
.dsp-incl-list{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 26px}
.dsp-incl-list li{display:flex;align-items:flex-start;gap:10px;font-size:14.5px;color:#0f172a;line-height:1.5}
.dsp-incl-list li .dsp-tick{margin-top:2px}

.dsp-cta-band{text-align:center;margin-top:44px;padding:38px 24px;border-radius:20px;
  background:var(--primary-grad,linear-gradient(135deg,#8A7021,#BF9B2E));color:#fff}
.dsp-cta-band h2{margin:0 0 8px;font-size:26px}
.dsp-cta-band p{margin:0 0 20px;opacity:.92;font-size:15px}
.dsp-cta-big{background:#fff;color:var(--primary-d,#8A7021);font-weight:700;padding:12px 30px}
.dsp-cta-big:hover{background:#f1f5f9}

@media (max-width:900px){
  .dsp-wrap{padding:32px 5vw 56px}
  .dsp-grid{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
  .dsp-card.popular{transform:none}
}

/* ===== Sidebar: expandable parent menus with sub-modules ====================
   A parent (e.g. Homepage CMS) toggles its children rather than navigating.
   It stays open while one of its children is the current page, so you can
   always see where you are inside a multi-section module. */
.nav-parent{margin-bottom:2px}
.nav-toggle{cursor:pointer;user-select:none}
.nav-toggle .nav-caret{margin-left:auto;font-size:11px;opacity:.7;font-style:normal;transition:opacity .15s ease}
.nav-toggle:hover .nav-caret{opacity:1}
/* The parent is highlighted, but more softly than the active page itself, so the
   two are never confused at a glance. */
.nav-item.active-parent{background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary-ink);font-weight:600}
.nav-item.active-parent svg{color:var(--primary-ink)}
.nav-sub{margin:2px 0 6px 14px;padding-left:10px;border-left:2px solid color-mix(in srgb,var(--primary) 22%,transparent);
  display:flex;flex-direction:column;gap:1px}
.nav-sub[hidden]{display:none}
.nav-subitem{padding:8px 10px;font-size:13.5px;font-weight:500;border-radius:8px}
.nav-subitem:hover{transform:translateX(2px)}
.nav-subitem.active{background:color-mix(in srgb,var(--primary) 16%,transparent);color:var(--primary-ink);font-weight:600}

/* CMS section tabs — quick switching between sub-modules without the side menu. */
.se-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}
.se-tab{padding:7px 14px;border-radius:999px;border:1px solid var(--border);background:var(--surface);
  color:var(--muted);font-size:15px;font-weight:600;cursor:pointer;transition:all .14s ease}
.se-tab:hover{border-color:var(--primary);color:var(--primary-ink)}
.se-tab.on{background:var(--primary);border-color:var(--primary);color:#fff}

/* Sub-page editor: photo gallery thumbnails. */
.se-gallery{display:flex;flex-wrap:wrap;gap:10px;margin-top:4px}
.se-galitem{position:relative;width:120px;height:84px;border-radius:10px;overflow:hidden;border:1px solid var(--border)}
.se-galitem img{width:100%;height:100%;object-fit:cover;display:block}
.se-galitem .btn{position:absolute;top:3px;right:3px;padding:1px 7px;min-height:0;line-height:1.5;
  background:rgba(255,255,255,.92);border-radius:6px;font-size:12px}
.se-detail{border-left:3px solid color-mix(in srgb,var(--primary) 35%,transparent)}
.se-detail .se-item-head{flex-wrap:wrap;gap:8px}

/* Preview banner — pinned to the bottom so it never hides page content. */
.preview-bar{position:fixed;left:0;right:0;bottom:0;z-index:99999;display:flex;align-items:center;gap:12px;
  padding:10px 18px;background:var(--navy);color:#fff;font-size:13px;line-height:1.45;
  box-shadow:0 -2px 14px rgba(15,23,42,.22)}
.preview-bar b{background:#fff;color:var(--navy);border-radius:5px;padding:2px 8px;font-size:11px;letter-spacing:.09em;flex:0 0 auto}
body.has-preview-bar{padding-bottom:46px}
@media (max-width:640px){.preview-bar{font-size:12px;padding:8px 12px}body.has-preview-bar{padding-bottom:62px}}

/* Live password rule checklist — green tick once met, red ✕ until then. */
.pw-rules{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:7px}
.pw-rule{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:500;color:var(--danger,#dc2626);line-height:1.5}
.pw-rule i{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;flex:0 0 14px;
  border-radius:50%;background:var(--danger,#dc2626);color:#fff;font-size:9px;font-style:normal;font-weight:700}
.pw-rule.ok{color:var(--ok,#10b981)}
.pw-rule.ok i{background:var(--ok,#10b981)}
/* Before typing starts, the list is guidance rather than an error. */
.pw-rules.idle .pw-rule{color:var(--muted,#64748b)}
.pw-rules.idle .pw-rule i{background:var(--muted,#94a3b8)}

/* Inline email validation message. */
.em-err{font-size:11.5px;color:var(--danger,#dc2626);margin-top:4px;line-height:1.45}

/* Currency picker — regions as option groups, with a search box above. */
.cur-select{width:100%}
.cur-select optgroup{font-weight:700;color:var(--muted)}

/* ===== Phone field: dial-code block + local number ==========================
   The country block sits to the left of the number, with its own small search
   box so 237 countries stay usable. Below the pair, a quiet line shows exactly
   what will be stored, because a silently reformatted phone number is a
   surprise nobody wants. */
.phone-field{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-start}
.phone-cc{display:flex;flex-direction:column;gap:4px;flex:0 0 148px;min-width:132px}
.phone-search{padding:5px 8px;font-size:11.5px;border:1px solid var(--border);border-radius:7px;
  background:var(--surface);color:var(--text)}
.phone-code{padding:9px 8px;border:1px solid var(--border);border-radius:9px;background:var(--surface);
  color:var(--text);font-size:13.5px;width:100%}
.phone-num{flex:1 1 160px;min-width:140px}
.phone-hint{flex:1 0 100%;font-size:13px;color:var(--muted);margin-top:2px;letter-spacing:.01em}
@media (max-width:520px){.phone-cc{flex:1 1 100%}.phone-num{flex:1 1 100%}}

/* ===== Login location gate ================================================= */
.loc-gate{margin:0 0 14px}
.loc-pick{width:100%;display:flex;align-items:center;gap:10px;padding:12px 14px;border-radius:11px;cursor:pointer;
  border:1.5px dashed var(--primary);background:color-mix(in srgb,var(--primary) 7%,transparent);text-align:left}
.loc-gate.done .loc-pick{border-style:solid;background:color-mix(in srgb,var(--ok,#10b981) 9%,transparent);border-color:var(--ok,#10b981)}
.loc-lead{font-size:13px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted)}
.loc-val{font-weight:600;font-size:14px;margin-left:auto}
.loc-caret{color:var(--muted);font-size:14px}
/* Locked until a location is chosen — visibly inert rather than silently broken. */
.lf-locked{opacity:.5;pointer-events:none}
.loc-list{max-height:46vh;overflow-y:auto;display:flex;flex-direction:column;gap:2px}
.loc-head{font-size:13px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);
  padding:12px 4px 6px;position:sticky;top:0;background:var(--surface)}
.loc-head small{text-transform:none;letter-spacing:0;font-weight:500}
.loc-row{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border-radius:9px;border:1px solid transparent;
  background:transparent;cursor:pointer;text-align:left;font-size:14px;color:var(--text)}
.loc-row:hover{background:color-mix(in srgb,var(--primary) 10%,transparent);border-color:var(--primary)}
.loc-flag{font-size:18px;line-height:1;flex:0 0 auto}
.loc-name{flex:1}
.loc-tag{font-size:12.5px;font-weight:700;padding:2px 7px;border-radius:5px;background:var(--border);color:var(--muted)}
.loc-tag.ok{background:color-mix(in srgb,var(--ok,#10b981) 16%,transparent);color:var(--ok,#10b981)}

/* ===== Bulk import dialog ================================================== */
.imp-step{display:flex;align-items:center;gap:9px;font-weight:600;font-size:13.5px;margin-bottom:10px}
.imp-step b{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;flex:0 0 22px;
  border-radius:50%;background:var(--primary);color:#fff;font-size:12px}
.imp-country .badge{font-size:12.5px}
.imp-cols{margin-top:4px}
.imp-collist{display:flex;flex-wrap:wrap;gap:5px}
.imp-col{font-size:13px;padding:3px 8px;border-radius:6px;background:var(--border);color:var(--muted);white-space:nowrap}
.imp-col.req{background:color-mix(in srgb,var(--primary) 14%,transparent);color:var(--primary-ink);font-weight:600}
.imp-tally{display:flex;gap:10px;flex-wrap:wrap}
.imp-t{flex:1 1 120px;padding:12px 14px;border-radius:11px;background:var(--surface);border:1px solid var(--border);text-align:center}
.imp-t b{display:block;font-size:24px;line-height:1.2}
.imp-t span{font-size:13.5px;color:var(--muted)}
.imp-t.ok{border-color:var(--ok,#10b981);background:color-mix(in srgb,var(--ok,#10b981) 8%,transparent)}
.imp-t.ok b{color:var(--ok,#10b981)}
.imp-t.bad{border-color:var(--danger,#dc2626);background:color-mix(in srgb,var(--danger,#dc2626) 8%,transparent)}
.imp-t.bad b{color:var(--danger,#dc2626)}
.imp-sample div{font-size:13px;padding:3px 0}
.imp-line{display:inline-block;min-width:56px;font-size:13px;color:var(--muted)}
.imp-bad{max-height:230px;overflow-y:auto;margin-top:6px;border:1px solid var(--border);border-radius:10px;padding:10px}
.imp-bad>div{font-size:13px;padding:6px 0;border-bottom:1px solid var(--border)}
.imp-bad>div:last-child{border-bottom:0}
.imp-bad ul{margin:3px 0 0 62px;padding:0;list-style:disc}
.imp-bad li{font-size:12px;color:var(--danger,#dc2626);line-height:1.5}

/* Enquiry form: the plan they picked, and the bot honeypot. */
.reg-plan{background:color-mix(in srgb,var(--primary) 9%,transparent);border:1px solid color-mix(in srgb,var(--primary) 25%,transparent);
  border-radius:10px;padding:10px 13px;font-size:13px;margin-bottom:14px;line-height:1.55}
/* Off-screen rather than display:none — some bots skip hidden inputs but not
   positioned ones, and a real person can never reach it (tabindex -1). */
.hp-field{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

/* Leads: clickable status tiles and the message block. */
.lead-kpi{cursor:pointer;transition:border-color .14s ease,background .14s ease}
.lead-kpi:hover{border-color:var(--primary)}
.lead-kpi.on{border-color:var(--primary);background:color-mix(in srgb,var(--primary) 8%,transparent)}
.lead-msg{white-space:pre-wrap;background:var(--surface);border:1px solid var(--border);border-radius:10px;
  padding:11px 13px;font-size:13.5px;line-height:1.6;max-height:220px;overflow-y:auto}

/* ---- Document types: Required/Optional and their information fields ---- */
.dt-tick{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;cursor:pointer}
.dt-tick input{width:16px;height:16px;cursor:pointer}
.dt-meta{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:11.5px;
  padding:0 2px 10px;margin-top:-4px}
.dt-fields{margin-top:8px;display:flex;flex-direction:column;gap:6px}
.dt-field{display:flex;align-items:center;gap:6px}
.dt-fnum{width:20px;text-align:center;font-size:13px;color:var(--muted);flex:none}
.dt-field .dt-flabel{flex:1 1 120px;min-width:0;padding:7px 9px;border:1px solid var(--border);
  border-radius:9px;background:var(--surface);color:var(--text);font-size:13px}
.dt-field .dt-fkind{flex:0 0 88px;padding:7px 6px;border:1px solid var(--border);
  border-radius:9px;background:var(--surface);color:var(--text);font-size:12.5px}
@media (max-width:520px){
  .dt-field{flex-wrap:wrap}
  .dt-field .dt-flabel{flex:1 1 100%}
  .dt-field .dt-fkind{flex:0 0 84px}
}

/* ---- Hiding documents from the Add Employee picker ---- */
.doc-pick-edit{display:inline-flex;align-items:center;gap:8px;cursor:default}
.doc-pick-edit.is-hidden{opacity:.5}
.doc-pick-edit.is-hidden span{text-decoration:line-through}
.doc-eye{border:none;background:transparent;cursor:pointer;font-size:15px;line-height:1;
  padding:2px 4px;border-radius:7px}
.doc-eye:hover{background:var(--bg)}

/* The employer an employee is signed in against — shown on their own profile so
   somebody working across several establishments always knows which one. */
.p-employer{margin-top:8px;display:inline-block;padding:4px 12px;border-radius:999px;
  background:var(--bg);border:1px solid var(--border);color:var(--text);
  font-size:12.5px;font-weight:600;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Attendance search results: one employee per line. */
.ac-row{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  padding:9px 12px;border:1px solid var(--border);border-radius:12px;
  background:var(--surface);color:var(--text);cursor:pointer;margin-bottom:6px}
.ac-row:hover{border-color:var(--primary);background:var(--bg)}
.ac-row-main{display:flex;flex-direction:column;min-width:0;flex:1}
.ac-row-main b{font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ac-row-main small{color:var(--muted);font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ac-row-go{color:var(--muted);font-size:20px;flex:none}
.ac-list{display:flex;flex-direction:column;margin-top:8px}
/* The avatar holds a photo behind the initials; it must never outgrow its box. */
.avatar{position:relative;flex:none}
.avatar img{border-radius:inherit}
@media (max-width:520px){
  .ac-row{padding:8px 10px;gap:8px}
  .ac-row-main b{font-size:13px}
}

/* ---- Payslip: earnings on the left, deductions on the right ---- */
.ps-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;
  padding-bottom:12px;border-bottom:2px solid var(--primary)}
.ps-brand{display:flex;align-items:center;gap:12px;min-width:0}
.ps-logo{height:42px;width:auto;max-width:140px;object-fit:contain}
.ps-period{text-align:right;flex:none}
.ps-emp{display:flex;flex-direction:column;gap:10px;padding:12px 0;border-bottom:1px solid var(--border)}
.ps-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:4px 18px}
.ps-info{display:flex;justify-content:space-between;gap:8px;font-size:12px;padding:2px 0}
.ps-info span{color:var(--muted)}
.ps-info b{text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ps-cols{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:14px}
.ps-col-head{font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  color:var(--primary-ink);padding-bottom:5px;margin-bottom:4px;border-bottom:1px solid var(--border)}
.ps-net{display:flex;justify-content:space-between;align-items:center;margin-top:16px;
  /* --primary-d, not --primary: this is a printed financial figure in white
     on a solid fill. The raw gold gives white text 2.6:1, which fails even the
     large-text rule; the darker shade of the SAME gold gives 4.7:1. Layout,
     columns and figures are untouched. */
  padding:12px 16px;border-radius:12px;background:var(--primary-d);color:#fff}
.ps-net span{font-size:12px;font-weight:700;letter-spacing:.08em}
.ps-net b{font-size:19px}
.ps-words{margin-top:6px;font-size:14px;font-style:italic;color:var(--muted);text-align:right}
.ps-bank{margin-top:14px;padding-top:12px;border-top:1px solid var(--border)}
.ps-sign{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;margin-top:34px}
.ps-sign small{color:var(--muted);font-size:13px}
.ps-sign-line{border-bottom:1px solid var(--text);margin-bottom:5px;height:26px}
.ps-foot{margin-top:16px;font-size:10.5px;text-align:center}
/* A phone is too narrow for two money columns side by side. */
@media (max-width:560px){
  .ps-cols{grid-template-columns:1fr;gap:10px}
  .ps-sign{grid-template-columns:1fr;gap:22px}
  .ps-head{flex-direction:column;gap:8px}
  .ps-period{text-align:left}
}

/* Choosing which documents appear: a tick-box selection, applied in one go. */
.doc-sel-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px;
  padding:8px 10px;border:1px dashed var(--border);border-radius:10px;background:var(--bg)}
.doc-pick-sel{cursor:pointer}
.doc-pick-sel input{cursor:pointer}

/* ---- Bulk payslips: one employee per page ---------------------------------
   Hidden on screen; only used while printing. Each payslip is forced onto its
   own sheet, so "Save as PDF" produces one multi-page file with a payslip per
   page — and because it is the same markup as the single payslip view, the two
   can never look different. */
.bulk-payslips{display:none}
body.printing-bulk .bulk-payslips{display:block}
body.printing-bulk > *:not(.bulk-payslips){display:none !important}
.bulk-payslip{padding:6mm 0}
@media print{
  body.printing-bulk .bulk-payslip{
    break-after:page; page-break-after:always;
    break-inside:avoid; page-break-inside:avoid;
  }
  body.printing-bulk .bulk-payslip:last-child{break-after:auto;page-break-after:auto}
  /* Keep the filled NET PAY bar and the brand rules in the printout. */
  body.printing-bulk .ps-net{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  body.printing-bulk .ps-head{-webkit-print-color-adjust:exact;print-color-adjust:exact}
}

/* ============================================================================
   Tables that fit any screen
   ----------------------------------------------------------------------------
   A panel hides its overflow so the rounded corners stay clean. That also meant
   a table wider than the panel was SQUEEZED rather than allowed to scroll — so
   the Actions column ran out of room, its five icons wrapped onto separate
   lines, and every row grew three times taller than it needed to be. It looked
   worst on the Employees list, which has the most columns.

   Panels that contain a table now scroll sideways when the content genuinely
   needs more width. Nothing is hidden, nothing is squashed, and rows stay one
   line tall — the same behaviour on a phone, a tablet and a desktop.
   ============================================================================ */
.panel:has(> table){ overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; }
.panel > table{ min-width:100%; }

/* An action cell keeps its buttons on one line, always. */
td[style*="nowrap"] .icon-btn,
td[style*="nowrap"] .btn{ vertical-align:middle; }
.panel > table td, .panel > table th{ white-space:nowrap; }
/* …except the columns that hold real prose, which may wrap as they always did. */
.panel > table td .emp-cell b,
.panel > table td .emp-cell small{ white-space:nowrap; }

/* Narrow screens: slightly tighter rows and icons so more fits before scrolling. */
@media (max-width:760px){
  .panel > table td, .panel > table th{ padding-left:10px; padding-right:10px; }
  .panel > table .icon-btn{ width:28px !important; height:28px !important; }
  .emp-cell .avatar{ width:30px !important; height:30px !important; font-size:11px !important; }
}

/* ---- The on-screen help assistant (administrators only) ---- */
.as-log{max-height:300px;overflow-y:auto;display:flex;flex-direction:column;gap:8px;
  padding:10px;border:1px solid var(--border);border-radius:12px;background:var(--bg)}
.as-msg{padding:8px 11px;border-radius:12px;font-size:13px;line-height:1.6;max-width:88%;white-space:pre-wrap}
.as-you{align-self:flex-end;background:var(--primary);color:#fff;border-bottom-right-radius:4px}
.as-bot{align-self:flex-start;background:var(--surface);border:1px solid var(--border);border-bottom-left-radius:4px}
.as-go{align-self:flex-start;margin-top:2px}

/* ---- Drag & drop upload target highlight ---- */
.drop-on{outline:2px dashed var(--primary,#2563eb);outline-offset:3px;border-radius:12px}

/* ---- Chat assistant: structured replies instead of walls of prose ---- */
.ds2-msg p{margin:0 0 6px}
.ds2-msg p:last-child{margin-bottom:0}
.ds2-msg ol.ds2-li,.ds2-msg ul.ds2-li{margin:6px 0 6px 18px;padding:0}
.ds2-msg ol.ds2-li li,.ds2-msg ul.ds2-li li{margin:0 0 4px;line-height:1.5}
.ds2-resume{text-align:center;font-size:11px;opacity:.6;font-style:italic}

/* ---- Ameera's avatar: launcher, chat header, in-app assistant ---- */
.ds2-ask-face{width:26px;height:26px;border-radius:50%;display:block;flex:0 0 auto}
.ds2-ask-head{display:flex;align-items:center;gap:11px}
.ds2-head-face{width:44px;height:44px;border-radius:50%;flex:0 0 auto;box-shadow:0 2px 8px rgba(0,0,0,.18)}
.ds2-head-txt{display:block;min-width:0}
.ds2-head-txt b{display:block;font-size:15px}
.ds2-head-txt span{font-size:12px;opacity:.9}
.am-face{width:30px;height:30px;border-radius:50%;vertical-align:middle;margin-right:9px}
.modal-head h3{display:flex;align-items:center}
/* ICONS.ai is now her photo: size it like the sibling stroke icons it replaced. */
.am-ico{width:20px;height:20px;border-radius:50%;flex:0 0 auto;object-fit:cover;display:inline-block;vertical-align:middle}
.am-btn{padding:0;overflow:hidden}
.am-btn img{width:28px;height:28px;border-radius:50%;display:block;object-fit:cover}
.side-nav .am-ico,.nav-item .am-ico{margin-right:2px}
/* Her face beside each of her replies — reads like a real agent, not a system message. */
.ds2-msg.bot{position:relative;margin-left:34px}
.ds2-msg.bot::before{content:"";position:absolute;left:-34px;bottom:0;width:26px;height:26px;border-radius:50%;background:url(/img/ameera-64.png) center/cover no-repeat}
.ds2-msg.bot.typing::before{opacity:.55}
.ds2-chips{margin-left:34px}


/* ---- Ameera's window: one panel, three sizes ----
   Centre (default), docked mini-panel, and a collapsed pill button. Same blue
   header as the website widget so she looks like one person on both sides. */
.am-head{background:var(--primary-grad);color:#fff;display:flex;align-items:center;gap:10px;padding:12px 14px;border-radius:16px 16px 0 0}
.am-head .am-face{width:38px;height:38px;border-radius:50%;flex:0 0 auto;margin:0;box-shadow:0 2px 8px rgba(0,0,0,.2)}
.am-title{display:block;min-width:0;flex:1}
.am-title b{display:block;font-size:14.5px;line-height:1.2}
.am-title small{font-size:11.5px;opacity:.9}
.am-head .am-x{background:transparent;color:#fff;font-size:17px;line-height:1;width:26px;height:26px;border-radius:50%;flex:0 0 auto}
.am-head .am-x:hover{background:rgba(255,255,255,.18)}
.am-win .modal{border-radius:16px;overflow:hidden}

/* Docked: small live panel, bottom-right. Backdrop stops blocking the page. */
.modal-bg.am-dock{position:fixed;inset:auto 18px 18px auto;width:auto;height:auto;background:none;backdrop-filter:none;display:block;padding:0;z-index:100040;pointer-events:none}
.modal-bg.am-dock > .modal{pointer-events:auto;width:350px;max-width:92vw;max-height:62vh;display:flex;flex-direction:column;box-shadow:0 18px 50px rgba(15,23,42,.32);animation:none}
.modal-bg.am-dock .am-head{cursor:pointer;padding:9px 12px}
.modal-bg.am-dock .am-head .am-face{width:30px;height:30px}
.modal-bg.am-dock .am-title small{display:none}
.modal-bg.am-dock .modal-body{padding:10px 12px;overflow-y:auto}
.modal-bg.am-dock .as-log{max-height:190px}
.modal-bg.am-dock .chat-log{max-height:190px;min-height:120px}
.modal-bg.am-dock .modal-foot,.modal-bg.am-dock .as-eg{display:none}

/* Collapsed: a pill button in the corner, exactly like the website launcher. */
.modal-bg.am-pill{position:fixed;inset:auto 18px 18px auto;width:auto;height:auto;background:none;backdrop-filter:none;display:block;padding:0;z-index:100040;pointer-events:none}
.modal-bg.am-pill > .modal{pointer-events:auto;width:auto;max-width:none;background:var(--primary-grad);border-radius:30px;box-shadow:0 10px 26px rgba(191,155,46,.45);animation:none;padding:0}
.modal-bg.am-pill .am-head{padding:9px 16px 9px 9px;border-radius:30px;cursor:pointer}
.modal-bg.am-pill .am-head .am-face{width:30px;height:30px}
.modal-bg.am-pill .am-title b{font-size:13.5px}
.modal-bg.am-pill .am-title small,.modal-bg.am-pill #amMin,.modal-bg.am-pill #amBig{display:none}
.modal-bg.am-pill .modal-body,.modal-bg.am-pill .modal-foot{display:none}

/* Centre window: taller conversation area — the short box meant only two or
   three exchanges were visible before scrolling. Width stays as it was. */
.am-win:not(.am-dock):not(.am-pill) .chat-log{min-height:420px;max-height:64vh}
.am-win:not(.am-dock):not(.am-pill) .as-log{min-height:380px;max-height:60vh}
.am-win:not(.am-dock):not(.am-pill) > .modal{max-height:88vh;display:flex;flex-direction:column}
.am-win:not(.am-dock):not(.am-pill) > .modal > .modal-body{overflow-y:auto}

/* ---- Ameera's action confirmation card ----
   Deliberately amber and boxed: it must not look like an ordinary chat bubble,
   because clicking Confirm changes real records. */
.am-confirm{border:1px solid var(--warning,#f59e0b);background:#fffbeb;border-radius:12px;padding:11px 13px;margin:8px 0;align-self:stretch}
[data-theme="dark"] .am-confirm{background:rgba(245,158,11,.12)}
.am-confirm-t{font-size:12px;letter-spacing:.02em;text-transform:uppercase;color:#b45309;margin-bottom:4px}
.am-confirm-s{font-size:13.5px;line-height:1.5;margin-bottom:9px;color:var(--text,#1f2937)}
.am-confirm-b{display:flex;gap:8px}

/* ---- Ameera's unread-message reminder (3 Aug) ---------------------------
   The card she leaves in her chat, and the pinned copy an employee sees at the
   top of the portal (they have no assistant of their own). Deliberately a CARD
   and not an overlay: the owner asked for insistence, not for a box that
   cannot be dismissed. Line icons and her own photo only — no emoji (§3.38). */
.am-remind{border:1px solid var(--danger,#dc2626);background:rgba(220,38,38,.06);border-radius:12px;padding:11px 13px;margin:8px 0;align-self:stretch}
[data-theme="dark"] .am-remind{background:rgba(220,38,38,.14)}
.am-remind-h{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--danger,#dc2626);margin-bottom:5px}
.am-remind-face{width:22px;height:22px;border-radius:50%;flex:none}
.am-remind-t{font-size:13.5px;line-height:1.5;margin-bottom:9px;color:var(--text,#1f2937)}
.am-remind-b{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.am-remind-b .am-ack{flex:0 1 auto;max-width:260px}
/* The panel is fixed to the viewport and the portal's header is shorter than
   the admin topbar, so it gets its own offset rather than inheriting a
   --topbar-h that does not exist on this screen. */
.notif-panel-portal{top:66px;right:12px}
[dir="rtl"] .notif-panel-portal{right:auto;left:12px}

/* ---- Buttons: one branded, filled look ----------------------------------
   Every labelled action button now matches the primary style — navy fill,
   white text, white icons — instead of the outlined "ghost" variant.
   Deliberately NOT touched: inputs, textareas, selects, checkboxes, chips,
   calendar day options and cards, which keep their current design. */
.btn.ghost{background:var(--primary-grad);color:#fff;border:1px solid transparent}
.btn.ghost:hover{background:var(--primary-grad);filter:brightness(1.06)}
.btn.ghost svg{stroke:currentColor}
.btn.ghost svg *{stroke:currentColor}
.btn svg,.btn.ghost svg{color:#fff}

/* Destructive actions stay red — a Delete that looks exactly like Save is a
   trap. Matches any ghost button whose inline colour asks for danger. */
.btn.ghost[style*="danger"]{background:linear-gradient(135deg,#ef4444,#f87171);color:#fff !important;border-color:transparent}
.btn.ghost[style*="danger"]:hover{filter:brightness(1.06)}
.btn.ghost[style*="danger"] svg{color:#fff}

/* Ameera's chat keeps its own quiet buttons on the blue header. */
.am-head .icon-btn,.ds2-ask .btn.ghost{background:transparent;color:#fff;border-color:transparent}

/* ==== Add Employee as a full page (owner-approved redesign, 30 Jul) =========
 * The SAME modal element, restyled: no dark backdrop, full width, a section
 * rail on the left, the folder drop on top. `.ep-collapsed` is how the rail
 * shows one section at a time — it is a CSS-only skin over the tick-boxes'
 * inline display, which save-validation reads and which stays untouched. */
.modal-bg.emp-page{background:var(--bg);align-items:stretch;justify-content:stretch;padding:0}
.emp-page .modal{max-width:none;width:100%;max-height:none;height:100%;border-radius:0;box-shadow:none;background:var(--bg);display:flex;flex-direction:column}
.emp-page .modal-head{background:var(--surface);position:sticky;top:0;z-index:5}
.emp-page .modal-head h3{font-size:21px;font-weight:800}
.emp-page .modal-body{flex:1;overflow-y:auto;max-width:1160px;width:100%;margin:0 auto;padding:22px 26px}
.emp-page .modal-foot{background:var(--surface);position:sticky;bottom:0;z-index:5}
.emp-page .ep-layout{display:flex;gap:20px;align-items:flex-start}
.emp-page .ep-rail{width:264px;flex-shrink:0;position:sticky;top:8px}
.emp-page .ep-main{flex:1;min-width:0}
@media(max-width:880px){.emp-page .ep-layout{flex-direction:column}.emp-page .ep-rail{width:100%;position:static}}
.emp-page .emp-sec.ep-collapsed{display:none !important}
.emp-page #epOthers.ep-collapsed{display:none !important}

.ep-step{width:100%;display:flex;align-items:center;gap:11px;padding:11px 12px;margin-bottom:8px;
  border:1px solid var(--border);border-radius:13px;background:var(--surface);color:var(--text);
  cursor:pointer;font:inherit;text-align:left}
body.soft .ep-step{border:none;box-shadow:var(--nm-out-sm)}
.ep-step:hover{border-color:var(--primary)}
.ep-step.active{color:var(--primary-ink)}
body.soft .ep-step.active{box-shadow:var(--nm-in)}
.ep-dot{width:26px;height:26px;flex-shrink:0;border-radius:9px;display:flex;align-items:center;
  justify-content:center;font-size:12.5px;font-weight:800;background:#f1f5f9;color:#64748b}
.ep-step.done .ep-dot{background:#ecfdf5;color:var(--ok,#10b981)}
.ep-step.warn .ep-dot{background:#fef2f2;color:var(--danger,#ef4444)}
[data-theme="dark"] .ep-dot{background:rgba(255,255,255,.06);color:var(--muted)}
.ep-txt{display:flex;flex-direction:column;gap:1px;min-width:0}
.ep-txt b{font-size:13px}
.ep-txt small{font-size:11.5px;opacity:.75;font-weight:500}
.ep-note{margin:12px 4px 0;font-size:11.5px;line-height:1.6;color:var(--muted)}

.ep-drop{border-radius:18px;padding:22px 22px 18px;text-align:center;margin-bottom:18px;
  border:1.5px dashed color-mix(in srgb,var(--primary) 45%,transparent);background:var(--surface)}
body.soft .ep-drop{border:none;box-shadow:var(--nm-in)}
.ep-drop.over{border-style:solid;border-color:var(--primary);background:color-mix(in srgb,var(--primary) 7%,transparent)}
.ep-drop-ic{width:46px;height:46px;margin:0 auto 10px;border-radius:14px;display:flex;align-items:center;
  justify-content:center;background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary-ink)}
.ep-drop-ic svg{width:24px;height:24px}
.ep-drop h3{font-size:17px;margin:0 0 5px}
.ep-drop p{margin:0 auto 12px;max-width:640px;font-size:12.5px;line-height:1.6;color:var(--muted)}
.ep-drop-row{display:flex;gap:10px;justify-content:center;align-items:center;flex-wrap:wrap}
.ep-drop-list{display:flex;flex-direction:column;gap:7px;margin-top:12px;text-align:left;max-width:680px;margin-left:auto;margin-right:auto}
.ep-file{display:flex;align-items:center;gap:11px;padding:9px 13px;border-radius:12px;
  border:1px solid var(--border);background:var(--surface);font-size:12.5px}
body.soft .ep-file{border:none;box-shadow:var(--nm-out-sm)}
.ep-file b{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ep-file small{color:var(--muted);flex:1}
.ep-file.ok .ep-dot{background:#ecfdf5;color:var(--ok,#10b981)}

/* ==== Add Employee: split top + visible side menu (owner, 31 Jul) ==========
 * The page overlay starts AFTER the 250px sidebar on desktop, so the main
 * menu stays visible and sticky. Below 820px the sidebar is a drawer over
 * everything (z-index 40 < the overlay's 50 is fine: the drawer opens from
 * the topbar, which this page replaces — so the overlay goes back to full
 * width there and the form keeps its own ✕ to leave). */
.modal-bg.emp-page{left:250px}
@media(max-width:820px){.modal-bg.emp-page{left:0}}

/* The two equal halves: drop gateway | hiring details. */
.emp-page .ep-top{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:18px;align-items:stretch}
@media(max-width:980px){.emp-page .ep-top{grid-template-columns:1fr}}
.emp-page .ep-drop{margin-bottom:0;padding:18px 18px 16px}
.emp-page .ep-drop h3{font-size:16px}
.emp-page .ep-drop p{font-size:12px;margin-bottom:10px}
.emp-page .ep-drop-list{max-height:230px;overflow-y:auto}
.emp-page .ep-hire{min-width:0}
.emp-page .ep-hire .form-section{height:100%;margin-bottom:0}

/* The ✕ on a dropped file: removes the attachment from its section. */
.ep-rm{border:none;cursor:pointer;flex-shrink:0;width:24px;height:24px;border-radius:8px;
  display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;
  background:var(--surface);color:var(--danger,#ef4444)}
body.soft .ep-rm{box-shadow:var(--nm-out-sm)}
body.soft .ep-rm:active{box-shadow:var(--nm-in)}

/* ==== Add Employee: keep the TOPBAR visible + live read states (31 Jul) ====
 * z-index 35: below the phone sidebar drawer (40) and every normal modal (50),
 * above the page content (topbar is 20). The top offset is set at open time
 * from the real topbar height — see openEmployeeForm. */
.modal-bg.emp-page{z-index:35}

/* ==== Add/Edit Employee ON A PHONE: the page ends where the docker begins
 * (3 Aug — the owner could not save an employee edit from his phone AT ALL).
 *
 * The overlay is `position:fixed;inset:0` and only its `top` is overwritten
 * (measured from the real topbar, above), so `bottom:0` survives and the
 * overlay already ended exactly at the bottom of the viewport — it never
 * overflowed. The bug was STACKING, not sizing: the bottom docker `.m-dock`
 * is also `position:fixed;bottom:0`, 64px tall plus the iOS safe area, and
 * sits at z-index 38 against this page's 35. It therefore painted straight
 * over the sticky `.modal-foot`. Measured in a real browser with the real
 * DOM at 390x844, 360x640 and 390x430: the Save button was 100% covered and
 * `elementFromPoint` at its centre returned the docker's icon, never the
 * button — invisible AND untappable.
 *
 * So the page now occupies exactly the band between the measured topbar and
 * the top of the docker, and the form inside it becomes ONE flex column with
 * ONE scroll region. Before this, `<form>` was a block, which made
 * `.emp-page .modal-body{flex:1}` a no-op, so the body took `body.soft`'s
 * 70vh cap, overflowed the modal by ~36px and left a second scroller behind
 * a sticky footer. Everything below is inside the phone media query and
 * carries the `.emp-page` class, so DESKTOP IS BYTE-FOR-BYTE UNCHANGED. */
:root{ --m-dock-h: calc(64px + env(safe-area-inset-bottom,0px)) }
@media (max-width:820px){
  /* beats `body.soft .modal-bg{align-items:flex-end}` and the bottom-sheet
     block on specificity (0,3,1 vs 0,2,1), not on source order. */
  body.soft .modal-bg.emp-page{ bottom:var(--m-dock-h); align-items:stretch }
  body.soft .modal-bg.emp-page > .modal{
    height:100%; max-height:none; margin-top:0; border-radius:0 !important;
  }
  body.soft .modal-bg.emp-page > .modal > form{
    display:flex; flex-direction:column; flex:1 1 auto; min-height:0;
  }
  body.soft .modal-bg.emp-page .modal-body{
    max-height:none; flex:1 1 auto; min-height:0; overflow-y:auto;
  }
  body.soft .modal-bg.emp-page .modal-foot{ flex:none }
}

/* ===========================================================================
   THE EMPLOYEE FORM SCROLLED SIDEWAYS ON A PHONE (owner, open since 3 Aug)
   ---------------------------------------------------------------------------
   MEASURED, NOT REASONED (§3.10, §3.51 — guessing at this screen has cost a
   round twice). DataSync-EmpForm-Overflow-Probe.html renders the real overlay
   in a real 390x844 viewport and outlines whatever passes the edge. It showed
   the form overflowing under `body.soft` and NOT under `body.soft.m2`.

   WHY. style.css ~2825 carries a safety net — `min-width:0` plus
   `overflow-wrap:anywhere` — but every selector in it is `body.soft.m2`.
   Mobile V2 is opt-in (`ds_m2` in localStorage, off by default, app.js
   ~2500), so an ordinary phone has never had that protection. What actually
   sticks out is unbreakable content: a 40-character e-mail, `784-1988-
   1234567-1`, an international phone number, a long establishment name. A
   flex or grid item defaults to `min-width:auto`, which REFUSES to shrink
   below its content — the same root cause already fixed for the workdays row
   at ~3783, quoted there as "min-width:0 is what actually lets them shrink".

   WHAT THIS DOES *NOT* DO — deliberately. The V2 net also sets
   `overflow-x:hidden` on the scrollers. That does not make anything fit; it
   CLIPS what does not, and a cut-off Emirates ID number on a form used to
   type Emirates ID numbers is worse than a scrollbar. So only the wrapping
   half is taken. If the probe still shows overflow after this, the answer is
   to find the element and let it wrap — never to hide it.

   SCOPE — EVERY MODULE AND EVERY PAGE (owner, 5 Aug: "need this correction in
   every module and pages for this and others forms"). The first version of
   this block was scoped to `.emp-page` alone because that is the screen he
   reported and the only one I had measured. He asked for it everywhere, so
   the wrapping net below is applied to all three phone scroll regions —
   `.content` (the admin app), `.portal-body` (the employee portal) and
   `.modal-body` (every dialog and form) — which is exactly the reach the V2
   net already had. The `.emp-page` rules that follow stay as they are: they
   are specific to that overlay, not general policy.

   Everything is inside `@media (max-width:820px)`, so DESKTOP IS BYTE-FOR-BYTE
   UNCHANGED and `@media print` never sees any of it.

   ONE DELIBERATE EXCLUSION: `.rep-fit` print documents and `.rep-scroll`.
   §3.56 settled that a print document is shown at its TRUE width inside a
   horizontal scroller, like a PDF reader — re-flowing it produced a column of
   single letters (`word-break:break-word`, the same bug three times). Those
   tables must keep overflowing on purpose, so they are excluded here.
   =========================================================================== */
@media (max-width:820px){
  /* ---- the net, everywhere a phone can scroll ---- */
  body.soft .content, body.soft .portal-body, body.soft .modal-body,
  body.soft .content *, body.soft .portal-body *, body.soft .modal-body *{
    min-width:0;
  }
  /* Long unbreakable values are what actually stick out: e-mail addresses,
     784-XXXX-XXXXXXX-X, +8801712345678, file names, long company names.
     `anywhere` (not `break-word`) is what the V2 net uses — and `break-word`
     is the rule that broke words one-letter-per-line three separate times
     (§3.53, §3.56). Applied to TEXT holders only, never to layout boxes. */
  body.soft .content b, body.soft .content small, body.soft .content span,
  body.soft .content p, body.soft .content label, body.soft .content h3,
  body.soft .content h4, body.soft .content td, body.soft .content th,
  body.soft .portal-body b, body.soft .portal-body small, body.soft .portal-body span,
  body.soft .portal-body p, body.soft .portal-body label, body.soft .portal-body td,
  body.soft .modal-body b, body.soft .modal-body small, body.soft .modal-body span,
  body.soft .modal-body p, body.soft .modal-body label, body.soft .modal-body td{
    overflow-wrap:anywhere;
  }
  /* An input is a replaced element: it keeps its OWN intrinsic size (~20
     characters) whatever the box around it says, and that is wider than a
     390px phone once paddings are counted. This is the half a non-V2 phone
     never got. */
  body.soft .content .field input, body.soft .content .field select, body.soft .content .field textarea,
  body.soft .portal-body .field input, body.soft .portal-body .field select, body.soft .portal-body .field textarea,
  body.soft .modal-body .field input, body.soft .modal-body .field select, body.soft .modal-body .field textarea{
    width:100%; min-width:0; max-width:100%;
  }
  /* Rows of `white-space:nowrap` buttons (~1290) cannot break a line by
     themselves, so they simply ran off the edge. Let the ROW wrap. */
  body.soft .content .upload-row, body.soft .modal-body .upload-row,
  body.soft .content .form-section-head, body.soft .modal-body .form-section-head,
  body.soft .content .panel-head, body.soft .modal-body .panel-head{ flex-wrap:wrap }

  /* ---- THE EXCLUSION: print documents keep their true width (§3.56) ---- */
  body.soft .rep-fit, body.soft .rep-fit *,
  body.soft .rep-scroll, body.soft .rep-scroll *,
  body.soft .af-doc, body.soft .af-doc *{
    min-width:revert; overflow-wrap:revert;
  }

  /* ---- RP1a (16 Sep 2026) — THE EXCLUSION ABOVE DOES NOT EXCLUDE ANYTHING.
     `min-width:revert` returns to the BROWSER's default, not to the author
     rule further down this same file (`.rep-fit, .a4-report table,
     .af-doc table{min-width:720px}`). So the blanket
     `body.soft … .modal-body *{min-width:0}` above wins and the floor is lost.

     ⛔ MEASURED IN A REAL BROWSER, SIGNED IN, AT 390px — not reasoned:
        computed min-width  0px   (it must read 720px)
        the table           314px instead of 720px
        narrowest column    34.9px
        one row             149px tall, because every cell wraps
     That is the "column of single letters" the note at the foot of this file
     warns about, and it has been happening on every report screen.
     ⚠️ SIGNED OUT the floor still stands and the report is merely scrolled —
     which is why this was never seen from the public side of the app.

     ⛔ SCOPE: the four TABLE reports only. `:not(.af-tbl)` deliberately
     leaves openAttendanceReport and openAppForm alone — he asked for the
     application FORM to be measured first (appform = ka), and a 720px floor
     may be the wrong answer for a form. Do not widen this selector until
     those two screens have been opened and measured.

     ⚠️ This restores the width the stylesheet already intended. It is a
     REPAIR, not a new decision — his decision at the foot of this file
     ("a print document is not re-flowed to fit a phone … scrolled sideways")
     is what it puts back into effect. ---- */
  body.soft .rep-fit,
  body.soft .a4-report table:not(.af-tbl){
    min-width:720px;
  }

  /* ---- AF2 (17 Sep 2026) — HIS CONDITION ON `ka`: "ensured any writing
     should not cutting out from the border edge".

     ⛔ MEASURED AT 390px, SIGNED IN — not reasoned. Two elements, and only
     these two, ran off the right edge of the A4 sheet:
        .af-ref                 +36.3px past the edge
        .af-approve (the box)   +29.5px past the edge
     The other four report screens were scanned the same way and had NOTHING
     cut outside their tables.

     1  .af-ref is `white-space:nowrap` (style.css:920). On a sheet of paper
        that is right — the employee line should not break. On a 310px sheet
        the line cannot break AT ALL, so it simply leaves the page, and
        "Employee: SAMPLE EMPLOYEE ONE" reads as "Employee: SAMPLE EMPLOYEE O".
        `overflow-wrap:anywhere` is there for the long unbroken strings that
        have no space to break at — a 30-character name, a passport number.

     2  .af-approve is a three-column grid (style.css:932). Three boxes in
        310px is ~96px each, and "Signature: ______________" cannot shrink
        below its own min-content, so the grid overflows. One column per row
        on a phone; the boxes keep their size and their order.

     ⚠️ A WIDE TABLE THAT SCROLLS SIDEWAYS IS NOT A CUT, and nothing here
     touches one. He answered `ka`: the attendance report KEEPS the width it
     has today. No floor is added, none is removed.

     ⚠️ THE PRINTED PAGE IS NOT AFFECTED. This whole block is
     `@media (max-width:820px)`, which @media print never sees (the note at
     the head of this block), so the A4 he prints keeps its nowrap employee
     line and its three approval boxes side by side. ---- */
  .af-doc .af-ref{
    white-space:normal;
    overflow-wrap:anywhere;
  }
  .af-doc .af-approve{
    grid-template-columns:1fr;
  }

  /* ---- RP1d (18 Sep 2026) — HIS REPORT: "see the header of the report.
     it's scrolling. right to left, left to right … All the information you
     set in left place and the right top right place is empty."

     ⛔ MEASURED AT 390px, SIGNED IN. Two separate faults, both older than
     RP1b and older than AF2 (checked against the RP1a stylesheet: identical):

     1 THE HEADER TRAVELS WITH THE TABLE. `.a4-head` lives inside
       `.modal-body`, and `.modal-body` is what scrolls sideways — his own
       decision, a few rules above this one. Swipe the table 400px and the
       header goes from x 38..352 to x -362..-48. VISIBLE AFTERWARDS: 0px.
       The company name, the report name and the date leave the screen, and
       the top of the sheet is blank. `position:sticky; left:0` pins it to
       the edge he is looking at, and costs nothing anywhere else.

     2 "Emirates ID" IS BROKEN MID-WORD — "Emirat" + "es ID". `.a4-head` is
       `justify-content:space-between`, so a long company name on the left
       squeezes the right-hand block to 46px, and the long-string safety net
       (`body.soft .modal-body b{overflow-wrap:anywhere}`) then breaks the
       word to fit it. 46px cannot hold "Emirates" at any size.
       The right block is told not to shrink; the LEFT block is told it may
       (`min-width:0`), because a company name wraps at spaces and a
       document type has none to wrap at.

     ✅ MEASURED AFTER: right block 46px -> 74px · "Emirates ID" 2 lines ->
     1 line · header visible after a 400px swipe 0px -> 314px.

     ⚠️ THE PRINTED PAGE AND THE DESKTOP DO NOT MOVE. This is inside
     @media (max-width:820px), which @media print never sees. ---- */
  .a4-report .modal-body > .a4-head{
    position:sticky;
    left:0;
  }
  .a4-report .a4-head > div:first-child{
    min-width:0;
  }
  .a4-report .a4-head > div:last-child{
    flex:0 0 auto;
  }
  .a4-report .a4-head > div:last-child b{
    overflow-wrap:normal;
    white-space:nowrap;
  }

  /* Nothing in this form may refuse to shrink. */
  body.soft .modal-bg.emp-page .modal-body,
  body.soft .modal-bg.emp-page .ep-layout,
  body.soft .modal-bg.emp-page .ep-main,
  body.soft .modal-bg.emp-page .ep-top,
  body.soft .modal-bg.emp-page .ep-rail,
  body.soft .modal-bg.emp-page .form-section,
  body.soft .modal-bg.emp-page .field,
  body.soft .modal-bg.emp-page .upload-row,
  body.soft .modal-bg.emp-page .doc-picker,
  body.soft .modal-bg.emp-page .doc-list{ min-width:0 }

  /* Long unbreakable values are the thing that actually sticks out: e-mail
     addresses, 784-XXXX-XXXXXXX-X, +8801712345678, file names. Break them
     rather than push the page wider. `anywhere` (not `break-word`) is what
     the V2 net already uses, so the two cannot look different. */
  body.soft .modal-bg.emp-page .field label,
  body.soft .modal-bg.emp-page .field small,
  body.soft .modal-bg.emp-page .form-section-head,
  body.soft .modal-bg.emp-page .doc-pick,
  body.soft .modal-bg.emp-page .doc-pick span,
  body.soft .modal-bg.emp-page .ep-drop h3,
  body.soft .modal-bg.emp-page .ep-drop p,
  body.soft .modal-bg.emp-page .ep-txt b,
  body.soft .modal-bg.emp-page .ep-txt small,
  body.soft .modal-bg.emp-page .upload-name,
  body.soft .modal-bg.emp-page .muted,
  body.soft .modal-bg.emp-page .badge{ min-width:0; overflow-wrap:anywhere }

  /* An input is a replaced element: it keeps its OWN default size (~20
     characters) no matter what the box around it says, and that default is
     wider than a 390px phone once the paddings are counted. This is the
     `.field input` half of the V2 block, which a non-V2 phone never got. */
  body.soft .modal-bg.emp-page .field input,
  body.soft .modal-bg.emp-page .field select,
  body.soft .modal-bg.emp-page .field textarea{ width:100%; min-width:0; max-width:100% }

  /* The two-button row inside the drop gateway, and the section header that
     carries "Choose documents" + "Manage types": both are flex rows of
     `white-space:nowrap` buttons (~1290), so they cannot break a line by
     themselves and simply ran off the edge. */
  body.soft .modal-bg.emp-page .ep-drop-row,
  body.soft .modal-bg.emp-page .form-section-head{ flex-wrap:wrap }

  /* Breathing room back: 26px each side of a 390px screen is 13% of it. */
  body.soft .modal-bg.emp-page .modal-body{ padding-left:14px; padding-right:14px }
}
.ep-spin{display:inline-block;width:12px;height:12px;border-radius:50%;
  border:2px solid color-mix(in srgb,var(--primary) 30%,transparent);border-top-color:var(--primary);
  animation:ep-spin .7s linear infinite}
@keyframes ep-spin{to{transform:rotate(360deg)}}

/* ==== Employees list: Document File meter, ⋯ menu, profile window (31 Jul) == */
.doc-meter{display:inline-flex;gap:3px;vertical-align:middle}
.doc-seg{width:16px;height:7px;border-radius:4px;background:#d7dbe7}
[data-theme="dark"] .doc-seg{background:rgba(255,255,255,.12)}
.doc-seg.g{background:var(--ok,#10b981)}
.doc-seg.a{background:var(--warn,#f59e0b)}
.doc-seg.r{background:var(--danger,#ef4444)}
.emp-row{cursor:pointer}
.emp-fix{background:var(--primary-grad,linear-gradient(135deg,#8A7021,#BF9B2E));color:#fff;font-weight:700}
.emp-menu{position:absolute;right:6px;top:38px;z-index:45;background:var(--surface);border:1px solid var(--border);
  border-radius:13px;box-shadow:0 10px 30px rgba(15,23,42,.18);padding:6px;z-index:30;min-width:190px;
  display:none;text-align:left}
body.soft .emp-menu{border:none;box-shadow:var(--nm-out)}
.emp-menu.open{display:block}
.emp-menu button{display:flex;align-items:center;width:100%;gap:9px;border:none;background:none;cursor:pointer;
  padding:9px 11px;border-radius:9px;font-size:13px;text-align:left;color:var(--text);font-family:inherit}
.emp-menu button:hover{background:color-mix(in srgb,var(--primary) 8%,transparent)}
.emp-menu button svg{width:15px;height:15px;flex-shrink:0}
.chip-filter{border:none;cursor:pointer;border-radius:20px;padding:8px 14px;font-size:12.5px;font-weight:700;
  background:var(--surface);border:1px solid var(--border)}
body.soft .chip-filter{border:none;box-shadow:var(--nm-out-sm)}
.chip-filter.amber{color:#d97706}.chip-filter.red{color:var(--danger,#ef4444)}
.chip-filter.on{box-shadow:var(--nm-in) !important}

.pf-body{display:flex;gap:20px;align-items:flex-start}
@media(max-width:760px){.pf-body{flex-direction:column}}
.pf-card{width:230px;flex-shrink:0;text-align:center}
.pf-photo{width:130px;height:130px;border-radius:50%;margin:0 auto 12px;position:relative;overflow:hidden;
  background:var(--primary-grad,linear-gradient(135deg,#8A7021,#BF9B2E));color:#fff;display:flex;align-items:center;
  justify-content:center;font-size:38px;font-weight:800}
body.soft .pf-photo{box-shadow:var(--nm-out-sm)}
.pf-card h4{font-size:17px;margin:0 0 2px}
.pf-role{color:var(--muted);font-size:13px;margin-bottom:10px}
.pf-meta{text-align:left;font-size:12.5px;color:var(--muted);line-height:1.9;margin-top:12px}
.pf-meta b{color:var(--text)}
.pf-vault{flex:1;min-width:0}
.pf-vault h4{font-size:14px;margin:0 0 10px}
.pf-vrow{display:flex;align-items:center;gap:12px;padding:11px 13px;border-radius:13px;
  border:1px solid var(--border);margin-bottom:8px;background:var(--surface)}
body.soft .pf-vrow{border:none;box-shadow:var(--nm-out-sm)}
.pf-vrow .ic{width:34px;height:34px;border-radius:10px;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary-ink)}
.pf-vrow .ic svg{width:16px;height:16px}
.pf-vrow .t{flex:1;min-width:0}
.pf-vrow .t b{font-size:13px;display:block}
.pf-vrow .t small{color:var(--muted);font-size:11.5px}

/* ==== Attendance today dashboard (owner-approved, 31 Jul) ==== */
.att-grid{display:grid;grid-template-columns:1.2fr .8fr;gap:14px;margin-bottom:14px}
@media(max-width:900px){.att-grid{grid-template-columns:1fr}}
.att-rrow{display:flex;align-items:center;gap:11px;padding:10px 2px;border-top:1px solid var(--border)}
.att-rrow:first-of-type{border-top:none}
.att-rrow .t{flex:1;min-width:0}
.att-rrow .t b{font-size:13px;display:block}
.att-rrow .t small{color:var(--muted);font-size:11.5px}
.att-bar{height:9px;border-radius:5px;overflow:hidden;display:flex;margin:10px 0 8px;background:var(--bg)}
body.soft .att-bar{box-shadow:var(--nm-in)}
.att-bar i{display:block;height:100%}
.att-pins{display:flex;gap:7px;flex-wrap:wrap;margin-top:10px}

/* ==== Payroll runway + exceptions (owner-approved, 31 Jul) ==== */
.pay-stages{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:12px}
@media(max-width:900px){.pay-stages{grid-template-columns:repeat(2,1fr)}}
.pay-stage{border:1px solid var(--border);cursor:pointer;text-align:left;font:inherit;color:var(--text);
  border-radius:14px;padding:11px 13px;background:var(--surface);display:flex;gap:10px;align-items:center}
body.soft .pay-stage{border:none;box-shadow:var(--nm-out-sm)}
body.soft .pay-stage:active{box-shadow:var(--nm-in)}
.pay-stage .n{width:26px;height:26px;border-radius:9px;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;font-weight:800;font-size:12px;background:#f1f5f9;color:#64748b}
.pay-stage.done .n{background:#ecfdf5;color:var(--ok,#10b981)}
.pay-stage.warn .n{background:#fef2f2;color:var(--danger,#ef4444)}
.pay-stage b{font-size:12.5px;display:block}
.pay-stage small{color:var(--muted);font-size:10.5px}
.pay-ex{display:flex;align-items:center;gap:11px;padding:10px 12px;border-radius:13px;
  border:1px solid var(--border);margin-bottom:8px;background:var(--surface)}
body.soft .pay-ex{border:none;box-shadow:var(--nm-out-sm)}
.pay-ex .n{width:26px;height:26px;border-radius:9px;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;font-weight:800;font-size:12.5px;background:#fef2f2;color:#dc2626}
.pay-ex .n.a{background:#fffbeb;color:#d97706}
.pay-ex .t{flex:1;min-width:0}
.pay-ex .t b{font-size:13px;display:block}
.pay-ex .t small{color:var(--muted);font-size:11.5px}

/* ==== Org Chart: photos, IDs, clickable cards (owner, 31 Jul) ==== */
.org-card{cursor:pointer;transition:transform .15s ease}
.org-card:hover{transform:translateY(-2px)}
.org-card .avatar.og-av{position:relative;overflow:hidden}
.org-card .og-code{display:block;color:var(--primary-ink);font-weight:700;font-size:11.5px;margin-top:2px}

/* ============================================================
   MOBILE — phones and small tablets (1 Aug)
   Everything here lives inside a media query, so the desktop
   layout is untouched. Nothing is hidden or removed: the same
   rows, the same columns, the same actions — re-stacked so a
   thumb can reach them.
   ============================================================ */
@media (max-width:820px){

  /* ---- Tables become cards -------------------------------------------
     Keyed off .m-cards, which app.js adds — NOT :has(), which older Android
     WebViews do not support. With :has() those phones applied half these rules
     and kept the old scroll box: a broken half-state on the cheapest devices.

     Every box below sets min-width:0. A flex child refuses to shrink below its
     own content by default, so without it long names and nested .emp-cell
     blocks overflowed the card and were CUT instead of wrapping. That was the
     clipped text. */
  body.soft .panel.m-cards-box{ overflow:visible !important; background:transparent !important; box-shadow:none !important; padding:0 !important; }
  body.soft table.m-cards{ display:block; width:100%; }
  body.soft table.m-cards > thead{ display:none; }
  body.soft table.m-cards > tbody{ display:block; }
  body.soft table.m-cards > tbody > tr{
    display:block; background:var(--surface); border-radius:16px;
    box-shadow:var(--nm-out-sm); padding:12px 14px; margin-bottom:10px;
  }
  body.soft table.m-cards > tbody > tr > td{
    display:flex; align-items:flex-start; gap:10px; border:none !important;
    padding:5px 0; font-size:13.5px; min-height:0; min-width:0;
    white-space:normal; word-break:break-word; overflow-wrap:anywhere;
  }
  /* The column name. Narrower than before (38%) and allowed to wrap, so it
     never squeezes the value it is describing. */
  body.soft table.m-cards > tbody > tr > td[data-label]::before{
    content:attr(data-label); flex:0 0 38%; max-width:38%; min-width:0;
    font-size:11px; font-weight:700; letter-spacing:.02em; padding-top:1px;
    text-transform:uppercase; color:var(--muted); line-height:1.4;
    white-space:normal; overflow-wrap:anywhere;
  }
  /* The value side must be allowed to shrink and wrap. Without min-width:0 on
     these nested blocks the text is clipped rather than wrapped. */
  body.soft table.m-cards > tbody > tr > td > *{ min-width:0; max-width:100%; }
  body.soft table.m-cards > tbody > tr > td .emp-cell{ flex:1; min-width:0; }
  body.soft table.m-cards > tbody > tr > td .emp-cell > div{ min-width:0; overflow-wrap:anywhere; }
  body.soft table.m-cards > tbody > tr > td b,
  body.soft table.m-cards > tbody > tr > td small,
  body.soft table.m-cards > tbody > tr > td span{ white-space:normal; overflow-wrap:anywhere; }
  /* The first cell is the subject of the row — full width, no label. */
  body.soft table.m-cards > tbody > tr > td:first-child{
    padding-bottom:9px; margin-bottom:5px; border-bottom:1px solid var(--border) !important;
    font-size:14.5px;
  }
  body.soft table.m-cards > tbody > tr > td:first-child::before{ content:none }
  body.soft table.m-cards > tbody > tr > td:empty{ display:none }
  /* Buttons inside a card get their own line and stay reachable. */
  body.soft table.m-cards > tbody > tr > td .btn{ min-height:38px }
  /* An empty-state row must not pretend to be a card. */
  body.soft table.m-cards > tbody > tr:has(> td[colspan]){ box-shadow:none; background:transparent; padding:0 }
  body.soft table.m-cards > tbody > tr > td[colspan]{ display:block }
  body.soft table.m-cards > tbody > tr > td[colspan]::before{ content:none }

  /* ---- Reach: nothing important smaller than a fingertip ---- */
  body.soft .btn, body.soft .chip, body.soft select, body.soft .icon-btn{ min-height:44px }
  body.soft .btn.sm, body.soft .chip.sm{ min-height:38px }
  /* …except the header. This rule sits later in the file than the topbar block,
     so without an exception it re-inflated those seven controls back to 44px and
     re-created the cramped bar it was meant to fix. Header controls are
     secondary and the bar now wraps, so 36px there is deliberate. */
  body.soft .topbar .chip, body.soft .topbar .icon-btn,
  body.soft .topbar select, body.soft .topbar .hamburger{ min-height:36px }
  body.soft .field input, body.soft .field select, body.soft .field textarea{
    min-height:48px; font-size:16px;   /* 16px stops iOS zooming the page on focus */
  }

  /* ---- Filter bars scroll sideways instead of stacking into a wall ---- */
  body.soft .toolbar{ flex-wrap:nowrap; overflow-x:auto; overscroll-behavior-x:contain; padding-bottom:8px }
  body.soft .toolbar::-webkit-scrollbar{ height:0 }
  body.soft .toolbar > *{ flex:none }

  /* ---- Page headings ---- */
  body.soft .page-head h2{ font-size:24px; letter-spacing:-.02em }

  /* ---- Modals sit at the bottom, where the thumb is ---- */
  body.soft .modal{ max-width:100% !important; width:100%; border-radius:22px 22px 0 0 !important; margin-top:auto }
  body.soft .modal-bg{ align-items:flex-end }
  /* A CAP WITHOUT A SCROLLER IS NOT A CAP (owner, 11 Aug — the action bar
     floated in the middle of every dialog on his phone, in all sections).
     `max-height` alone stops the BOX at 70vh; the content is not told to
     scroll, so it overflows VISIBLY and the sibling `.modal-foot` — laid out
     immediately after that 70vh box — lands two thirds of the way down the
     screen with the list painting straight through it, above and below.
     `position:sticky` on the footer then glues it there.
     The Add/Edit Employee page never showed this because it overrides
     `max-height:none; overflow-y:auto` for itself at ~2325 (3 Aug, §3.51).
     That fix was scoped to one screen; this is the same fault everywhere else.
     Proved by modalfoot-test.mjs, which reads the COMPUTED value rather than
     trusting the shape of this rule. */
  body.soft .modal-body{ max-height:70vh; overflow-y:auto }
  body.soft .modal-foot{ position:sticky; bottom:0; background:var(--surface) }
  body.soft .modal-foot .btn{ flex:1 }

  /* ---- Wide reports are downloaded, not previewed (owner, 1 Aug) ----
     A 14-column report cannot be read on a phone. Every option, filter and
     column stays exactly where it was — only the on-screen preview of the
     wide table is replaced by a plain sentence and the download buttons. */
  body.soft .m-wide-note{ display:flex }
  body.soft .m-nopreview{ display:none !important }
}
@media (min-width:821px){ .m-wide-note{ display:none } }
.m-wide-note{
  gap:11px; align-items:flex-start; padding:13px 15px; border-radius:16px;
  margin:10px 0; font-size:12.5px; line-height:1.55; color:var(--muted);
  box-shadow:var(--nm-in);
}

/* ---- Bottom docker: five destinations, always reachable ---------------
   The drawer stays (it holds every module). The docker is the five a
   person actually uses, so the common path is one tap instead of three. */
.m-dock{ display:none }
@media (max-width:820px){
  .m-dock{
    display:flex; position:fixed; left:0; right:0; bottom:0; z-index:38;
    /* --m-dock-h is declared beside the Add/Edit Employee page rules, which
       size themselves off it so the two can never drift apart. */
    height:var(--m-dock-h);
    padding:6px 4px env(safe-area-inset-bottom,0px);
    background:var(--surface); border-top:1px solid var(--border);
  }
  .m-dock .m-it{
    flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:3px; font-size:10px; font-weight:600; color:var(--muted);
    background:none; border:none; cursor:pointer; padding:4px 2px; min-width:0;
  }
  .m-dock .m-it svg, .m-dock .m-it img{ width:22px; height:22px }
  .m-dock .m-it span{ max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
  .m-dock .m-it.on{ color:var(--primary-ink); font-weight:700 }
  /* The content must not end underneath the docker. */
  body.soft .content{ padding-bottom:calc(78px + env(safe-area-inset-bottom,0px)) }
  body.soft .am-dock, body.soft .ameera-dock{ bottom:calc(72px + env(safe-area-inset-bottom,0px)) }
}

/* ============================================================
   MOBILE V2 — the new layout, OFF by default (1 Aug)
   Turned on per device with ?m2=1 and remembered; ?m2=0 turns it
   off again. Everything is scoped to body.m2 inside the phone
   media query, so a device without the flag — and every desktop —
   is byte-for-byte unaffected.

   Structure follows the approved V2 design: coloured header with a
   large title and three actions, hero card, dense rows, tile menu
   under pale bands, grouped stat grid, docker with a centre action.
   Colours are the existing soft-emboss palette throughout.
   ============================================================ */
@media (max-width:820px){

  /* ---- Header: LIGHT, one large title, pill controls (owner's sample) ----
     The first V2 pass used a coloured gradient bar. His sample shows a light
     header on the page colour, with the title large and dark and the controls
     as soft pills on the right. This follows the sample. */
  body.soft.m2 .topbar{
    background:var(--bg); border-bottom:none; padding:12px 16px 6px;
    gap:9px; row-gap:8px;
  }
  body.soft.m2 .topbar h1#pageTitle{
    color:var(--text); font-size:26px; font-weight:800; letter-spacing:-.03em;
    flex:1 1 100%; order:9; min-width:0; overflow-wrap:anywhere; white-space:normal;
    margin:4px 0 0;
  }
  body.soft.m2 .topbar .icon-btn{
    width:38px !important; height:38px !important; min-height:38px !important;
    flex:none !important; padding:0 !important; border-radius:50%; border:none !important;
    background:var(--surface) !important; color:var(--muted) !important;
    box-shadow:var(--nm-out-sm) !important; overflow:hidden;
  }
  /* The bell's icon and Ameera's photo both carry their own dimensions, which
     is why they came out enormous on the phone. Both are pinned here. */
  body.soft.m2 .topbar .icon-btn svg{width:18px !important; height:18px !important}
  body.soft.m2 .topbar .am-btn img, body.soft.m2 .topbar .am-ico{
    width:26px !important; height:26px !important;
  }
  body.soft.m2 .topbar .notif-badge{
    top:-2px; right:-2px; min-width:16px; height:16px; font-size:10px; border-width:2px;
  }
  /* The drawer button showed the word "Menu" in a pill twice the height of the
     rest. Icon only on a phone — the docker already says Menu. */
  body.soft.m2 .topbar .ham-label{display:none}
  body.soft.m2 .topbar .hamburger{
    background:var(--surface) !important; color:var(--primary-ink) !important;
    box-shadow:var(--nm-out-sm) !important; border-radius:14px;
  }
  body.soft.m2 .topbar #userBtn{
    background:var(--surface) !important; box-shadow:var(--nm-out-sm) !important;
    padding:3px 7px; border-radius:20px;
  }
  body.soft.m2 .topbar #userBtn .avatar{width:28px !important;height:28px !important;font-size:11px !important}

  /* The controls, once they live in the drawer. */
  .m2-extra{display:flex;flex-wrap:wrap;gap:8px;padding:12px 14px;border-top:1px solid var(--border)}
  .m2-extra > *{flex:1 1 auto;min-width:0}

  /* ---- Content lifts over the coloured bar ---- */
  body.soft.m2 .content{padding-top:6px}
  /* The screenshots showed the page title TWICE — small in the header and huge
     below. The real markup is `.page-head > div > h2`, and my earlier rule for
     it never matched because a later rule re-set the size. The header keeps the
     title; the page-head's copy is removed from the layout entirely. */
  body.soft.m2 .content > .page-head > div > h2,
  body.soft.m2 .page-head h2{
    display:none !important;
  }
  body.soft.m2 .page-head > div > .muted{
    font-size:13px; color:var(--muted); display:block; overflow-wrap:anywhere;
  }
  body.soft.m2 .page-head{
    padding:0 2px 6px; margin:0 0 12px; background:none; box-shadow:none;
    display:flex; align-items:center; gap:9px; flex-wrap:wrap; position:static;
  }
  /* Every block keeps its own space. The overlapping cards in the screenshot
     were panels with negative or zero margins stacking on each other. */
  body.soft.m2 .content > .panel,
  body.soft.m2 .content > .card,
  body.soft.m2 .content > .cards,
  body.soft.m2 .content > .form-section{ margin:0 0 12px !important; position:static }

  /* ---- Dense rows: label left, value right, never clipped ---- */
  body.soft.m2 table.m-cards > tbody > tr{padding:13px 14px;border-radius:16px}
  body.soft.m2 table.m-cards > tbody > tr > td{
    justify-content:space-between;align-items:baseline;gap:14px;padding:5px 0;font-size:13.5px;font-weight:600;
  }
  body.soft.m2 table.m-cards > tbody > tr > td[data-label]::before{
    flex:0 1 auto;max-width:52%;font-size:11.5px;font-weight:700;color:var(--muted);
    text-transform:uppercase;letter-spacing:.02em;
  }
  body.soft.m2 table.m-cards > tbody > tr > td:first-child{
    font-size:15.5px;font-weight:700;padding-bottom:10px;margin-bottom:6px;
  }

  /* ---- Stat grid: big light numerals, coloured underline ---- */
  body.soft.m2 .stat{
    padding:14px 8px 12px;text-align:center;border-radius:14px;
    border-bottom:3px solid var(--primary);
  }
  body.soft.m2 .stat b, body.soft.m2 .stat .stat-v{font-weight:300;font-size:26px;line-height:1.1}
  body.soft.m2 .stat small, body.soft.m2 .stat .stat-k{
    font-size:10.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
    color:var(--muted);overflow-wrap:anywhere;
  }

  /* ---- Drawer becomes a tile menu under pale bands ---- */
  body.soft.m2 .sidebar{width:100%;max-width:100%}
  body.soft.m2 .sidebar .nav-group > h6{
    background:color-mix(in srgb,var(--primary) 9%,transparent);
    border-radius:12px;padding:10px 14px;margin:14px 12px 10px;
    font-size:13px;font-weight:700;color:var(--primary-ink);letter-spacing:0;text-transform:none;
  }
  body.soft.m2 .sidebar .nav-group{display:block}
  body.soft.m2 .sidebar .nav-group > .nav-item,
  body.soft.m2 .sidebar .nav-group > .nav-parent{
    display:inline-flex;flex-direction:column;align-items:center;justify-content:flex-start;
    width:calc(33.333% - 10px);margin:0 4px 10px;padding:12px 4px;vertical-align:top;
    text-align:center;gap:7px;border-radius:14px;
  }
  body.soft.m2 .sidebar .nav-group > .nav-item svg{width:22px;height:22px}
  body.soft.m2 .sidebar .nav-group > .nav-item span{
    font-size:11.5px;line-height:1.3;overflow-wrap:anywhere;white-space:normal;
  }

  /* ---- Docker: centre action ---- */
  body.soft.m2 .m-dock{background:var(--surface)}
  body.soft.m2 .m-dock .m-it:nth-child(3){visibility:hidden}
  .m2-fab{
    position:fixed;left:50%;transform:translateX(-50%);
    bottom:calc(30px + env(safe-area-inset-bottom,0px));
    width:56px;height:56px;border-radius:50%;border:4px solid var(--surface);
    background:var(--primary-grad);color:#fff;font-size:0;z-index:39;cursor:pointer;
    display:flex;align-items:center;justify-content:center;
    box-shadow:0 6px 18px rgba(109,110,113,.4);
  }
  .m2-fab img{width:30px;height:30px;border-radius:50%;display:block}
}
.m2-fab{display:none}
@media (max-width:820px){ body.soft.m2 .m2-fab{display:flex} }

/* ============================================================
   MOBILE V2 — FULL COVERAGE (1 Aug)
   The first V2 pass only reached the header and table screens.
   This covers every other kind of page the app actually renders:
   forms, modals, cards, stat grids, filter bars, the employee
   portal, payslips, calendars, timesheets, chat, org chart and
   the assorted list shells.
   Still CSS only, still scoped to body.m2 inside the phone media
   query, still the existing palette. No wiring, no logic.
   ============================================================ */
@media (max-width:820px){

  /* ---- Cards and panels: one shape everywhere ---- */
  body.soft.m2 .card, body.soft.m2 .panel, body.soft.m2 .form-section,
  body.soft.m2 .callout, body.soft.m2 .req-card, body.soft.m2 .org-card{
    border-radius:16px !important; padding:14px 15px;
  }
  body.soft.m2 .panel-head{
    padding:12px 14px; font-size:14.5px; font-weight:700;
    display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  }
  body.soft.m2 .panel-head > *{min-width:0}
  body.soft.m2 .empty{padding:26px 16px; font-size:13.5px; text-align:center; color:var(--muted)}

  /* ---- Any two-column grid becomes one column ---- */
  body.soft.m2 .grid-2, body.soft.m2 .form-grid, body.soft.m2 .org-grid{
    grid-template-columns:1fr !important; gap:11px;
  }
  /* Stat tiles stay two across — the numbers are short and it reads better. */
  body.soft.m2 .cards{grid-template-columns:1fr 1fr !important; gap:10px}
  body.soft.m2 .cards > *{min-width:0}

  /* ---- Forms: full width, thumb-sized, labels that never clip ---- */
  body.soft.m2 .field{width:100% !important; min-width:0}
  body.soft.m2 .field > label, body.soft.m2 .label{
    font-size:12px; font-weight:700; letter-spacing:.02em; text-transform:uppercase;
    color:var(--muted); margin-bottom:6px; display:block; overflow-wrap:anywhere;
  }
  body.soft.m2 .field input, body.soft.m2 .field select, body.soft.m2 .field textarea{
    width:100%; min-height:50px; font-size:16px; border-radius:13px;
  }
  body.soft.m2 .field textarea{min-height:96px}
  body.soft.m2 .field small{font-size:11.5px; line-height:1.5; display:block; margin-top:5px; overflow-wrap:anywhere}
  body.soft.m2 .upload-row{flex-wrap:wrap; gap:8px}
  body.soft.m2 .upload-row .btn{flex:1 1 auto}
  body.soft.m2 .upload-name{flex:1 1 100%; font-size:12px; overflow-wrap:anywhere}

  /* ---- Buttons: readable labels, never a squeezed word ---- */
  body.soft.m2 .btn{
    font-size:14.5px; font-weight:700; padding:0 16px; border-radius:13px;
    white-space:normal; line-height:1.25; min-width:0; overflow-wrap:anywhere;
  }
  body.soft.m2 .btn.sm{font-size:13px; padding:0 12px}
  body.soft.m2 .panel-head .btn, body.soft.m2 .page-head .btn{flex:0 1 auto}

  /* ---- Modals: a bottom sheet with a V2 header ---- */
  body.soft.m2 .modal-head{
    background:var(--surface); border:none;
    padding:16px 16px 12px; border-radius:22px 22px 0 0;
  }
  body.soft.m2 .modal-head h3{font-size:20px; font-weight:800; letter-spacing:-.02em; min-width:0; overflow-wrap:anywhere}
  body.soft.m2 .modal-body{padding:15px 15px 18px}
  body.soft.m2 .modal-foot{padding:12px 14px calc(14px + env(safe-area-inset-bottom,0px)); gap:9px}

  /* ---- Filter bars: one scrolling line ---- */
  body.soft.m2 .toolbar{gap:7px; padding:2px 0 9px}
  body.soft.m2 .toolbar .chip, body.soft.m2 .toolbar .chip-filter,
  body.soft.m2 .toolbar input, body.soft.m2 .toolbar select{
    min-height:40px; font-size:13px; border-radius:12px;
  }
  body.soft.m2 .chip-filter{padding:0 13px; font-weight:600}

  /* ---- Badges ---- */
  body.soft.m2 .badge, body.soft.m2 .tag{
    font-size:11px; font-weight:800; padding:4px 9px; border-radius:8px; white-space:nowrap;
  }

  /* ---- Key/value rows (used on ~41 screens) ---- */
  body.soft.m2 .p-doc{
    padding:11px 0; font-size:14px; gap:14px; align-items:baseline;
  }
  body.soft.m2 .p-doc span{min-width:0; overflow-wrap:anywhere}
  body.soft.m2 .p-doc span:first-child{
    font-size:11.5px; font-weight:700; text-transform:uppercase; letter-spacing:.02em; flex:0 1 auto;
  }
  body.soft.m2 .p-doc span:last-child{text-align:right; font-weight:600}

  /* ---- THE EMPLOYEE PORTAL — a separate render path, now covered ---- */
  body.soft.m2 .portal-bar{
    background:var(--bg); border-bottom:none;
    padding:12px 14px; flex-wrap:wrap; row-gap:9px;
  }
  body.soft.m2 .portal-bar b, body.soft.m2 .portal-bar h1, body.soft.m2 .portal-bar strong{
    font-size:24px; font-weight:800; letter-spacing:-.02em; min-width:0; overflow-wrap:anywhere;
  }
  body.soft.m2 .portal-bar .icon-btn, body.soft.m2 .portal-bar .chip{
    background:var(--surface) !important; border:none !important;
    box-shadow:var(--nm-out-sm) !important; min-height:38px; border-radius:20px;
  }
  body.soft.m2 .portal-bar .brand-logo{max-height:30px; width:auto}
  body.soft.m2 .portal-tabs{
    top:var(--topbar-h,56px); gap:18px; padding:0 14px; background:var(--surface);
  }
  body.soft.m2 .portal-tabs > *{
    padding:12px 0 10px; font-size:13px; font-weight:700; white-space:nowrap;
    border-bottom:3px solid transparent;
  }
  body.soft.m2 .portal-tabs > .active, body.soft.m2 .portal-tabs > .on{
    color:var(--primary-ink); border-bottom-color:var(--primary);
  }
  body.soft.m2 .portal-body{padding:14px}
  body.soft.m2 .p-employer{
    border-radius:16px; padding:14px 15px; margin-bottom:12px;
    box-shadow:0 6px 18px rgba(16,24,40,.12);
  }
  body.soft.m2 .portal-drawer{width:100%; max-width:100%}
  body.soft.m2 .portal-nav a, body.soft.m2 .portal-nav button, body.soft.m2 .portal-nav .nav-item{
    min-height:50px; font-size:15px; border-radius:13px;
  }

  /* ---- Payslip ---- */
  body.soft.m2 .pay-top{flex-direction:column; gap:12px}
  body.soft.m2 .pay-table td{padding:11px 2px; font-size:14px; overflow-wrap:anywhere}
  body.soft.m2 .pay-total td{font-size:17px; font-weight:800}
  body.soft.m2 .pay-stages{flex-wrap:wrap; gap:7px}

  /* ---- Calendar ---- */
  body.soft.m2 .cal-grid{gap:3px}
  body.soft.m2 .cal-cell, body.soft.m2 .cal-d{min-height:40px; font-size:12.5px; border-radius:9px}
  body.soft.m2 .cal-ctrl{flex-wrap:wrap; gap:8px}
  body.soft.m2 .cal-legend{flex-wrap:wrap; font-size:11.5px; gap:8px}

  /* ---- Timesheet ---- */
  body.soft.m2 .ts-months{overflow-x:auto; padding-bottom:6px}
  body.soft.m2 .ts-row{flex-wrap:wrap; gap:8px; padding:12px 0}
  body.soft.m2 .ts-meta{font-size:12px; gap:9px; flex-wrap:wrap}
  body.soft.m2 .ts-scroll{max-height:none}

  /* ---- Chat ---- */
  body.soft.m2 .bubble{max-width:88%; font-size:14.5px; line-height:1.55; border-radius:16px; overflow-wrap:anywhere}

  /* ---- Assign / picker lists ---- */
  body.soft.m2 .asg-bar{flex-wrap:wrap; gap:8px}
  body.soft.m2 .asg-bar input{flex:1 1 100%}
  body.soft.m2 .asg-list{max-height:44vh}
  body.soft.m2 .asg-name{min-width:0; overflow-wrap:anywhere}
  body.soft.m2 .perm-list{grid-template-columns:1fr !important}
  body.soft.m2 .perm-item{min-height:46px; font-size:14px}

  /* ---- Org chart ---- */
  body.soft.m2 .org-card{text-align:center}
  body.soft.m2 .org-card b{font-size:14.5px; overflow-wrap:anywhere}

  /* ---- The safety net: nothing in V2 may be cut off ---- */
  body.soft.m2 .content b, body.soft.m2 .content small, body.soft.m2 .content span,
  body.soft.m2 .portal-body b, body.soft.m2 .portal-body small, body.soft.m2 .portal-body span,
  body.soft.m2 .modal-body b, body.soft.m2 .modal-body small, body.soft.m2 .modal-body span{
    min-width:0; overflow-wrap:anywhere;
  }
  body.soft.m2 .content, body.soft.m2 .portal-body, body.soft.m2 .modal-body{overflow-x:hidden}
}

/* ============================================================
   MOBILE V2 — the tile look, everywhere (owner, 1 Aug)
   He liked the drawer's icon-tile menu and asked for the same
   treatment on the other option pages. Any grid of cards, and
   any run of navigation-style rows, now reads as tiles with the
   same circular embossed icon. CSS only.
   ============================================================ */
@media (max-width:820px){

  /* ---- Card grids become three-across tiles ---- */
  body.soft.m2 .cards{
    display:grid !important; grid-template-columns:repeat(3,1fr) !important; gap:8px 4px !important;
  }
  body.soft.m2 .cards > .card{
    background:none !important; box-shadow:none !important; border:none !important;
    padding:10px 3px !important; text-align:center; min-width:0; border-radius:14px !important;
  }
  /* The icon: whatever the card already carries — an svg, an emoji span or a
     .ic block — is dropped into the same embossed circle. */
  body.soft.m2 .cards > .card > svg,
  body.soft.m2 .cards > .card > .ic,
  body.soft.m2 .cards > .card > span:first-child:not(.value){
    display:flex !important; align-items:center; justify-content:center;
    width:52px; height:52px; margin:0 auto 7px; border-radius:50%;
    background:var(--surface); color:var(--primary-ink); font-size:20px;
    box-shadow:var(--nm-out-sm);
  }
  body.soft.m2 .cards > .card > svg{padding:15px; box-sizing:border-box}
  body.soft.m2 .cards > .card .label,
  body.soft.m2 .cards > .card > span:last-child{
    display:block; font-size:11.5px; font-weight:600; line-height:1.3;
    color:var(--text); overflow-wrap:anywhere; white-space:normal;
  }
  /* A card that is really a statistic keeps its number legible. */
  body.soft.m2 .cards > .card .value{
    display:block; font-size:22px; font-weight:300; line-height:1.15; margin-top:2px;
  }

  /* ---- Runs of nav-style rows read as tiles too ---- */
  body.soft.m2 .tile-row{
    display:grid; grid-template-columns:repeat(3,1fr); gap:8px 4px;
  }
  body.soft.m2 .tile-row > *{
    text-align:center; padding:11px 3px; border-radius:14px; min-width:0;
    display:flex; flex-direction:column; align-items:center; gap:7px;
  }

  /* ---- Section bands, the same pale header used in the drawer ---- */
  body.soft.m2 .section-title{
    background:color-mix(in srgb,var(--primary) 9%,transparent);
    border-radius:12px; padding:10px 14px; margin:16px 0 11px;
    font-size:13px; font-weight:700; color:var(--primary-ink);
    text-transform:none; letter-spacing:0; overflow-wrap:anywhere;
  }
  body.soft.m2 .page-head{text-align:left}
}
/* Old WebViews without color-mix() still get a readable band. */
@media (max-width:820px){
  body.soft.m2 .section-title{background:rgba(191,155,46,.09)}
}
@supports (color: color-mix(in srgb,#BF9B2E 9%,transparent)){
  @media (max-width:820px){
    body.soft.m2 .section-title{background:color-mix(in srgb,var(--primary) 9%,transparent)}
  }
}

/* ---- Filter chips, as the sample shows: soft pills that wrap ---- */
@media (max-width:820px){
  body.soft.m2 .toolbar{flex-wrap:wrap; overflow:visible; row-gap:8px; padding:2px 0 10px}
  body.soft.m2 .toolbar .chip, body.soft.m2 .toolbar .chip-filter{
    border-radius:20px; padding:0 15px; min-height:40px; font-size:13.5px; font-weight:600;
    background:var(--surface) !important; box-shadow:var(--nm-out-sm) !important; border:none !important;
  }
  body.soft.m2 .toolbar .chip-filter.active, body.soft.m2 .toolbar .chip.active{
    box-shadow:var(--nm-in) !important; color:var(--primary-ink) !important; font-weight:700;
  }
  /* Rows in the sample sit apart with a clear gap and a soft raise. */
  body.soft.m2 table.m-cards > tbody > tr{
    margin-bottom:12px; padding:14px 15px; box-shadow:var(--nm-out-sm);
  }
  body.soft.m2 table.m-cards > tbody > tr > td:first-child{
    border-bottom:none !important; margin-bottom:2px; padding-bottom:4px;
  }
}

/* ---- V2: the patterns from the employee samples (1 Aug) ----
   Primary-action card, alert row with its action, 2x2 quick tiles and the
   status-row list. All reachable from markup the app already renders. */
@media (max-width:820px){
  /* The one thing to do today, as a solid brand card. Any panel or card marked
     .primary — or the first .callout on a screen — reads as the big action. */
  body.soft.m2 .callout, body.soft.m2 .card.primary{
    background:var(--primary-grad) !important; color:#fff !important;
    border-radius:20px !important; padding:18px 17px !important; box-shadow:0 8px 22px rgba(109,110,113,.32) !important;
  }
  body.soft.m2 .callout b, body.soft.m2 .callout h3, body.soft.m2 .card.primary b{
    color:#fff; font-size:26px; font-weight:800; letter-spacing:-.02em; display:block; overflow-wrap:anywhere;
  }
  body.soft.m2 .callout small, body.soft.m2 .callout .muted, body.soft.m2 .card.primary small{
    color:rgba(255,255,255,.86) !important; font-size:13px; line-height:1.5; display:block; margin-top:4px;
  }
  body.soft.m2 .callout .btn, body.soft.m2 .card.primary .btn{
    background:#fff !important; color:var(--primary-ink) !important; box-shadow:none !important;
    min-height:50px; border-radius:14px; font-size:16px; font-weight:800; margin-top:14px; width:100%;
  }
  /* Quick tiles, two across, icon square + title + one muted line. */
  body.soft.m2 .grid-2 > .card{
    text-align:left; padding:15px 14px !important; border-radius:18px !important;
  }
  body.soft.m2 .grid-2 > .card > svg, body.soft.m2 .grid-2 > .card > .ic{
    width:40px; height:40px; border-radius:13px; padding:9px; box-sizing:border-box;
    background:color-mix(in srgb,var(--primary) 10%,transparent); color:var(--primary-ink);
    display:flex; align-items:center; justify-content:center; margin-bottom:10px;
  }
  body.soft.m2 .grid-2 > .card b{font-size:15.5px; font-weight:800; display:block; overflow-wrap:anywhere}
  body.soft.m2 .grid-2 > .card small, body.soft.m2 .grid-2 > .card .muted{
    font-size:12.5px; color:var(--muted); display:block; margin-top:2px; overflow-wrap:anywhere;
  }
  /* Status rows: round state marker, title, one muted line, action on the right. */
  body.soft.m2 .p-doc{
    background:var(--surface); border-radius:16px; padding:14px 15px; margin-bottom:10px;
    border-bottom:none !important; box-shadow:var(--nm-out-sm); align-items:center;
  }
  body.soft.m2 .p-doc span:first-child{
    font-size:15px; font-weight:700; text-transform:none; letter-spacing:0; color:var(--text);
  }
  body.soft.m2 .p-doc .btn{min-height:40px; border-radius:12px; font-size:13.5px}
}

/* ============================================================
   EMPLOYEES MOBILE (≤820px only) — two-level Establishment → Employee
   card rail, owner-approved (DataSync-Employees-Mobile-Demo.html and
   -Demo-2.html, "Style B" button sizing, 1 Aug). Presentation only:
   every action calls the SAME functions the desktop ⋯ menu already
   calls (see the new block at the end of VIEWS.employees in app.js).
   Desktop (>820px) is untouched: .e2-root is display:none outside the
   media query below, and the existing desktop page-head/toolbar/table
   carry a new .e2-desktop class that only hides at ≤820px — nothing
   above 820px changes. Independent of the unrelated ?m2=1 "Mobile V2"
   experiment above; this is the standard Employees mobile screen.
   Reuses --nm-dark/--nm-light/--nm-out/--nm-out-sm/--nm-in/--bg and the
   existing --primary/--primary-grad/--danger/--warn/--ok palette — no
   new colours anywhere in this block. Class names are prefixed .e2- to
   stay clear of the unrelated .trail/.crumb/.pill/.meter/.chip/.est/
   .card names already used (or free) elsewhere in this file.
   ============================================================ */
.e2-root{ display:none }
@media (max-width:820px){
  body.soft .e2-desktop{ display:none !important }
  body.soft .e2-root{ display:block }

  body.soft .e2-trail{
    background:var(--primary-grad); margin:-2px -2px 12px; padding:10px 14px 12px; border-radius:0 0 16px 16px;
    display:flex; align-items:center; gap:6px; overflow-x:auto; white-space:nowrap;
  }
  body.soft .e2-trail::-webkit-scrollbar{ height:0 }
  body.soft .e2-crumb{
    font:700 11.5px/1 var(--font-brand),sans-serif; color:#fff; background:rgba(255,255,255,.16);
    border:none; border-radius:16px; padding:7px 12px; cursor:pointer; flex:none;
    display:inline-flex; align-items:center; gap:6px;
  }
  body.soft .e2-crumb svg{ flex:none }
  body.soft .e2-crumb.e2-here{ background:#fff; color:var(--primary-d) }
  body.soft .e2-sep{ display:inline-flex; color:rgba(255,255,255,.7); flex:none }
  /* The establishment crumb is the only one allowed to shrink: a long company
     name ellipses inside the pill instead of running off the screen edge. The
     "Employees" crumb keeps flex:none so it can never be squeezed away.
     On Payroll (2 Aug) the establishment crumb became a MIDDLE crumb rather
     than the trailing one, and it keeps this rule: it is still the only crumb
     whose text the customer writes and whose length nothing bounds, which is
     the thing the rule was written to contain. The module crumb and the tab
     crumb are a fixed vocabulary and stay flex:none. */
  body.soft .e2-crumb.e2-crumb-est{ flex:0 1 auto; min-width:0; overflow:hidden }
  body.soft .e2-ct{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
  /* A crumb that names where you are but has nothing behind it to go back to —
     the establishment crumb on a one-establishment account. It is rendered as a
     span, not a button, so it cannot be tapped at all; this only stops the
     pointer pretending otherwise. No colour of its own. */
  body.soft .e2-crumb.e2-static{ cursor:default }

  body.soft .e2-body{ padding:0 2px 18px }

  body.soft .e2-hero{
    background:var(--bg); border-radius:18px; padding:15px 16px; margin-bottom:12px; box-shadow:var(--nm-out);
  }
  body.soft .e2-hrow{ display:flex; align-items:center; gap:12px; min-width:0 }
  body.soft .e2-hrow > div:first-child{ flex:1; min-width:0 }
  body.soft .e2-k{ font-size:10.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--muted) }
  body.soft .e2-hrow b{ display:block; font-size:16px; font-weight:700; line-height:1.3; overflow-wrap:anywhere; margin-top:2px; color:var(--text) }
  body.soft .e2-cnt{ flex:none; text-align:center }
  body.soft .e2-cnt b{ display:block; font-size:21px; font-weight:800; color:var(--primary-ink); line-height:1; margin:0 }
  body.soft .e2-cnt small{ font-size:10.5px; color:var(--muted) }
  /* Style B (owner, 1 Aug): 38px CTA, 12.5px label. */
  body.soft .e2-cta{
    display:flex; align-items:center; justify-content:center; gap:7px; width:100%; margin-top:12px;
    min-height:38px; border-radius:11px; border:none; cursor:pointer;
    font:700 12.5px var(--font-brand),sans-serif; letter-spacing:.01em; background:var(--primary-grad); color:#fff;
  }

  body.soft .e2-srch{
    display:flex; align-items:center; gap:9px; background:var(--bg); border-radius:13px;
    padding:11px 14px; margin-bottom:10px; box-shadow:var(--nm-in);
  }
  body.soft .e2-srch span{ display:inline-flex; color:var(--muted); flex:none }
  body.soft .e2-srch input{
    flex:1; min-width:0; border:none; background:none; outline:none;
    font:500 14px var(--font-brand),sans-serif; color:var(--text);
  }
  body.soft .e2-srch input::placeholder{ color:var(--muted) }

  body.soft .e2-chips{ display:flex; gap:7px; margin-bottom:12px; overflow-x:auto; padding-bottom:2px }
  body.soft .e2-chips::-webkit-scrollbar{ height:0 }
  /* Style B: 34px row chip, 11.5px label. */
  body.soft .e2-chip{
    flex:none; font:700 11.5px var(--font-brand),sans-serif; padding:0 12px; min-height:34px; border-radius:20px; border:none;
    background:var(--bg); color:var(--text); box-shadow:var(--nm-out-sm); cursor:pointer;
  }
  body.soft .e2-chip.e2-amber{ color:var(--warn) }
  body.soft .e2-chip.e2-red{ color:var(--danger) }
  body.soft .e2-chip.e2-on.e2-amber{ background:var(--warn); color:#fff; box-shadow:var(--nm-in) }
  body.soft .e2-chip.e2-on.e2-red{ background:var(--danger); color:#fff; box-shadow:var(--nm-in) }

  /* The .e2-band rules were deleted with the band itself (owner, 1 Aug) — the
     hero card and the trail already carry the name, so it was pure repetition
     costing ~60px on every screen. The hero's count switches to "N shown" when
     a filter is narrowing the list, which is the only thing the band added. */
  body.soft .e2-empty{ text-align:center; color:var(--muted); padding:28px 0; font-size:13.5px }

  /* ---- Level 1: establishment card ---- */
  body.soft .e2-est{
    background:var(--bg); border-radius:16px; padding:8px 15px; margin-bottom:8px; cursor:pointer;
    box-shadow:var(--nm-out-sm);
  }
  body.soft .e2-est .e2-tt{ display:flex; align-items:flex-start; gap:10px; min-width:0 }
  body.soft .e2-est .e2-tt b{
    /* Regular weight, not bold (owner, 1 Aug). Shared by Employees and
       Documents on purpose — one establishment card, one look. */
    flex:1; min-width:0; font-size:14px; font-weight:400; letter-spacing:.01em; color:var(--primary-d);
    line-height:1.25; text-transform:uppercase; overflow-wrap:anywhere; display:flex; align-items:center; gap:7px;
  }
  body.soft .e2-est .e2-tt > svg{ color:var(--muted); flex:none; margin-top:1px }
  /* Two rows, label and value side by side on one line (owner's layout, 1 Aug):
     "6 Employees   License No 1118495" / "Trade License valid   Est. Card 19d".
     Stacking them cost a whole line of height each and gained nothing. */
  body.soft .e2-grid{ display:flex; gap:8px; margin-top:5px; padding-top:5px; border-top:1px solid var(--border) }
  body.soft .e2-grid + .e2-grid{ margin-top:4px; padding-top:4px }
  body.soft .e2-grid div{
    flex:1; min-width:0; display:flex; align-items:center; justify-content:center; gap:5px; flex-wrap:wrap;
  }
  body.soft .e2-grid small{ font-size:10.5px; line-height:1.25; color:var(--muted) }
  body.soft .e2-grid b{ font-size:13px; line-height:1.25; font-weight:700; overflow-wrap:anywhere; color:var(--text) }
  body.soft .e2-grid .e2-warn{ color:var(--warn) }
  body.soft .e2-grid .e2-bad{ color:var(--danger) }
  body.soft .e2-est.e2-all{ background:rgba(191,155,46,.08) }
  body.soft .e2-est.e2-all .e2-tt b{ text-transform:none }

  /* ---- Level 2: employee card ---- */
  body.soft .e2-card{
    background:var(--bg); border-radius:16px; padding:8px 14px; margin-bottom:8px; box-shadow:var(--nm-out-sm);
  }
  body.soft .e2-card .e2-top{ display:flex; gap:11px; align-items:flex-start; min-width:0 }
  body.soft .e2-card .avatar{ width:42px !important; height:42px !important; font-size:14px !important }
  body.soft .e2-nm{ flex:1; min-width:0; cursor:pointer }
  body.soft .e2-nm b{ display:block; font-size:15.5px; font-weight:700; line-height:1.2; overflow-wrap:anywhere; color:var(--text) }
  body.soft .e2-nm small{ display:block; font-size:12px; line-height:1.3; color:var(--muted); margin-top:0; overflow-wrap:anywhere }
  body.soft .e2-nm em{ display:block; font-style:normal; font-size:12.5px; line-height:1.3; color:var(--text); opacity:.72; margin-top:1px; overflow-wrap:anywhere }
  body.soft .e2-temp{ color:var(--warn); font-weight:700 }
  body.soft .e2-side{ display:flex; flex-direction:column; align-items:flex-end; gap:6px; flex:none }
  body.soft .e2-dots{
    width:34px; height:34px; border-radius:10px; border:none; background:var(--bg); color:var(--text);
    cursor:pointer; display:flex; align-items:center; justify-content:center; box-shadow:var(--nm-out-sm);
  }
  body.soft .e2-pill{ display:inline-flex; padding:3px 9px; border-radius:8px; font-size:10.5px; font-weight:800; flex:none }
  body.soft .e2-pill.e2-ok{ background:rgba(16,185,129,.13); color:var(--ok) }
  body.soft .e2-pill.e2-bad{ background:rgba(239,68,68,.13); color:var(--danger) }
  body.soft .e2-pill.e2-warn{ background:rgba(245,158,11,.15); color:var(--warn) }
  body.soft .e2-pill.e2-gray{ background:rgba(148,163,184,.16); color:var(--muted) }

  /* The .e2-docs / .e2-meter strip was removed on the owner's instruction,
     1 Aug — see the note next to e2Card() in app.js. Its rules are deleted
     rather than left behind, so nothing dead accumulates in the stylesheet. */
  body.soft .e2-fixb{ margin-top:6px; display:flex; gap:8px }
  /* Style B: 34px row button, 11.5px label. */
  body.soft .e2-fix{
    flex:1; min-height:34px; border-radius:10px; border:none; cursor:pointer; display:flex; align-items:center;
    justify-content:center; gap:6px; font:700 11.5px var(--font-brand),sans-serif; background:var(--primary-grad); color:#fff;
  }

  /* ---- ⋯ bottom sheet: same 7 actions, same order, as desktop ---- */
  body.soft .e2-sheetbg{
    position:fixed; inset:0; background:rgba(20,26,45,.45); display:none; align-items:flex-end; z-index:60;
  }
  body.soft .e2-sheetbg.open{ display:flex }
  /* Sheet sized to 3/5 of the first build — rows, text, icons and padding
     all scaled together so the proportions stay the same, just smaller. */
  body.soft .e2-sheet{
    background:var(--bg); border-radius:20px 20px 0 0; width:100%; padding:6px 14px 14px;
    box-shadow:0 -8px 30px rgba(16,24,40,.25); max-height:80vh; overflow-y:auto;
  }
  body.soft .e2-grab{ width:36px; height:4px; border-radius:3px; background:rgba(148,163,184,.4); margin:3px auto 7px }
  body.soft .e2-sheet h4{ margin:0 0 2px; font-size:12.5px; font-weight:700; line-height:1.25; color:var(--text) }
  body.soft .e2-sheet h4 small{ display:block; font-weight:600; color:var(--muted); font-size:10px; line-height:1.25; margin-top:1px }
  body.soft .e2-sheet button{
    display:flex; align-items:center; gap:9px; width:100%; background:none; border:none;
    padding:8px 2px; font:600 12.5px var(--font-brand),sans-serif; line-height:1.25; color:var(--text); cursor:pointer;
    border-bottom:1px solid var(--border); text-align:left;
  }
  body.soft .e2-sheet button:last-child{ border-bottom:none }
  body.soft .e2-sheet button.e2-actwarn{ color:var(--warn) }
  body.soft .e2-sheet button.e2-actbad{ color:var(--danger) }
  body.soft .e2-sheet button svg{ flex:none }

  /* ============================================================
     DOCUMENTS MOBILE — the same rail, one level down. Everything the
     Documents screen needs already exists above (trail, crumbs, hero,
     cta, search, grid, pill, dots, fix, empty, sheet) and is reused
     verbatim; only the four things below are genuinely new, because
     nothing in the Employees screen had them:
       .e2-share/.e2-sh  the hero's PDF Report · Export CSV row (in the
                         owner-approved Documents demo, not in Employees)
       .e2-stats         the five tappable stat bands — the distinctive
                         feature of this screen
       .e2-dot           the 11px status dot on a document card
       .e2-person        the person the document belongs to, with avatar
     No new colours: the bands take --danger / --warn / --primary, the
     same values the desktop stat cards already use. The demo's separate
     orange for the "Missing" band is deliberately NOT introduced — the
     desktop Missing card's own value colour is already --warn.
     ============================================================ */
  body.soft .e2-share{
    display:flex; align-items:center; gap:8px; margin-top:12px; padding-top:11px;
    border-top:1px solid var(--border);
  }
  body.soft .e2-sh{
    flex:1; min-width:0; display:flex; align-items:center; justify-content:center; gap:6px;
    background:none; border:none; padding:0; min-height:22px; cursor:pointer;
    font:700 12.5px var(--font-brand),sans-serif; color:var(--primary-ink);
  }
  body.soft .e2-sh svg{ flex:none }

  /* Five across, because five is what the desktop shows and dropping one
     would hide a filter that exists. 8.5px uppercase captions keep them
     readable at 393px without scrolling. */
  body.soft .e2-stats{
    display:grid; grid-template-columns:repeat(5,1fr); background:var(--bg); border-radius:16px;
    overflow:hidden; box-shadow:var(--nm-out-sm); margin-bottom:12px;
  }
  body.soft .e2-stats button{
    background:none; border:none; padding:11px 3px 8px; text-align:center; min-width:0; cursor:pointer;
    border-bottom:3px solid transparent; font-family:var(--font-brand),sans-serif; color:var(--text);
  }
  body.soft .e2-stats button + button{ border-left:1px solid var(--border) }
  body.soft .e2-stats b{ display:block; font-size:21px; font-weight:800; line-height:1.1 }
  body.soft .e2-stats small{
    display:block; font-size:8.5px; font-weight:700; letter-spacing:.02em; text-transform:uppercase;
    color:var(--muted); margin-top:4px; line-height:1.3;
  }
  body.soft .e2-stats .e2-bad b{ color:var(--danger) }
  body.soft .e2-stats .e2-warn b{ color:var(--warn) }
  body.soft .e2-stats .e2-blue b{ color:var(--primary-ink) }
  body.soft .e2-stats button.e2-on{ border-bottom-color:var(--primary); background:rgba(191,155,46,.09) }

  /* One dot and one pill carry the status — never a five-segment meter.
     It was deleted from the Employees card on the owner's instruction
     (1 Aug) and must not reappear here in another shape. */
  body.soft .e2-dot{ width:11px; height:11px; border-radius:50%; flex:none; margin-top:5px }
  body.soft .e2-dot.e2-ok{ background:var(--ok) }
  body.soft .e2-dot.e2-warn{ background:var(--warn) }
  body.soft .e2-dot.e2-bad{ background:var(--danger) }
  body.soft .e2-dot.e2-gray{ background:rgba(148,163,184,.5) }

  /* The person sits INSIDE the title block as its third line, not on a row of
     its own (owner, 1 Aug). The right-hand column is ⋯ over the status pill and
     is taller than a two-line title, which left a dead rectangle under the
     document number — he photographed it. Making the person the third line
     fills that space instead of adding height below it. */
  body.soft .e2-person{ display:flex; align-items:flex-start; gap:7px; margin-top:4px; min-width:0 }
  body.soft .e2-card .e2-person .avatar{ width:26px !important; height:26px !important; font-size:10px !important; margin-top:1px }
  body.soft .e2-pw{ flex:1; min-width:0 }
  body.soft .e2-person > .e2-pw > span{
    display:block; min-width:0; font-size:12.5px; font-weight:600; line-height:1.25;
    overflow-wrap:anywhere; color:var(--text);
  }
  /* Employee code on the left, status pill on the right, one row. */
  body.soft .e2-pid{ display:flex; align-items:center; gap:8px; margin-top:2px; min-width:0 }
  body.soft .e2-pid small{ flex:1; min-width:0; color:var(--muted); font-weight:600; font-size:11.5px; overflow-wrap:anywhere }
  body.soft .e2-pid .e2-pill{ margin-left:auto }
  /* Nothing but ⋯ lives in the right column now. */
  body.soft .e2-card .e2-side{ align-items:flex-end; gap:5px }

  /* Thinner still, document card only (owner asked twice). Everything here is
     vertical space: the shell, the gap to the dates row, and the dates row's
     own leading. No font is shrunk below what he already approved. */
  body.soft .e2-card.e2-doc{ padding:7px 12px; margin-bottom:7px }
  body.soft .e2-card.e2-doc .e2-top{ gap:9px }
  body.soft .e2-card.e2-doc .e2-dot{ margin-top:4px }
  body.soft .e2-card.e2-doc .e2-nm b{ line-height:1.15 }
  body.soft .e2-card.e2-doc .e2-nm small{ line-height:1.2 }
  body.soft .e2-card.e2-doc .e2-grid{ margin-top:4px; padding-top:4px }
  body.soft .e2-card.e2-doc .e2-grid small{ line-height:1.15 }
  body.soft .e2-card.e2-doc .e2-grid b{ line-height:1.15 }
  body.soft .e2-card.e2-doc .e2-dots{ width:30px; height:30px; border-radius:9px }

  /* .tap press feedback — only ever used on the new e2- elements above. */
  body.soft .e2-root .tap{ cursor:pointer; transition:box-shadow .13s, transform .13s }
  body.soft .e2-root .tap:active{ transform:translateY(.5px) }

  /* ============================================================
     EMPLOYEE NAMES −2px, PHONE ONLY (owner, 1 Aug — "employee's name
     everywhere make 2 pt more smaller"; he chose phone-only, desktop
     untouched). Applied by RENDERING and checking, not by grepping for a
     size — 3.11's lesson, where a colour-based sweep missed the three
     rules that carried no colour of their own.
     The two card families share .e2-nm b but do NOT mean the same thing:
     on the Employees card it is the PERSON, on the Documents card it is
     the DOCUMENT TYPE. Only the person shrinks — :not(.e2-doc) is what
     keeps "Passport" at its approved size.
     ============================================================ */
  body.soft .e2-card:not(.e2-doc) .e2-nm b{ font-size:13.5px }
  /* The document item name (Passport / Emirates ID), phone only. Third pass:
     he said twice it still read bigger than the person's name. It is now the
     SAME 11.5px as the name — the bold weight, not the size, is what marks it
     as the card's subject. Its number line drops to 10.5 so the sizes descend
     down the card instead of the sub-line out-sizing its own title. */
  body.soft .e2-card.e2-doc .e2-nm b{ font-size:11.5px }
  body.soft .e2-card.e2-doc .e2-nm small{ font-size:10.5px }
  /* 11.5, not 10.5 — he said the person's name went a step too small at −2. */
  body.soft .e2-person > .e2-pw > span{ font-size:11.5px }
  /* The expiry pill ("24 days left" / "EXPIRED 9d ago") — smaller too, and its
     padding with it, so the pill shrinks rather than just its text. */
  body.soft .e2-card.e2-doc .e2-pill{ font-size:9px; padding:2px 7px; border-radius:7px }
  body.soft .e2-pid small{ font-size:10.5px }
  /* Names inside the generic table→card converter (every module we have not
     hand-built yet still renders through it). .emp-cell b inherits the 14px
     body size, so 12px is the same −2. */
  body.soft table.m-cards .emp-cell b{ font-size:12px }

  /* ============================================================
     ADD / EDIT DOCUMENT — thinner boxes so the whole window is thinner
     (owner, 1 Aug). Height only: the 14px input text is left alone,
     because anything smaller is harder to read while typing an Emirates
     ID number on a phone. Scoped to .modal so no inline form elsewhere
     on the page is caught by it.
     ============================================================ */
  body.soft .modal .field{ margin-bottom:9px }
  body.soft .modal .field label{ margin-bottom:3px; font-size:12px }
  body.soft .modal .field input,
  body.soft .modal .field select,
  body.soft .modal .field textarea{ padding:7px 11px; border-radius:9px }

  /* ============================================================
     ATTENDANCE MOBILE — the same rail again (owner-approved,
     DataSync-Attendance-Rail-Demo.html, 1 Aug). Almost everything it
     needs already exists above and is reused verbatim: .e2-trail,
     .e2-crumb, .e2-hero, .e2-cta, .e2-chips/.e2-chip, .e2-est,
     .e2-grid, .e2-card, .e2-top, .e2-nm, .e2-side, .e2-dots,
     .e2-fixb/.e2-fix, .e2-empty and the whole .e2-sheet. Only six
     things below are genuinely new, because nothing in the Employees
     or Documents screens had them:
       .e2-att       the attendance card, tightened to the Documents
                     card's approved proportions without moving the
                     Employees or Documents cards underneath them
       .e2-alert     the "Needs attention" strip at the top of the
                     screen — the people flagged today who have no
                     attendance row at all, so they can have no card
       .e2-arow      one person inside that strip
       .e2-why       the amber reason line on a card that DOES have a
                     row but still needs a decision
       .e2-fixg      the ghost second button beside .e2-fix ("Edit",
                     "See on map") — the demo's .gh
       .e2-amberize  see the note further down
     No new colours: --warn, --danger, --ok, --border, --muted, --bg
     and --primary-grad only, at tints already used above.
     Nothing here is position:sticky — the block at the end of this
     media query un-sticks the phone on purpose, and the strip and the
     chips deliberately scroll away with the page like everything else.
     ============================================================ */
  /* ---- the attendance card ------------------------------------------------
     THIS IS THE HEIGHT THE OWNER APPROVED (1 Aug). He pointed at a rendered
     Attendance card and said "this height". It was briefly changed to the
     taller Employees sizing on a misreading of an earlier instruction and put
     straight back — do not "harmonise" it upward again.

     7px 12px shell, 7px between cards, 38px avatar, 11.5px name, 10.5px
     sub-line, 4px divider gaps, 30px ⋯ — the same tight sizing the Documents
     card carries. */
  body.soft .e2-card.e2-att{ padding:7px 12px; margin-bottom:7px }
  body.soft .e2-card.e2-att .e2-top{ gap:9px }
  body.soft .e2-card.e2-att .avatar{ width:38px !important; height:38px !important; font-size:12px !important }
  body.soft .e2-card.e2-att .e2-nm b{ font-size:11.5px; line-height:1.15 }
  body.soft .e2-card.e2-att .e2-nm small{ font-size:10.5px; line-height:1.2 }
  body.soft .e2-card.e2-att .e2-grid{ margin-top:4px; padding-top:4px }
  body.soft .e2-card.e2-att .e2-grid small{ line-height:1.15 }
  body.soft .e2-card.e2-att .e2-grid b{ line-height:1.15 }
  body.soft .e2-card.e2-att .e2-dots{ width:30px; height:30px; border-radius:9px }
  body.soft .e2-card.e2-att .e2-side{ gap:5px }
  body.soft .e2-card.e2-att .badge{ font-size:9px; padding:2px 7px; border-radius:7px }
  body.soft .e2-card.e2-att .e2-grid .e2-warn{ color:var(--warn) }

  /* ---- the reason line, and the ghost second fix button ---- */
  body.soft .e2-why{
    display:flex; align-items:flex-start; gap:6px; margin-top:5px; padding-top:5px;
    border-top:1px dashed var(--border); font-size:10.5px; line-height:1.25;
    font-weight:700; color:var(--warn);
  }
  body.soft .e2-why svg{ flex:none; margin-top:1px }
  body.soft .e2-why span{ min-width:0; overflow-wrap:anywhere }
  body.soft .e2-fixg{
    flex:1; min-height:34px; border-radius:10px; border:none; cursor:pointer; display:flex;
    align-items:center; justify-content:center; gap:6px;
    font:700 11.5px var(--font-brand),sans-serif; background:var(--bg); color:var(--primary-d);
    box-shadow:var(--nm-out-sm);
  }
  body.soft .e2-fix svg, body.soft .e2-fixg svg{ flex:none }

  /* ---- the "Needs attention" strip ----
     It sits ABOVE the list, at the top of the screen, because the people
     in it have no attendance row today and therefore cannot appear as
     cards in the list at all (owner, 1 Aug). It is a plain block in the
     flow — never sticky. */
  body.soft .e2-alert{
    background:rgba(245,158,11,.09); border-radius:16px; padding:8px 12px 9px;
    margin-bottom:10px; box-shadow:var(--nm-out-sm);
  }
  body.soft .e2-atitle{
    display:flex; align-items:center; gap:7px; color:var(--warn);
    font-size:11.5px; line-height:1.25; font-weight:700;
  }
  body.soft .e2-atitle svg{ flex:none }
  body.soft .e2-atitle span{ margin-left:auto; font-size:9px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--muted) }
  body.soft .e2-arow{ margin-top:6px; padding-top:6px; border-top:1px solid var(--border) }
  body.soft .e2-arow .e2-top{ display:flex; gap:9px; align-items:flex-start; min-width:0 }
  body.soft .e2-arow .avatar{ width:30px !important; height:30px !important; font-size:10px !important }
  body.soft .e2-arow .e2-nm b{ font-size:11.5px; line-height:1.15 }
  body.soft .e2-arow .e2-nm small{ font-size:10.5px; line-height:1.2; color:var(--warn); font-weight:600 }
  body.soft .e2-amore{ margin-top:6px; font-size:10.5px; line-height:1.25; color:var(--muted) }

  /* ============================================================
     LATE and HALF DAY READ AMBER — PHONE ONLY.
     This deliberately breaks the one-source-of-truth preference the
     rest of this file follows, at the owner's explicit instruction
     (1 Aug). PLEASE DO NOT "FIX" IT.
     statusBadge() (app.js ~4315) maps both statuses to grey and is
     shared by the desktop Attendance table, Payroll, Leave, Timesheet
     and more — changing it there would repaint every one of them and
     change the desktop, which must stay exactly as it is. So the
     override lives here instead: mobile only (inside @media
     (max-width:820px)), and scoped to the Attendance rail's own card
     (.e2-att) so no other grey badge anywhere is touched. The colour
     is the existing --warn at the same tint .e2-pill.e2-warn above
     already uses — nothing new is invented.
     ============================================================ */
  body.soft .e2-card.e2-att .e2-amberize .badge.gray{
    background:rgba(245,158,11,.15); color:var(--warn);
  }

  /* ============================================================
     PAYROLL MOBILE — the same rail a fourth time (owner-approved,
     DataSync-Payroll-Rail-Demo.html, 1 Aug). Screen 4 of 10.

     Almost everything it needs already exists above and is reused
     verbatim, not reinvented: .e2-trail / .e2-crumb / .e2-crumb-est /
     .e2-sep / .e2-ct, .e2-hero / .e2-hrow / .e2-k / .e2-cnt, .e2-cta,
     .e2-srch, .e2-chips + .e2-chip (the six tabs), .e2-est / .e2-all,
     .e2-grid, .e2-card / .e2-top / .e2-nm / .e2-side / .e2-dots,
     .e2-why, .e2-fixb / .e2-fix, .e2-temp, .e2-empty and the whole
     .e2-sheet. Only THREE things below are genuinely new, because
     nothing in the Employees, Documents or Attendance screens had them:

       .e2-pay    the payroll card. Same thin proportions as .e2-att —
                  see the note on it below.
       .p2-mstep  the month stepper inside the hero (‹ July 2026 ›).
                  Payroll is the first rail whose whole screen is a
                  MONTH rather than a day or a list, so it is the first
                  that needs one.
       .e2-okwhy  the GREEN variant of the existing amber .e2-why, used
                  by one line only: "Ready — N employee(s)…" after the
                  WPS readiness check. A red-only reason line cannot say
                  the file is fine.

     No new colours. --ok, --warn, --danger, --border, --muted, --bg,
     --text and --primary-d only, at tints already used above.
     Nothing here is position:sticky — the month stepper in particular
     scrolls away with the page, like the Attendance chips and the
     Documents bands, and the un-sticky block at the end of this media
     query holds the line behind it.
     ============================================================ */

  /* ---- the payroll card --------------------------------------------------
     THE SAME HEIGHT THE OWNER APPROVED for Attendance (1 Aug): 7px 12px
     shell, 7px between cards, 38px avatar, 11.5px name, 10.5px sub-line,
     4px divider gaps, 30px ⋯. Per 3.38 this thin card is the standard for
     every module after Employees — it is NOT re-derived here, the values
     are copied from .e2-att above and mobile-payroll-test.mjs parses BOTH
     rules out of this file and fails if a single one of them ever differs,
     so the two cannot drift apart even though they are written twice.

     Written as its own rule rather than added to .e2-att's selector list on
     purpose: verify-attendance-preview.mjs asserts the literal string
     "body.soft .pv-phone .e2-card.e2-att{", and widening that selector would
     have broken a shipped verifier to save nine lines. */
  body.soft .e2-card.e2-pay{ padding:7px 12px; margin-bottom:7px }
  body.soft .e2-card.e2-pay .e2-top{ gap:9px }
  body.soft .e2-card.e2-pay .avatar{ width:38px !important; height:38px !important; font-size:12px !important }
  body.soft .e2-card.e2-pay .e2-nm b{ font-size:11.5px; line-height:1.15 }
  body.soft .e2-card.e2-pay .e2-nm small{ font-size:10.5px; line-height:1.2 }
  body.soft .e2-card.e2-pay .e2-grid{ margin-top:4px; padding-top:4px }
  body.soft .e2-card.e2-pay .e2-grid small{ line-height:1.15 }
  body.soft .e2-card.e2-pay .e2-grid b{ line-height:1.15 }
  body.soft .e2-card.e2-pay .e2-dots{ width:30px; height:30px; border-radius:9px }
  body.soft .e2-card.e2-pay .e2-side{ gap:5px }
  body.soft .e2-card.e2-pay .badge{ font-size:9px; padding:2px 7px; border-radius:7px }
  body.soft .e2-card.e2-pay .e2-grid .e2-warn{ color:var(--warn) }
  body.soft .e2-card.e2-pay .e2-grid .e2-bad{ color:var(--danger) }

  /* ---- the month stepper -------------------------------------------------
     A full-width row under the hero's heading rather than a third item
     beside it: at 390px the establishment name, the count and a stepper on
     one line left the name three characters wide. The month is the single
     most important word on this screen, so it gets its own line and is
     centred between two 32px taps.

     The chevron is ONE icon (ICO.chev points right) turned 180° for the
     previous month, so there is no second glyph to keep in step. */
  body.soft .p2-mstep{
    display:flex; align-items:center; justify-content:center; gap:12px;
    margin-top:11px; padding-top:10px; border-top:1px solid var(--border);
  }
  body.soft .p2-mstep button{
    width:32px; height:32px; border-radius:9px; border:none; flex:none; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    background:var(--bg); color:var(--primary-d); box-shadow:var(--nm-out-sm);
  }
  body.soft .p2-mstep button[data-p2m="-1"] svg{ transform:rotate(180deg) }
  body.soft .p2-mstep b{
    font-size:13px; font-weight:800; color:var(--primary-d); white-space:nowrap;
    min-width:120px; text-align:center;
  }

  /* ---- Level 1: the tab tile (owner, 2 Aug) ------------------------------
     The six tabs as tappable tiles. They started life on the landing page;
     after the owner's phone review they moved one level down, onto the
     establishment's own page, with the month stepper above them. Not one
     value below changed with them — only where the grid is drawn.

     THE SIZING IS THE ESTABLISHMENT CARD'S, COPIED not re-derived (3.38):
     8px 15px shell, 8px between tiles, 14px name at weight 400, uppercase,
     --primary-d. mobile-payroll-test.mjs parses both rules out of this file
     and fails if a single value ever differs, exactly as it already does for
     .e2-att vs .e2-pay, so the two cannot drift even though they are written
     twice.

     Written as its own family rather than reusing .e2-est on purpose. The two
     cards mean different things and must be countable apart: an establishment
     card carries COUNTS ONLY and may never show a money total, while a tab
     tile prints the server's own month totals. Sharing one class would have
     made every existing .e2-est assertion — including that inverted one —
     ambiguous the moment a tab tile appeared on the same screen. Since the
     re-order the two now sit one level apart, which makes that all the more
     worth keeping: the picker is Level 0, the tiles are Level 1.

     Only three rules are new. Everything inside the tile is the shipped
     .e2-grid label/value pair, inline and never stacked. Nothing here is
     position:sticky; the block at the end of this media query holds that line.
     No new colours. */
  body.soft .p2-tile{
    background:var(--bg); border-radius:16px; padding:8px 15px; margin-bottom:8px; cursor:pointer;
    box-shadow:var(--nm-out-sm);
  }
  body.soft .p2-tt{ display:flex; align-items:center; gap:9px; min-width:0 }
  body.soft .p2-tt b{
    flex:1; min-width:0; font-size:14px; font-weight:400; letter-spacing:.01em; color:var(--primary-d);
    line-height:1.25; text-transform:uppercase; overflow-wrap:anywhere;
  }
  body.soft .p2-tt > svg{ color:var(--muted); flex:none }
  /* One line saying what the tab holds. It is what a tile shows INSTEAD of
     figures for a tab nobody has opened yet this month — see the note in
     app.js on why no extra request is made to fill them. */
  body.soft .p2-sub{ font-size:10.5px; line-height:1.25; color:var(--muted); margin-top:2px }

  /* ---- the readiness answer ----
     .e2-why says "something is wrong" in amber. This is the same line in
     green for the one case where the answer is that nothing is: the WPS
     file would be accepted. It inherits every other property from .e2-why,
     so only the two colours are restated. */
  body.soft .e2-why.e2-okwhy{ color:var(--ok); border-top-color:rgba(16,185,129,.4) }

  /* ============================================================
     LEAVE MOBILE — the same rail a fifth time (owner-approved,
     DataSync-Leave-Rail-Demo.html, 1 Aug). Screen 5 of 10.

     Almost everything it needs already exists above and is reused
     verbatim, not reinvented: .e2-trail / .e2-crumb / .e2-crumb-est /
     .e2-sep / .e2-ct, .e2-hero / .e2-hrow / .e2-k / .e2-cnt, .e2-cta,
     .e2-srch, .e2-chips + .e2-chip, .e2-est / .e2-all, .e2-grid,
     .e2-card / .e2-top / .e2-nm / .e2-side / .e2-dots, .e2-temp,
     .e2-fixb / .e2-fix / .e2-fixg, .e2-empty and the whole .e2-sheet.
     Only FOUR things below are genuinely new, because nothing in the
     Employees, Documents, Attendance or Payroll screens had them:

       .e2-lv     the leave-request card. Same thin proportions as
                  .e2-att — see the note on it below.
       .e2-note   the quiet inset box the reason sits in. It is the
                  "optional quiet inset box for reasons / last replies"
                  of the approved card recipe (3.35) and the demo's own
                  .reason; Leave is the first rail whose rows carry a
                  sentence a person typed, so it is the first that needs
                  one.
       .e2-today  "on leave today" in the card's sub-line, the green
                  sibling of the existing amber .e2-temp. It is what the
                  demo said with a "· today" suffix on the status pill;
                  put in the sub-line instead so the right-hand column
                  stays two items tall and the card stays thin.
       .e2-fix.e2-ok / .e2-fixg.e2-bad
                  the Approve / Reject pair on a pending card — the
                  distinctive feature of this screen. Approve is the
                  existing .e2-fix with --ok instead of the brand
                  gradient, Reject the existing ghost .e2-fixg in
                  --danger, which is exactly what the approved demo
                  showed. No new geometry: both keep .e2-fix's own 34px
                  height and 11.5px label (Style B).

     No new colours. --ok, --danger, --border, --muted, --bg and --text
     only, at tints already used above. Nothing here is position:sticky;
     the un-sticky block at the end of this media query holds that line,
     and the chips and the reason boxes scroll away with the page like
     everything else.
     ============================================================ */

  /* ---- the leave-request card --------------------------------------------
     THE SAME HEIGHT THE OWNER APPROVED for Attendance (1 Aug): 7px 12px
     shell, 7px between cards, 38px avatar, 11.5px name, 10.5px sub-line,
     4px divider gaps, 30px ⋯. Per 3.38 this thin card is the standard for
     every module after Employees — it is NOT re-derived here, the values
     are copied from .e2-att above, and mobile-leave-test.mjs parses BOTH
     rules out of this file and fails if a single one of them ever differs,
     exactly as mobile-payroll-test.mjs already does for .e2-att vs .e2-pay.

     The demo's own card was 13px 14px with a 42px avatar and a 15px name —
     it predates four rounds of size decisions, so the house style wins. */
  body.soft .e2-card.e2-lv{ padding:7px 12px; margin-bottom:7px }
  body.soft .e2-card.e2-lv .e2-top{ gap:9px }
  body.soft .e2-card.e2-lv .avatar{ width:38px !important; height:38px !important; font-size:12px !important }
  body.soft .e2-card.e2-lv .e2-nm b{ font-size:11.5px; line-height:1.15 }
  body.soft .e2-card.e2-lv .e2-nm small{ font-size:10.5px; line-height:1.2 }
  body.soft .e2-card.e2-lv .e2-grid{ margin-top:4px; padding-top:4px }
  body.soft .e2-card.e2-lv .e2-grid small{ line-height:1.15 }
  body.soft .e2-card.e2-lv .e2-grid b{ line-height:1.15 }
  body.soft .e2-card.e2-lv .e2-dots{ width:30px; height:30px; border-radius:9px }
  body.soft .e2-card.e2-lv .e2-side{ gap:5px }
  body.soft .e2-card.e2-lv .badge{ font-size:9px; padding:2px 7px; border-radius:7px }
  body.soft .e2-card.e2-lv .e2-grid .e2-warn{ color:var(--warn) }

  /* ---- the reason, in its own quiet inset box ----
     Inset (--nm-in) rather than raised, so it reads as something written
     INTO the card rather than another card on top of it — the same
     treatment the search box already has. */
  body.soft .e2-note{
    margin-top:5px; padding:6px 9px; border-radius:9px; background:var(--bg);
    box-shadow:var(--nm-in); font-size:10.5px; line-height:1.3; color:var(--text);
  }
  body.soft .e2-note b{
    display:block; font-size:9px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
    color:var(--muted); margin-bottom:2px;
  }
  body.soft .e2-note span{ display:block; min-width:0; overflow-wrap:anywhere }

  /* ---- "on leave today", the green sibling of .e2-temp ---- */
  body.soft .e2-today{ color:var(--ok); font-weight:700 }

  /* ---- Approve / Reject, directly on a pending card ---- */
  body.soft .e2-fix.e2-ok{ background:var(--ok) }
  body.soft .e2-fixg.e2-bad{ color:var(--danger) }

  /* ============================================================
     TIMESHEET MOBILE — the same rail a SIXTH time (owner-approved,
     DataSync-Timesheet-Rail-Demo.html, 1 Aug). Screen 6 of 10.

     Almost everything it needs already exists above and is reused
     verbatim, not reinvented: .e2-trail / .e2-crumb / .e2-crumb-est /
     .e2-sep / .e2-ct, .e2-hero / .e2-hrow / .e2-k / .e2-cnt,
     .e2-srch, .e2-chips + .e2-chip, .e2-est / .e2-all, .e2-grid,
     .e2-card / .e2-top / .e2-nm / .e2-side / .e2-dots, .e2-temp,
     .e2-note (Leave's quiet inset box), .e2-share / .e2-sh (the
     Documents hero's export row), .p2-mstep (the Payroll month
     stepper), .e2-empty and the whole .e2-sheet.

     Only THREE things below are genuinely new, because nothing in the
     five shipped rails had them:

       .e2-ts        the timesheet card. Same thin proportions as
                     .e2-att — see the note on it below.
       .t2-net       NET PAYABLE, in the card's right-hand column. It is
                     the figure this whole screen exists for and the
                     approved demo put it exactly here, big, beside the
                     name. It replaces the status pill the other rails
                     put in that slot, so .e2-side still holds two items
                     and the card stays the same height.
       .t2-formula   the salary formula banner the demo insisted on
                     keeping — the same sentence the desktop page-head
                     carries. It is the inset treatment .e2-note and the
                     search box already use, so it reads as something
                     written INTO the hero rather than a card on top of
                     it. It is NOT sticky; the block below holds that.

     Plus ONE line so the SHIPPED .p2-mstep can be reused as it stands:
     the stepper's own rule flips the chevron for the "previous" button
     by its data-p2m hook, and this rail's hook is data-t2m. Written as
     its own rule rather than added to the payroll selector so nothing
     mobile-payroll-test.mjs parses out of that block changes.

     No new colours. --primary-ink, --muted, --text, --bg, --border and
     --nm-in only, at tints already used above. Nothing here is
     position:sticky, and no rule adds a figure to another figure —
     there is no arithmetic in a stylesheet, but there is none in the
     markup it styles either (see the note in app.js).
     ============================================================ */

  /* ---- the timesheet card ------------------------------------------------
     THE SAME HEIGHT THE OWNER APPROVED for Attendance (1 Aug): 7px 12px
     shell, 7px between cards, 38px avatar, 11.5px name, 10.5px sub-line,
     4px divider gaps, 30px ⋯. Per 3.38 this thin card is the standard for
     every module after Employees — it is NOT re-derived here, the values
     are copied from .e2-att above, and mobile-timesheet-test.mjs parses BOTH
     rules out of this file and fails if a single one of them ever differs,
     exactly as mobile-payroll-test.mjs and mobile-leave-test.mjs already do.

     The demo's own card was 13px 14px with a 42px avatar and a 15px name —
     it predates five rounds of size decisions, so the house style wins. */
  body.soft .e2-card.e2-ts{ padding:7px 12px; margin-bottom:7px }
  body.soft .e2-card.e2-ts .e2-top{ gap:9px }
  body.soft .e2-card.e2-ts .avatar{ width:38px !important; height:38px !important; font-size:12px !important }
  body.soft .e2-card.e2-ts .e2-nm b{ font-size:11.5px; line-height:1.15 }
  body.soft .e2-card.e2-ts .e2-nm small{ font-size:10.5px; line-height:1.2 }
  body.soft .e2-card.e2-ts .e2-grid{ margin-top:4px; padding-top:4px }
  body.soft .e2-card.e2-ts .e2-grid small{ line-height:1.15 }
  body.soft .e2-card.e2-ts .e2-grid b{ line-height:1.15 }
  body.soft .e2-card.e2-ts .e2-dots{ width:30px; height:30px; border-radius:9px }
  body.soft .e2-card.e2-ts .e2-side{ gap:5px }
  body.soft .e2-card.e2-ts .badge{ font-size:9px; padding:2px 7px; border-radius:7px }
  body.soft .e2-card.e2-ts .e2-grid .e2-warn{ color:var(--warn) }
  body.soft .e2-card.e2-ts .e2-grid .e2-bad{ color:var(--danger) }

  /* ---- Net payable, in the right-hand column ----
     Sized to the sub-line, not to a headline: at 390px the name needs the
     room more than the figure does, and the figure is already the only
     coloured thing on the card. */
  body.soft .t2-net{ display:block; text-align:right; flex:none; min-width:0 }
  body.soft .t2-net b{
    display:block; font-size:12.5px; font-weight:800; line-height:1.15;
    color:var(--primary-ink); white-space:nowrap;
  }
  body.soft .t2-net small{ display:block; font-size:9px; line-height:1.2; color:var(--muted) }

  /* ---- the salary formula banner ---- */
  body.soft .t2-formula{
    margin-top:9px; padding:7px 10px; border-radius:9px; background:var(--bg);
    box-shadow:var(--nm-in); font-size:10.5px; line-height:1.35; color:var(--muted);
  }
  body.soft .t2-formula b{ color:var(--primary-ink); font-weight:700 }

  /* ---- the shipped month stepper, with this rail's own hook ---- */
  body.soft .p2-mstep button[data-t2m="-1"] svg{ transform:rotate(180deg) }

  /* ============================================================
     NOTHING FREEZES ON A PHONE (owner, 1 Aug): "cancel the sticky mood from
     all cards, from all types filter option, in mobile view only, make them
     all scrollable. desktop version will remain unchanged."

     On a 390px screen the frozen page heading and filter bar were eating a
     third of the viewport before a single row appeared. Everything here now
     scrolls away with the content.

     Written WITHOUT the `body.soft` prefix and with !important on purpose:
     `.soft` is only added once a session token exists (app.js ~2459), and an
     override whose whole job is to beat an existing rule should not also
     depend on a class that arrives a moment later.

     DELIBERATELY LEFT STICKY — these are not cards or filters:
       .topbar          the app bar (3.15 measured its height on purpose)
       .sidebar         desktop only; a drawer on phones
       .modal-foot      keeps Save/Cancel reachable in a bottom-sheet modal
       .emp-page head/foot   3.15's full-page Add Employee form
       .ds2-nav / .pubm-head public site, not the logged-in app
     ============================================================ */
  .content > .page-head,
  .content > .sticky-filter,
  .toolbar.sticky-filter,
  .portal-bar,
  .portal-tabs,
  .loc-head,
  .ts-hist .ts-scroll thead th{
    position:static !important; top:auto !important; z-index:auto !important;
  }
}

/* ============================================================
   WORKING-DAY PICKER — ONE ROW ON A PHONE (3 Aug, owner)

   His words: "this date selector tick box not looking professional in
   mobile version ... fix this in a single row ... make good looking".
   Screenshot showed the Edit Employee form: seven native checkboxes at
   `gap:12px` with `flex-wrap:wrap`, so Sun–Fri took the full width (Fri
   clipped at the edge) and Sat dropped onto a second line by itself.

   THREE PLACES CARRY THIS PICKER, and §3.4 says the working week has
   three levels with ONE resolver — so the three must look identical:
     .empwd  Employee form  (app.js ~6910)  — the one he photographed
     .wdchk  Settings, company week (~13583)
     .dwd    Settings, per department (~13799)
   Every one of them now emits .wd-row > label.wd-day > input + span.

   WHY A <span> AROUND THE DAY NAME. Styling a label from its own child's
   :checked state needs :has(), and §3.38 already records that the old
   Android WebViews these workers' phones run do not support it. The
   adjacent-sibling selector `input:checked + span` is supported
   everywhere, but it needs a real element to point at — the day name was
   a bare text node. Wrapping it costs nothing: the label is still a flex
   row with the same `gap`, so DESKTOP RENDERS IDENTICALLY.

   WHY THE INLINE STYLES WERE LEFT IN PLACE. Each label still carries its
   original `style="display:flex;..."`. Inline styles outrank a stylesheet
   rule, so above 820px the desktop layout is not merely unchanged — it is
   structurally incapable of changing. Everything below therefore needs
   !important, which is the price of that guarantee and is deliberate.

   THE LOGIC IS UNTOUCHED. Same inputs, same classes, same values, same
   `.empwd:checked` / `.wdchk:checked` / `.dwd:checked` readers. The
   checkbox is moved out of sight with opacity, NOT `display:none`, so it
   stays focusable, stays in the tab order and stays in form serialisation.

   THE CHIP IS NOT A NEW SHAPE. It copies `.e2-chip` (line ~3047) value
   for value — 34px tall, 11.5px/700, var(--nm-out-sm) raised, and
   var(--nm-in) inset when on. That is the same chip the Attendance,
   Leave, Payroll and Timesheet rails already use, so this cannot drift
   into being a seventh style. Ticked uses --primary-grad (the gold of
   §3.41), the same fill as every primary control.
   ============================================================ */
@media (max-width:820px){
  .wd-row{
    display:flex !important;
    flex-wrap:nowrap !important;      /* the whole point: never a second line */
    gap:5px !important;
    width:100%;
    padding:3px 0 !important;
  }
  /* Seven equal columns. `min-width:0` is what actually lets them shrink —
     a flex item's default min-width:auto refuses to go below its content
     and is the reason the old row overflowed instead of compressing. */
  .wd-row .wd-day{
    flex:1 1 0 !important;
    min-width:0 !important;
    display:block !important;
    gap:0 !important;
    position:relative;
    cursor:pointer;
    -webkit-tap-highlight-color:transparent;
  }
  .wd-row .wd-day > input{
    position:absolute; left:0; top:0;
    width:100%; height:100%;
    margin:0; opacity:0; cursor:pointer;
  }
  .wd-row .wd-day > input + span{
    display:flex; align-items:center; justify-content:center;
    height:34px;                      /* .e2-chip's height — the tap target */
    padding:0 2px;
    border-radius:11px;
    font:700 11.5px var(--font-brand),sans-serif;
    line-height:1;
    color:var(--text);
    background:var(--bg);
    box-shadow:var(--nm-out-sm);
    overflow:hidden;
  }
  .wd-row .wd-day > input:checked + span{
    background:var(--primary-grad);
    color:#fff;
    box-shadow:var(--nm-in);
  }
  /* Keyboard users can still see where they are once the box is invisible. */
  .wd-row .wd-day > input:focus-visible + span{
    outline:2px solid var(--primary);
    outline-offset:1px;
  }
  /* Per-department row: the 130px name label was sharing the line with all
     seven chips. Name on its own line, chips get the full width. */
  .wd-dept{
    flex-direction:column !important;
    align-items:stretch !important;
    gap:6px !important;
  }
  .wd-dept > b{ min-width:0 !important }
}

/* ============================================================
   THE MENU GRID — MOHRE-style service cards (4 Aug, owner)

   Tapping Menu in the dock used to open the side drawer. It now opens
   VIEWS.menu: one card per module, three columns. Settled over six rounds
   against a rendered preview, and every number below is one HE chose —
   do not "tidy" them.

   HIS CORRECTIONS, IN ORDER, ALL STILL BINDING:
   1. 4 columns -> 3. At 4 the labels broke mid-word ("Dashboar/d",
      "Timeshee/t"). That was `word-break:break-word`, now `normal`:
      labels wrap at SPACES ONLY. At 3 columns nothing needs shortening,
      so every label is his own wording, untouched.
   2. "NOT MAKE DOUBLE CARD SMALL AND BIG. EVERY MODULE WILL BE ONLY ONE
      CARD AND ON IT ICON AND MODULE NAME WILL BE FLAT." The icon had its
      own embossed square inside the card. There is now EXACTLY ONE raised
      surface per tile — the card. The icon carries no background, no
      radius and no shadow. If you ever add one back, you have undone this.
   3. Card two steps smaller, gap larger AND EQUAL. The gap was 9px between
      rows but 8px between columns; it is now a single 12px value, which is
      why it is written as one number and not two.
   4. No header on this page: no greeting, no date, no page-head. The
      welcome line belongs to the dashboard, a different screen.
   5. The PNG icons STAY. `documents`, `eye`, `edit`, `userDelete`,
      `userOff` are <img> not <svg> (`ai` is Ameera, per 3.35 rule 2). He
      was offered drawn line icons and said "no need, keep same my png".
      Consequence to accept, not fix: a PNG cannot take currentColor, so
      those tiles do not turn gold. That is his decision.

   Prefixed `dg-` and only reachable on a phone: `.m-dock` is display:none
   above 820px (line ~2507), so `_menu` cannot be pressed on a desktop.
   ============================================================ */
@media (max-width:820px){
  /* No header on the grid. The topbar is in the shell, so the view cannot
     hide it — renderView() puts the route on <body> and this reads it. */
  body[data-mroute="menu"] .topbar{ display:none !important }
  body[data-mroute="menu"] .content{ padding-top:0 !important }

  .dg-wrap{ padding:14px 12px 20px }
  /* Section names are gold filled pills, not grey text (owner, 4 Aug:
     "make the section name with our golden fill button style"). Same
     --primary-grad and white ink as every primary button in the app, and
     `inline-flex` so the pill hugs its words instead of running the full
     width like a heading. */
  .dg-grp{
    display:inline-flex; align-items:center; width:fit-content;
    font:700 10.5px var(--font-brand),sans-serif; color:#fff;
    text-transform:uppercase; letter-spacing:.6px;
    background:var(--primary-grad); padding:5px 13px; border-radius:20px;
    box-shadow:var(--nm-out-sm);
    margin:16px 0 10px 2px;
  }
  .dg-wrap > .dg-grp:first-child{ margin-top:0 }

  /* ONE value, so rows and columns can never drift apart again. */
  .dg-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:12px }

  .dg-tile{
    min-width:0; border:none; cursor:pointer; font-family:inherit;
    display:flex; flex-direction:column; align-items:center;
    justify-content:flex-start; gap:4px; padding:7px 3px 6px;
    background:var(--bg); border-radius:12px; box-shadow:var(--nm-out-sm);
    -webkit-tap-highlight-color:transparent;
    transition:box-shadow .12s ease, transform .12s ease;
  }
  /* The "clicky animation" he asked for. :active fires on touch-DOWN, so the
     press is felt before any JS runs. .dg-hit is added by the click handler
     and holds the pressed look for the 120ms before the next screen paints —
     without it the tap reads as dead on a slow phone. */
  .dg-tile:active,
  .dg-tile.dg-hit{ box-shadow:var(--nm-in); transform:scale(.97) }

  /* The icon sits FLAT on the card. No background, no radius, no shadow. */
  .dg-ic{ display:flex; align-items:center; justify-content:center; color:var(--primary) }
  .dg-ic svg{ width:19px; height:19px }
  .dg-ic img{ width:19px; height:19px; object-fit:contain }   /* his 5 PNGs */

  .dg-lb{
    font:500 9.5px var(--font-brand),sans-serif; color:var(--text);
    line-height:1.22; text-align:center;
    word-break:normal; overflow-wrap:anywhere; hyphens:none;
    display:flex; align-items:center; justify-content:center; min-height:22px;
  }
}

/* ============================================================
   THE PHONE TOPBAR (4 Aug, owner) — six instructions:

     "Menu button makes HOME ICON, remove the writing Menu"
     "language button make little more smaller"
     "Light/dark mood button make visibile with icon"
     "re-arrange all top button in good looking"
     "This top button will be only in dashboard"
     "in others module and inside of all pages will not show"

   PHONE ONLY. Everything is inside @media (max-width:820px) and the
   desktop bar is untouched — desktop has NO bottom dock, so stripping its
   controls would leave it with no navigation at all.

   WHAT SHOWS WHERE
     Dashboard : Home · title · Ameera · bell · language · location · theme · avatar
     Elsewhere : back arrow · title. Nothing else.

   The switch is `body[data-mroute]`, published by renderView(). The topbar
   is built by renderShell and cannot know the route by itself.

   HOME OPENS THE DRAWER, IT DOES NOT GO HOME. He was asked directly and
   chose this. It is knowingly a misleading icon — do not "fix" it, and do
   not be surprised by it. The Menu grid on the dock is the other way in.

   THE LOCATION CHIP IS GONE FROM THE PHONE ENTIRELY (owner, 4 Aug):
   "its unnecessary in inside while another location selection option
   exist before login". He is right, and it was checked before removing:
   the LOGIN screen carries its own picker (`#locPick`, app.js ~836) and
   app.js ~852 REFUSES TO LOG YOU IN without one — "Please select your
   location first." So the location can never be unset; the topbar chip
   was only a shortcut for changing it mid-session.

   app.js still calls that chip "always visible and always changeable —
   an override that silently changes which documents are required must
   never be hidden". That comment now describes DESKTOP only, which keeps
   the chip untouched. The cost on a phone: changing operating location
   mid-session means logging out and back in. That is the trade he chose.

   ONE ROW, NOT TWO. The old bar was `flex-wrap:wrap` with a `row-gap`, so
   on a 390px phone it silently became two rows — that is the "not good
   looking" he means. `nowrap` plus icon-only buttons fits it in one:
   34 + 6×30 + gaps + padding ≈ 293px, leaving ~97px for the title.
   ============================================================ */
@media (max-width:820px){
  .topbar{ flex-wrap:nowrap !important; row-gap:0 !important; gap:6px !important }

  /* Home: GOLD FILL, WHITE ICON (owner, 4 Aug) — square, no text. */
  .topbar .hamburger{
    background:var(--primary-grad) !important; color:#fff !important;
    width:34px !important; min-width:34px; height:34px !important;
    min-height:34px !important; padding:0 !important;
    border-radius:11px; justify-content:center;
  }
  .ham-label{ display:none !important }          /* the word is gone for good */

  /* THE TITLE'S `order`, WHICH COST A PREVIEW ROUND.
     Line ~246 deliberately gives #pageTitle `flex:1 1 100%; order:9` so it
     takes its OWN ROW — that was the old two-row bar. Switching the bar to
     `nowrap` did not undo it: `order:9` still applied, because the rule that
     replaced `flex` never mentioned `order`. Result: the title rendered AFTER
     the avatar, at the far right, exactly as his screenshot showed.
     Both properties must be reset, not just the one you notice. */
  .topbar h1#pageTitle{
    order:0 !important; flex:1 1 auto !important; min-width:0;
    font-size:15px; font-weight:700; letter-spacing:-.01em;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    margin:0; line-height:1.2;
  }

  /* Right-hand controls, all one size so the row reads as a set.
     `justify-content:center` matters more than it looks: .chip is a flex row
     with a `gap`, and once its <small> label is hidden the lone icon sat
     LEFT in a 30px box — which is the off-centre moon he spotted. Base .chip
     never sets justify-content because it normally has icon + text. */
  .topbar .icon-btn,
  .topbar .chip{ width:30px !important; min-width:30px; height:30px !important;
                 min-height:30px !important; padding:0 !important; flex:none;
                 justify-content:center; align-items:center; gap:0 !important }
  .topbar .icon-btn svg,
  .topbar .chip svg{ width:16px; height:16px }
  .topbar #userBtn .avatar{ width:24px !important; height:24px !important; font-size:9.5px !important }

  /* Language: "a little more smaller". A select cannot be 30px round and
     still show its arrow, so it keeps a small pill shape and drops to the
     narrowest width the two-letter codes need. */
  .topbar #langSel{
    width:52px !important; min-width:52px; max-width:52px;
    height:30px !important; min-height:30px !important;
    padding:0 4px !important; font-size:11px !important; flex:none;
  }
  /* Location: removed from the phone on every screen, dashboard included.
     Desktop still shows it — this rule is inside the 820px query. */
  .topbar #locChip{ display:none !important }
  /* Theme: the icon carries the meaning (sun in dark mode, moon in light),
     so the word beside it is dropped rather than the icon. */
  .topbar #themeBtn small{ display:none !important }

  /* Back arrow: phone only, away from the dashboard, and GOLD FILL with a
     WHITE icon (owner, 4 Aug) so it matches Home rather than looking like a
     disabled grey control. */
  .tb-back{ display:none }
  body[data-mroute]:not([data-mroute="dashboard"]) .topbar .tb-back{
    display:inline-flex !important;
    background:var(--primary-grad) !important; color:#fff !important;
    width:34px !important; min-width:34px; height:34px !important;
    min-height:34px !important; padding:0 !important;
    border-radius:11px; justify-content:center; align-items:center;
  }

  /* EVERYTHING except back + title disappears off the dashboard. */
  body[data-mroute]:not([data-mroute="dashboard"]) .topbar .hamburger,
  body[data-mroute]:not([data-mroute="dashboard"]) .topbar #aiBtn,
  body[data-mroute]:not([data-mroute="dashboard"]) .topbar #bellBtn,
  body[data-mroute]:not([data-mroute="dashboard"]) .topbar #langSel,
  body[data-mroute]:not([data-mroute="dashboard"]) .topbar #themeBtn{ display:none !important }

  /* ==== MLOGOUT (19 Aug 2026) — #userBtn WAS IN THE LIST ABOVE, AND A SUPER
     ADMIN COULD THEREFORE NEVER SIGN OUT ON A PHONE. ====================
     He found it: "Not founding any logout option" in the mobile Super Admin.

     #userBtn is the ONLY control that opens the menu containing Logout
     (openProfileMenu, app.js ~8279). The rule above hides it on every route
     whose name is not literally `dashboard`.

     ⛔ A COMPANY ADMIN's home route IS `dashboard`, so he kept ONE screen where
     he could sign out. A SUPER ADMIN's home route is `super-dashboard`
     (app.js:2340) — it never matches, so the button was hidden EVERYWHERE.

     ⛔ AND THE FALLBACK WAS GONE TOO. The same rule hides `.hamburger`, so
     there was no drawer, so `#sideUser` — the other way into that menu — could
     not be reached either. `.m-dock` is behind D3_ENABLED = false. The result
     was NO WAY AT ALL for a Super Admin to log out on a phone, on an account
     that holds every company's data, in a system whose own comment says
     "Sessions stay active until the person chooses Logout. There is no idle
     timeout." (app.js:1000)

     His decision, 19 Aug: the account button stays visible on EVERY phone
     screen, for EVERYONE — not just the Super Admin.

     ⛔ THE OTHER FIVE IN THAT RULE ARE DELIBERATELY LEFT HIDDEN. The bell,
     language, theme and AI buttons were hidden by design for narrow screens,
     and `.hamburger` is hidden because `.tb-back` REPLACES it off-dashboard
     (see the rule ~15 lines above). Unhiding those would be redesigning his
     top bar under cover of a bug fix. Only the logout path is restored. */
  body[data-mroute] .topbar #userBtn{ display:inline-flex !important }

  /* The menu grid keeps its own rule from 3.53: no bar there at all. */
}

/* ============================================================
   INSIDE THE MODULES — reports, dialogs, action rows (4 Aug)

   From nine phone screenshots. His words: "we have decorate in mobile
   version from outside of the module, inside of module and reporting
   style is still ugly and not fair."

   He was right, and the reason is structural: the mobile work so far had
   all been the SHELL — dock, menu grid, topbar, six list rails. The
   insides of the modules had never had a single phone rule written for
   them. Everything below is presentation only: no logic, no permissions,
   no payroll maths, and NOTHING that reaches the printed page.
   ============================================================ */
@media (max-width:820px){

  /* ---- 1. A4 REPORTS WERE UNREADABLE -----------------------------
     Four of the nine screenshots showed text running ONE LETTER PER
     LINE, vertically:  A / L / A / M / G / I / R

     Cause, style.css ~942:  .rep-fit td,.rep-fit th{word-break:break-word}
     A six-column A4 table on a 390px phone gives each column ~50px, and
     break-word is then free to break INSIDE a word — so it breaks after
     every character.

     THIS IS THE SAME RULE THAT PRODUCED "Dashboar/d" IN THE MENU GRID
     the same night. It was fixed there and not here, because these
     tables are deliberately skipped by the card converter (app.js 2531)
     so that printing is never destroyed — which left them with no phone
     rules at all.

     THE DECISION: a print document is not re-flowed to fit a phone. It
     is shown at its TRUE WIDTH and scrolled sideways, exactly the way
     every PDF reader behaves. Swiping a wide report is normal; a column
     of single letters is not.

     Everything here is inside the phone media query, so @media print and
     the generated PDF cannot be affected. */
  .a4-report .modal-body,
  .af-doc,
  .rep-scroll{
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;   /* momentum scroll on iOS */
  }
  .rep-fit,
  .a4-report table,
  .af-doc table{
    min-width:720px;                    /* keeps columns in proportion */
    table-layout:auto;
  }
  /* Wrap at SPACES. overflow-wrap is the safety net for one word longer
     than its column — it can never break a word that fits. */
  .rep-fit td, .rep-fit th,
  .a4-report td, .a4-report th{
    word-break:normal !important;
    overflow-wrap:break-word;
    hyphens:none;
    white-space:normal;
  }

  /* ---- 2. ACTION BUTTONS TRUNCATED TO NONSENSE -------------------
     Users & Roles showed:  Assi | Bloc | Rese
     Those are Assign, Block and Reset. style.css:1365 forces every
     .btn to `overflow:hidden;text-overflow:ellipsis;white-space:nowrap`,
     and inside a narrow converted card the row is squeezed below its
     content width, so the labels are simply cut.

     A truncated verb on a DESTRUCTIVE control is a safety problem, not
     a cosmetic one — "Bloc" and "Rese" sit side by side and both are
     irreversible to the person pressing them. Full words on two rows
     beat four cropped words on one. */
  table.m-cards td[data-label] > div,
  table.m-cards td:last-child,
  .m-cards-box .row-actions{
    display:flex; flex-wrap:wrap; gap:6px; justify-content:flex-end;
  }
  table.m-cards .btn,
  table.m-cards .chip{
    overflow:visible !important;
    text-overflow:clip !important;
    max-width:none !important;
    flex:0 0 auto;
  }

  /* ---- 3. DIALOG FOOTERS CLIPPED THEIR BUTTONS -------------------
     The Passport expiry dialog read:
        Close  CSV  Excel  PDF  are PD  Print
     "Share PDF" had been clipped at BOTH ends. Six buttons do not fit
     390px. A button you cannot read is a button you will not press. */
  .modal-foot{
    flex-wrap:wrap !important;
    gap:8px !important;
    justify-content:flex-end;
  }
  .modal-foot .btn,
  .modal-foot .chip{
    overflow:visible !important;
    text-overflow:clip !important;
    max-width:none !important;
    flex:0 0 auto;
  }

  /* ---- 4. A STICKY FOOTER WAS COVERING THE LAST FIELD ------------
     In the Attendance bulk form the Cancel/Save bar sat on top of the
     "Weekly offs & public holidays" field.

     Same family as 3.51 (the unreachable Save button) but the opposite
     direction: there the docker covered the footer, here the footer
     covers the content. 3.51 solved its half with ONE shared constant
     so the two could not drift; --m-foot-h is the same idea for this
     half. Do not hard-code a second copy of this number anywhere. */
  :root{ --m-foot-h: 64px }
  .modal-body{ padding-bottom:calc(var(--m-foot-h) + 12px) }
  /* The employee page already manages its own footer offset (3.51), so
     it must not be given a second one on top. */
  .modal-bg.emp-page .modal-body{ padding-bottom:12px }
}

/* ---- 5. ICONS INSIDE BUTTONS HAD NO SIZE AT ALL ------------------
   The Timesheet dialog rendered "A4 Report" as an enormous gold block
   with a printer icon several times taller than its text.

   ICONS.print is an <svg> with a viewBox and NO width or height, and a
   grep of the whole stylesheet found exactly one rule for icons inside
   buttons:
       .btn svg,.btn.ghost svg{color:#fff}     ← colour only, no size
   With no intrinsic size and no CSS size, the browser falls back to its
   default replaced-element size or stretches the flex item.

   This is a LATENT bug on every button carrying an icon, not just that
   one, so it is fixed centrally and OUTSIDE the media query — a giant
   icon is not more acceptable on a desktop. */
.btn > svg, .btn.ghost > svg, .chip > svg, .icon-btn > svg{
  width:16px; height:16px; flex:none;
}

/* AN ICON IN PLAIN TEXT — the giant person on the Attendance screen (5 Aug).
   Every rule above sizes icons by their CONTAINER: .btn, .chip, .icon-btn,
   .nav-item and so on. An `<svg>` carrying only a viewBox and no width has no
   intrinsic size, so in ANY other container it stretches to the full width of
   its box. The emoji sweep put `${ICONS.users}` inside a plain
   `<b>Employee Attendance Calendar</b>` and it rendered about 300px tall. He
   photographed it on two different companies within minutes of the deploy.
   This is the THIRD time an unsized icon has shipped — §3.56 records the same
   thing with a printer icon taller than its button in the Timesheet dialog,
   fixed for that one case only.
   So this is the general answer, not another single-case patch: an icon sitting
   directly in a text element takes the size of the text around it. Specificity
   is (0,1,1) at most, so every rule above still wins where it applies, and
   nothing already correct moves. */
b > svg, strong > svg, h1 > svg, h2 > svg, h3 > svg, h4 > svg,
label > svg, small > svg, td > svg, th > svg, p > svg, li > svg, a > svg,
span > svg{
  width:1em; height:1em; flex:none; vertical-align:-0.13em; margin-right:.4em;
}
/* Three more containers the sweep put an icon into that no rule had sized:
   the day-marker "View Log" button, the public thank-you tick, and the
   chat attachment chip. Checked mechanically, not by eye — every ${ICONS.x}
   site in app.js was matched against the class that sizes it, and these were
   the ones with no owner. */
.cal-opt > svg{ width:15px; height:15px; flex:none; vertical-align:-0.15em; margin-right:.35em }
.ds-thanks-ic > svg{ width:34px; height:34px }

/* ============================================================
   THINNER CARDS, SMALLER CONTROLS (4 Aug, after he saw the preview)

     "Make the card more thinner as Employee Card style"
     "Make the action button more smaller"
     "In report timeline make the font size more smaller"

   The converted cards were built before the thin standard existed. §3.38
   settled that standard from his own screenshot — 7px/12px padding, 7px
   between cards, 11.5px name — and said it applies "from Attendance
   onward". The generic converter never got it. Now it matches, so a
   converted screen and a hand-built rail sit at the same density.
   ============================================================ */
@media (max-width:820px){
  /* Card: 12/14 -> 7/12, gap 10 -> 7, radius 16 -> 12. The §3.38 numbers. */
  body.soft table.m-cards > tbody > tr{
    padding:7px 12px !important; margin-bottom:7px !important; border-radius:12px !important;
  }
  /* Rows inside the card: 5px -> 3px, 13.5px -> 11.5px (the §3.38 name size).
     `word-break` also goes from break-word to normal — the SAME defect as the
     reports and the menu grid. It was breaking values mid-word inside cards;
     `overflow-wrap:anywhere` beside it is the safety net for one long token. */
  body.soft table.m-cards > tbody > tr > td{
    padding:3px 0 !important; font-size:11.5px !important;
    word-break:normal !important;
  }
  body.soft table.m-cards > tbody > tr > td[data-label]::before{
    font-size:10px !important; line-height:1.35;
  }
  body.soft table.m-cards > tbody > tr > td:first-child{
    padding-bottom:5px !important; margin-bottom:4px !important;
  }
  body.soft table.m-cards > tbody > tr > td:first-child b{ font-size:12.5px }

  /* Action buttons: the row he circled. Down to the §3.38 "Style B" row
     button — 11.5px label on a 30px control — from the full-size .btn. */
  body.soft table.m-cards .btn,
  body.soft table.m-cards .btn.sm{
    min-height:28px !important; height:28px !important;
    padding:0 11px !important; font-size:11px !important;
    border-radius:9px !important;
  }
  body.soft table.m-cards .badge,
  body.soft table.m-cards .chip{ font-size:10.5px !important; padding:2px 8px !important }

  /* Report body: smaller type so more of a wide report is readable per
     swipe. Header stays slightly larger so the columns remain findable. */
  .rep-fit td, .a4-report td, .af-doc td{ font-size:10.5px !important; padding:4px 6px !important }
  .rep-fit th, .a4-report th, .af-doc th{ font-size:10px !important; padding:5px 6px !important }
  .a4-report .pay-table th{ font-size:10px !important }
}

/* ============================================================
   ⋯ ACTIONS, AND A ONE-ROW REPORT FOOTER (4 Aug, second pass)

     "action button more smaller in one row below reporting timeline"
     "in employee's card, fold the action button in three dot button
      in right-corner"
   ============================================================ */
@media (max-width:820px){

  /* ---- the ⋯ fold ------------------------------------------------
     The action buttons stay in the DOM exactly where their view put
     them — hidden, never removed — and the sheet PROXIES to them by
     calling .click(). Hiding rather than deleting is what lets any
     view keep a reference to its own button. */
  body.soft table.m-cards > tbody > tr > td[data-mcfold] > *:not(.mc-dots){
    display:none !important;
  }
  /* THE ⋯ SITS IN THE CARD'S TOP-RIGHT CORNER (owner, 4 Aug: "three dot menu
     will be at the right corner of the card"). It was left in the flow, which
     put it on its own row UNDER the last field — floating in the middle of
     nowhere, which is what he saw.

     The cell is collapsed to zero height instead of hidden, because the ⋯ is
     inside it and `display:none` on the parent would take the button with it.
     The row becomes the positioning context, so the corner is the CARD's
     corner and not the cell's. Offsets are the card's own padding (7px/12px,
     §3.38) so the button lines up with the name, not near it. */
  body.soft table.m-cards > tbody > tr{ position:relative }
  body.soft table.m-cards > tbody > tr > td[data-mcfold]{
    display:block !important; position:static;
    height:0 !important; min-height:0 !important; padding:0 !important;
    margin:0 !important; border:none !important; overflow:visible;
  }
  /* Keep the name clear of the button rather than letting it slide underneath.
     Uses a CLASS the folder adds, NOT :has() — §3.38 records that the old
     Android WebViews these workers' phones run do not support :has(), and the
     whole mobile stylesheet is written around that. I reached for it here out
     of habit and caught it before it shipped. */
  body.soft table.m-cards > tbody > tr.mc-folded > td:first-child{
    padding-right:38px;
  }
  body.soft .mc-dots{ position:absolute; top:7px; right:12px; z-index:2 }
  /* The label ("ACTIONS") goes too — a ⋯ in the corner needs no caption,
     and the rails do not have one either. */
  body.soft table.m-cards > tbody > tr > td[data-mcfold]::before{ display:none !important }
  /* 30px, matching .e2-dots on a non-person card in §3.38. */
  body.soft .mc-dots{ width:30px !important; height:30px !important; flex:none }
  body.soft .mc-dots svg{ width:16px; height:16px }

  /* ---- report footer: ONE row -------------------------------------
     He asked for a single row rather than the two the wrap produced.
     It scrolls instead of wrapping, because six buttons cannot fit
     390px at a readable size — and a row that scrolls is honest, while
     a row that clips gave him "are PD". Same device as .e2-chips. */
  .a4-report .modal-foot,
  .af-doc ~ .modal-foot,
  .modal-foot.rep-foot{
    flex-wrap:nowrap !important;
    overflow-x:auto; -webkit-overflow-scrolling:touch;
    padding-bottom:6px; gap:6px !important;
  }
  .a4-report .modal-foot::-webkit-scrollbar{ height:0 }
  .a4-report .modal-foot .btn,
  .modal-foot.rep-foot .btn{
    height:26px !important; min-height:26px !important;
    padding:0 10px !important; font-size:10.5px !important;
    border-radius:8px !important; flex:0 0 auto;
  }
  .a4-report .modal-foot .btn svg,
  .modal-foot.rep-foot .btn svg{ width:13px; height:13px }
}

/* ============================================================
   DOCUMENT-TYPE FILTER ON THE PHONE (4 Aug, owner)

   "add type filtering option to see expirer/missing items in Document
   module in mobile version also"

   Styled to match .e2-srch exactly — the search box that already sits
   under the primary CTA on every rail (§3.38) — so the two read as one
   pair of controls rather than a select bolted beside a search box.
   ============================================================ */
@media (max-width:820px){
  body.soft .e2-sel{
    display:flex; align-items:center; gap:8px;
    background:var(--bg); border-radius:12px; box-shadow:var(--nm-in);
    padding:0 12px; min-height:38px; margin-bottom:12px;
  }
  body.soft .e2-sel > span{ display:flex; color:var(--muted); flex:none }
  body.soft .e2-sel select{
    flex:1; min-width:0; border:none; background:none; outline:none;
    font:500 13px var(--font-brand),sans-serif; color:var(--text);
    padding:8px 0; -webkit-appearance:none; appearance:none;
    /* The native arrow is drawn by the OS and cannot take the brand colour;
       a caret is painted here instead so it matches every other control. */
    background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),
                     linear-gradient(135deg,var(--muted) 50%,transparent 50%);
    background-position:calc(100% - 11px) 50%, calc(100% - 6px) 50%;
    background-size:5px 5px, 5px 5px;
    background-repeat:no-repeat;
    padding-right:22px;
  }
}

/* ============================================================
   TYPE-TO-SEARCH DROPDOWNS, PHONE ONLY (4 Aug, owner)

   "convert all this type of drop down selector into text-to-search
    field, then menu wiring will remain same, only on mobile version"

   The <select> is still there and still submits. It is hidden with
   OPACITY, never display:none — a `required` select that is display:none
   makes the browser refuse to submit with "an invalid form control is
   not focusable", and the user sees a form that does nothing when they
   press Save. §3.52 hid the working-day checkboxes the same way.

   Desktop keeps the native dropdown: this whole block is inside the
   phone media query and mobileSearchSelects() returns early above 820px.
   ============================================================ */
@media (max-width:820px){
  .ms-wrap{ position:relative; display:block; width:100% }
  /* Present for the form, invisible to the eye, still focusable. */
  .ms-wrap > select{
    position:absolute; left:0; top:0; width:100%; height:100%;
    opacity:0; pointer-events:none; z-index:-1;
  }
  .ms-input{ width:100%; }
  .ms-list{
    display:none; position:absolute; left:0; right:0; top:calc(100% + 4px);
    z-index:70; max-height:46vh; overflow-y:auto; -webkit-overflow-scrolling:touch;
    background:var(--bg); border-radius:12px; box-shadow:0 10px 30px rgba(16,24,40,.22);
    padding:5px;
  }
  .ms-wrap.ms-open .ms-list{ display:block }
  .ms-opt{
    display:block; width:100%; text-align:left; border:none; background:none;
    padding:9px 10px; border-radius:9px; cursor:pointer;
    font:500 12.5px var(--font-brand),sans-serif; color:var(--text);
    line-height:1.3; white-space:normal; word-break:normal; overflow-wrap:anywhere;
  }
  .ms-opt:active{ box-shadow:var(--nm-in) }
  .ms-opt.ms-on{ background:var(--primary-grad); color:#fff }
  .ms-more{
    padding:8px 10px; font:600 11px var(--font-brand),sans-serif; color:var(--muted);
  }
}

/* ============================================================
   STOP THE PHONE SELECTING TEXT — GLOBAL (4 Aug, second attempt)

   FIRST ATTEMPT WAS WRONG AND HE CAUGHT IT. I listed the selectors I
   thought were tappable — .e2-root, .m-dock, .btn and so on — and then
   deliberately RE-ENABLED selection on the value text so numbers could
   still be long-pressed to copy. Two things were wrong with that:

     1. THE LIST HAD HOLES. Every modal, the Employee Profile window,
        .content, .panel and .e2-person were never covered at all — which
        is why his profile screenshots still popped a dictionary lookup.
        Enumerating tappable things in an 18,000-line app and hoping the
        list is complete is not a strategy.

     2. THE PART THAT "WORKED" WAS THE PROBLEM. Chrome's Touch-to-Search
        fires on a SINGLE TAP over selectable text, not only on a long
        press. So every element I left selectable — the document number,
        the employee code — was still grabbing a tap and opening Google.
        His words: "only with a single touch all the text in everywhere
        becoming selected".

   SO: BLOCK EVERYTHING, then allow back only where a person must be able
   to select — the fields they type into. A rule with no list cannot have
   holes. Desktop is untouched; this is inside the phone query.

   THE COST, STATED PLAINLY: long-press to copy a document number no
   longer works. He asked for that on 4 Aug and it cannot coexist with
   this — CSS has no way to distinguish a 300ms tap from an 800ms press.
   Copying is restored as an explicit "Copy number" row in the ⋯ sheet,
   which is deterministic and can never misfire.
   ============================================================ */
@media (max-width:820px){
  /* No list, no holes. */
  html, body, body *{
    -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none;
    -webkit-touch-callout:none;
    -webkit-tap-highlight-color:transparent;
  }
  /* Except what you type into. Same specificity as `body *`, declared
     later, so it wins without needing !important. */
  body input, body textarea, body select, body [contenteditable="true"]{
    -webkit-user-select:text; -moz-user-select:text; -ms-user-select:text; user-select:text;
    -webkit-touch-callout:default;
  }
}

/* ============================================================
   THE DASHBOARD RAIL — ACTION FIRST (4 Aug). Prefix d3-.

   The screen he asked for first and that kept getting deferred while the
   menu, topbar and reports were built.

   HIS THREE CHOICES, and they shape everything below:
     1. "What needs action today" ABOVE any total. On a phone this screen
        is opened standing on a site, not at a desk — the question is what
        is on fire, not how many people are employed.
     2. Stat cards THREE PER ROW.
     3. A hand-built rail, like Employees and Documents — not a tidied-up
        conversion.

   Desktop is untouched: `.d3-root` is display:none above 820px and its
   DOM is never even painted there (the paint call is behind matchMedia).
   Below 820px the desktop markup is hidden and the rail replaces it,
   exactly the pattern §3.36 established.
   ============================================================ */
.d3-root{ display:none }
@media (max-width:820px){
  /* The desktop dashboard steps aside. Everything it computed is still in
     the DOM and still wired — the rail DRIVES those controls rather than
     duplicating them, which is why they are hidden and not removed. */
  /* GATED ON `body.d3on`, WHICH THE RAIL ADDS ONLY WHEN IT ACTUALLY PAINTS.
     Without this gate these rules hid the desktop dashboard on every phone —
     and since the rail is currently held back (D3_ENABLED = false, his
     instruction of 5 Aug), the screen would have been COMPLETELY BLANK on
     mobile. Hiding one thing before the thing replacing it is switched on is
     how a screen disappears in production. */
  body.d3on[data-mroute="dashboard"] .content > .page-head,
  body.d3on[data-mroute="dashboard"] .content > .cards,
  body.d3on[data-mroute="dashboard"] .content > .panel,
  body.d3on[data-mroute="dashboard"] .content > .grid-2{ display:none !important }
  body.d3on .d3-root{ display:block }

  /* ---- hero ---- */
  .d3-hero{
    background:var(--bg); border-radius:16px; box-shadow:var(--nm-out-sm);
    padding:12px 14px; margin-bottom:12px;
  }
  .d3-hi{ font:700 15px var(--font-brand),sans-serif; color:var(--text) }
  .d3-dt{ font:400 11px var(--font-brand),sans-serif; color:var(--muted); margin-top:2px }
  .d3-sum{
    display:inline-flex; align-items:center; gap:6px; margin-top:9px;
    font:700 11.5px var(--font-brand),sans-serif; color:var(--muted);
    background:var(--bg); box-shadow:var(--nm-in); border-radius:20px; padding:5px 11px;
  }
  .d3-sum.d3-hot{ color:#fff; background:var(--primary-grad); box-shadow:var(--nm-out-sm) }
  .d3-sum svg{ width:14px; height:14px; flex:none }
  .d3-cta{
    display:flex; align-items:center; justify-content:center; gap:7px; width:100%;
    margin-top:10px; min-height:38px; border:none; border-radius:12px;
    background:var(--primary-grad); color:#fff;
    font:700 12.5px var(--font-brand),sans-serif;
  }
  .d3-cta:active{ box-shadow:var(--nm-in) }

  /* ---- the action strip: five tiles, scrolls if it must ---- */
  .d3-acts{ display:flex; gap:7px; margin-bottom:12px; overflow-x:auto; padding-bottom:2px }
  .d3-acts::-webkit-scrollbar{ height:0 }
  .d3-tile{
    flex:1 0 20%; min-width:66px; border:none; border-radius:12px;
    background:var(--bg); box-shadow:var(--nm-out-sm); padding:8px 4px 7px;
    display:flex; flex-direction:column; align-items:center; gap:2px;
  }
  .d3-tile:active{ box-shadow:var(--nm-in) }
  .d3-tile b{ font:800 17px var(--font-brand),sans-serif; line-height:1 }
  .d3-tile small{
    font:600 8.5px var(--font-brand),sans-serif; color:var(--muted);
    text-align:center; line-height:1.25; text-transform:uppercase; letter-spacing:.2px;
  }
  .d3-tile.d3-bad b{ color:var(--danger) }
  .d3-tile.d3-warn b{ color:var(--warn) }
  /* A zero is not an alarm — grey it so the eye goes to what is not zero. */
  .d3-tile.d3-zero b{ color:var(--muted) }

  /* ---- panels ---- */
  .d3-panel{
    background:var(--bg); border-radius:14px; box-shadow:var(--nm-out-sm);
    padding:10px 12px; margin-bottom:12px;
  }
  .d3-ph{
    display:flex; align-items:center; gap:7px; margin-bottom:8px;
    font:700 11.5px var(--font-brand),sans-serif; color:var(--text);
  }
  .d3-ph svg{ width:14px; height:14px; color:var(--primary); flex:none }
  .d3-row{
    display:flex; align-items:center; gap:10px;
    padding:6px 0; border-top:1px solid var(--border);
  }
  .d3-row:first-of-type{ border-top:none }
  .d3-rn{ flex:1; min-width:0; font:600 11.5px var(--font-brand),sans-serif; color:var(--text); line-height:1.25 }
  .d3-rn small{ display:block; font:500 10px var(--font-brand),sans-serif; color:var(--muted); margin-top:1px }
  .d3-rv{ font:800 11.5px var(--font-brand),sans-serif; flex:none }
  .d3-rv.d3-bad{ color:var(--danger) } .d3-rv.d3-warn{ color:var(--warn) }
  .d3-ok{
    flex:none; border:none; border-radius:9px; min-height:28px; padding:0 12px;
    background:var(--primary-grad); color:#fff;
    font:700 11px var(--font-brand),sans-serif;
  }
  .d3-ok:active{ box-shadow:var(--nm-in) }

  /* ---- stat cards: THREE per row, his choice ---- */
  .d3-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-bottom:12px }
  .d3-stat{
    border:none; border-radius:12px; background:var(--bg); box-shadow:var(--nm-out-sm);
    padding:9px 4px 8px; display:flex; flex-direction:column; align-items:center; gap:2px;
    min-width:0;
  }
  .d3-stat:active{ box-shadow:var(--nm-in) }
  .d3-stat b{ font:800 16px var(--font-brand),sans-serif; color:var(--text); line-height:1 }
  .d3-stat small{
    font:600 9px var(--font-brand),sans-serif; color:var(--muted); text-align:center;
    line-height:1.2; word-break:normal; overflow-wrap:anywhere;
  }

  /* ---- headcount bars ---- */
  .d3-bar{ display:flex; align-items:center; gap:8px; padding:4px 0 }
  .d3-bar > span{ flex:0 0 30%; max-width:30%; font:600 10.5px var(--font-brand),sans-serif; color:var(--text);
                  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
  .d3-bar > i{ flex:1; height:8px; border-radius:5px; background:var(--bg); box-shadow:var(--nm-in); overflow:hidden }
  .d3-bar > i > u{ display:block; height:100%; background:var(--primary-grad); border-radius:5px }
  .d3-bar > b{ flex:none; font:800 11px var(--font-brand),sans-serif; color:var(--text); min-width:20px; text-align:right }

  .d3-scan{
    display:flex; align-items:center; justify-content:center; gap:8px; width:100%;
    min-height:40px; border:none; border-radius:12px; background:var(--bg);
    box-shadow:var(--nm-out-sm); color:var(--text);
    font:700 12px var(--font-brand),sans-serif; margin-bottom:8px;
  }
  .d3-scan:active{ box-shadow:var(--nm-in) }
  .d3-scan svg{ width:15px; height:15px; color:var(--primary) }
}

/* ==========================================================================
   HOME2 (22 Aug 2026) — THE SIX ANCHORS STOP LANDING UNDER THE NAV.
   --------------------------------------------------------------------------
   goHomeSection() calls scrollIntoView(), which puts the section's TOP at the
   VIEWPORT's top - and the top of the viewport is exactly where the sticky
   menu bar sits. So every one of the six menu buttons worked perfectly and
   still looked broken, because it parked the section heading BEHIND the bar.
   He photographed it: "One platform. Every HR job done." cut in half.

   MEASURED, not guessed. The bar is, in design pixels:
       12 (padding-top) + 88 (logo) + 12 (padding-bottom) + 1 (border) = 113
   129 clears it with 16px of air. These are DESIGN pixels: the wrapper carries
   zoom .75, and scroll-margin is read in the element's own zoomed space, so
   113 and 129 are compared like for like. Nothing here needs dividing by .75.

   ⛔ THIS NUMBER IS TIED TO THE LOGO HEIGHT, AND IT HAS ALREADY MOVED ONCE.
   HOME2 shipped 168 against a 152px bar (18 + 115 + 18 + 1). HOME4 made the bar
   thinner at his request - logo 115 -> 88, padding 18 -> 12 - so this had to
   come down with it, or every menu jump would have left a 39px hole above the
   heading. Change #pubLogo again and change this in the same edit.
   There was NO scroll-margin anywhere in this application before HOME2.
   ========================================================================== */
#ds2-home,
#ds2-offer,
#ds2-services,
#ds2-how,
#ds2-reviews,
#ds2-contact { scroll-margin-top: 129px; }

/* ==========================================================================
   HOME4 (22 Aug 2026) — A SOFT SHADOW UNDER THE WHOLE HEADER BAR.
   ⚠️ HE HAD TO CORRECT ME ON THIS ONE. He asked for a drop shadow at the edge
   of the header; I put it on the menu pill inside the header instead. It is on
   the bar itself, so the shadow falls across the page as you scroll under it.
   The pill keeps the inset shadow his design gave it - untouched.
   ========================================================================== */
/* ⛔⛔ !important, AND THE REASON IS A RULE 3,500 LINES ABOVE THIS ONE.
   style.css:1462 says  .ds2-nav{ box-shadow: var(--nm-out-sm) !important }
   An !important beats a plain declaration at ANY specificity, so the rule
   below did nothing at all — he told me twice that the header shadow was
   missing and he was right both times.
   ⚠️ THIS IS THE THIRD TIME TODAY THE SAME LESSON HAS BITTEN. The card hover
   and the picture height were both silently losing to something stronger, I
   found and fixed those, wrote the reason down beside each of them — and then
   left this one plain without checking. Reading a warning is not applying it.
   Before adding ANY rule to this file, search for an existing !important on
   the same property first. */
[data-screen-label="Desktop homepage v2"] [data-sec="nav"] {
  box-shadow: 0 10px 26px rgba(109, 110, 113, .22) !important;
}

/* ⛔ THE MODULE ICON, PINNED FROM CSS AS WELL AS THE MARKUP.
   The size is written on the svg as width="56", but an attribute on an svg is
   only a hint — any CSS rule anywhere wins over it, and this stylesheet
   already carries twenty-six rules that size an svg. None of them reaches this
   one today, but "none of them today" is not a guarantee, and he has asked for
   a bigger icon more times than I want to count. Stated here as well, so the
   two agree and nothing can quietly shrink it again. */
[data-screen-label="Desktop homepage v2"] .hp-grid-mod > div > span > svg {
  width: 56px !important;
  height: 56px !important;
}

/* ==========================================================================
   HOME2 (22 Aug 2026) — THE LAST FIVE BOLD HEADINGS ON THE HOMEPAGE.
   --------------------------------------------------------------------------
   He asked for the page to be unbolded and chose scheme (খ): 800 and 700 both
   become 600. 153 declarations inside homeV2() were rewritten for that.
   ⛔ FIVE ELEMENTS WERE NOT AMONG THEM, AND NEITHER THE SOURCE COUNT NOR THE
   DEMO COULD SEE IT. They are the <h4> and <h5> headings inside the nav
   dropdowns — "What We Offer", "Features", "UAE Services", "Software", "IT" —
   and they carry no weight of their own at all. The browser's own default
   stylesheet makes every h1-h6 bold, so they resolved to 700 with nothing in
   this project to point at.
   ⛔ AND THE MEASUREMENT NEARLY MISSED THEM TWICE: getComputedStyle returns the
   KEYWORD 'bold' for these, not the number 700, and parseInt('bold') is NaN.
   The first check reported "still 700 or heavier: 0" while five headings were
   bold, and the demo skipped them for exactly the same reason.

   Scoped to the homepage shell so the heading weight of the actual product —
   every report, payslip and dashboard — is untouched. This rule can only reach
   an element that sets NO weight of its own, because an inline style always
   outranks a stylesheet; it therefore corrects the five and nothing else.
   ========================================================================== */
[data-screen-label="Desktop homepage v2"] h1,
[data-screen-label="Desktop homepage v2"] h2,
[data-screen-label="Desktop homepage v2"] h3,
[data-screen-label="Desktop homepage v2"] h4,
[data-screen-label="Desktop homepage v2"] h5,
[data-screen-label="Desktop homepage v2"] h6,
[data-screen-label="Desktop homepage v2"] b,
[data-screen-label="Desktop homepage v2"] strong { font-weight: 600; }

/* ==========================================================================
   HOME3 (22 Aug 2026) — THE HOVER THAT WENT MISSING, AND WHY IT WAS NOT
   BROKEN TODAY.
   --------------------------------------------------------------------------
   He said the cards used to rise under the cursor and had stopped. MEASURED
   before anything was written: HOME2 changed NOT ONE hover rule - the whole
   set is byte-identical before and after. What actually happened is older.
   `.ds2-card:hover{transform:translateY(-4px)}` is still sitting in this file
   at line 559, and it always worked - for ds2Landing(), the OLD homepage,
   whose cards carried .ds2-card. homeV2() replaced that page on 21 August and
   its cards carry .hp-grid-* instead. The rule has pointed at nothing since,
   and nobody noticed for a day. He is right that it used to be there; it went
   with HOME1, not with HOME2.

   ⛔ TARGETED AT .hp-grid > div ON PURPOSE, NOT AT THE INDIVIDUAL NAMES.
   The named classes could not be trusted: the "Results our clients see" grid
   was carrying TWO class attributes, so it answered to hp-grid-contact while
   the real contact grid had no name at all. That is fixed now, but the
   generic parent is the honest selector anyway - every one of the seven grids
   carries .hp-grid, and its DIRECT CHILDREN are the cards, always.

   Transform only, deliberately. Every card sets its own box-shadow INLINE,
   and an inline style outranks this file, so any shadow written here would
   need !important on cards whose backgrounds differ (#eceef6 and #fff) and
   would look wrong on half of them. `transform` appears in no inline style,
   so it lands cleanly with nothing to fight.

   He chose "lift AND grow a little" from three options, shown on his own page.
   ========================================================================== */
/* ⛔ CHANGED IN HOME4, AND THIS IS THE OPPOSITE OF WHAT HOME3 SHIPPED.
   HOME3 made the cards rise and grow slightly on hover, because that is the
   common thing to do and he had picked it from three options I wrote. He was
   choosing between MY three options. He then pointed at his own designer's
   page and said: this style card hovering.
   (The old rule is DESCRIBED, not reproduced — trap #2. Writing the discarded
   declaration out here would make any test asserting it is gone go red on a
   comment, which has cost this project four separate false alarms.)
   So I went and read the design's own stylesheet instead of guessing, and it
   settled it in one line:
       class="hph8"  is on  data-detail="feat-${i}"   — the module card
       .hph8:hover { box-shadow: inset 5px 5px 12px …, inset -4px -4px 10px … }
   His card does not rise. It PRESSES IN.
   ⛔ AND THE REASON IT LOOKS SO DIFFERENT: box-shadow REPLACES, it does not
   add. The card's normal outer shadow disappears the moment the inset one
   arrives, so the card stops floating and sinks into the surface with a
   defined edge. That is the lighter, outlined look in his screenshot — no
   colour changes at all.
   Transform is explicitly cleared so nothing of HOME3's lift survives. */
/* ⛔⛔ !important IS NOT LAZINESS HERE. IT IS THE ONLY THING THAT WORKS, AND
   THE PROOF IS THAT HIS DESIGN'S OWN RULE HAS BEEN DEAD FOR TWO DAYS WITHOUT IT.
   style.css line ~1568 already carries .hph8:hover with this exact inset
   shadow, and class="hph8" is already on the module card — both spliced in by
   HOME1. It has never once been seen, because every card writes
   box-shadow:… INLINE in its own style attribute, and an inline declaration
   beats a stylesheet rule at ANY specificity. Nothing but !important reaches
   past it. That is the whole answer to "the hover style is not coming up".
   ⚠️ I nearly shipped this bug a second time: the first draft of this block had
   no !important either, and it worked in the console demo only because the
   throwaway test line did have it. Found by asking why HIS rule was dead. */
[data-screen-label="Desktop homepage v2"] .hp-grid > div {
  transition: box-shadow .18s ease;
}
[data-screen-label="Desktop homepage v2"] .hp-grid > div:hover {
  transform: none;
  box-shadow: inset 5px 5px 12px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),
              inset -4px -4px 10px rgba(255, 255, 255, .95) !important;
}

/* ==========================================================================
   HOME4 — THE MODULE CARDS ARE SQUARE, AND STAY SQUARE AT EVERY WIDTH.
   --------------------------------------------------------------------------
   His words, clarifying what he meant the first time: the card should be
   square in SHAPE, not fixed in SIZE — narrow the window and it gets smaller
   while staying square. That is exactly what aspect-ratio does: the grid
   decides the width, and the height follows it.
   ⛔ THE PHOTO HAS TO BE A PERCENTAGE, NOT A FIXED HEIGHT. It was 140px. In a
   card that shrinks, a fixed 140 becomes a larger and larger share of the card
   until the text underneath has nowhere left to go. He watched exactly that
   happen and reported the writing being cut.
   ⚠️ THE PERCENTAGE RESOLVES AGAINST THE CARD'S CONTENT BOX, NOT ITS BORDER
   BOX — 339px, not 387 — because the card has 24px of padding top and bottom.
   That is why 56.25% lands at 146 real px and not the 166 the arithmetic
   suggests. Measured on his screen before this was written down; do not
   "correct" the number without measuring again.
   ========================================================================== */
[data-screen-label="Desktop homepage v2"] .hp-grid-mod > div {
  aspect-ratio: 1 / 1;
  overflow: hidden;
}
/* ⛔ !important, for the same reason as the hover above: the picture well
   writes height:140px INLINE in its own style attribute. Without it this rule
   is ignored, the well stays fixed at 140, and the whole point — a photo that
   shrinks with the card — silently does not happen. */
[data-screen-label="Desktop homepage v2"] .hp-grid-mod > div > div:first-child {
  height: calc(56.25% + 4px) !important;
}

/* HOME4 — the service names in the Beyond HR block turn gold under the cursor,
   the chevron with them. He pointed at that section and said the hover was not
   reaching the text. It is the same #8a7222 his design uses in twenty places. */
[data-screen-label="Desktop homepage v2"] [data-sec="offers"] [data-detail] {
  transition: color .16s ease;
}
[data-screen-label="Desktop homepage v2"] [data-sec="offers"] [data-detail]:hover {
  color: #8a7222;   /* the item itself sets no colour inline, so this lands */
}
/* ⛔ …but the chevron does: <span style="color:#bf9b2e">. Without !important the
   text would turn gold and the little arrow beside it would stay bronze, which
   looks like a bug rather than a hover. Checked every new rule in HOME4 the
   same way after finding his design's own hover had been dead for two days
   for exactly this reason. */
[data-screen-label="Desktop homepage v2"] [data-sec="offers"] [data-detail]:hover span {
  color: #8a7222 !important;
}

/* The six top-menu links. Their colour is written inline by homeV2, and an
   inline style beats a stylesheet, so this one genuinely needs !important -
   there is no other way to reach it without editing the generated markup. */
[data-screen-label="Desktop homepage v2"] .ds2-navlink {
  transition: color .18s ease, background .18s ease;
}
[data-screen-label="Desktop homepage v2"] .ds2-navlink:hover {
  color: #8a7222 !important;
}

/* Section headings: a gentle deepening, nothing that moves. A heading is not
   a button, and making it jump under the cursor invites a click that does
   nothing. This is the restrained half of what he asked for. */
[data-screen-label="Desktop homepage v2"] [data-sec] > div > div {
  transition: color .2s ease;
}

/* ⛔ AND IT ALL STOPS FOR ANYONE WHO HAS ASKED THEIR MACHINE TO STOP ANIMATING.
   Vestibular disorders make movement on screen genuinely unpleasant, and the
   operating system already carries the answer. One block, and the page is
   still perfectly usable - it simply does not move. */
@media (prefers-reduced-motion: reduce) {
  [data-screen-label="Desktop homepage v2"] .hp-grid > div,
  [data-screen-label="Desktop homepage v2"] [data-sec="offers"] [data-detail],
  [data-screen-label="Desktop homepage v2"] .ds2-navlink { transition: none; }
  /* ⚠️ RESTATED IN HOME4. This used to cancel a transform, because HOME3's
     hover LIFTED the card. HOME4 replaced that with his design's press-in,
     which is a shadow and not movement — so there is nothing here to cancel
     any more, and the cards keep their hover for everyone. The transitions
     above still go, because an instant change is the point of reduce. */
}

/* ==========================================================================
   HOME5 (22 Aug 2026) — THE ENGAGEMENT LINE UNDER THE HERO.
   --------------------------------------------------------------------------
   His words: "an focused animated section with written engagement line below
   of header headlines", and for the movement: "Raising up from the body
   repeatedly, headline style". He drew it in a box under the two hero buttons,
   which is where it sits.
   So: it rises from below, holds long enough to be READ, and goes again. The
   hold is the important part — a line that is only ever moving is a line
   nobody finishes reading, and this one is asking for the sale.
   The wording is CMS-editable (heroPledge); his own sentence is the default.
   ========================================================================== */
[data-screen-label="Desktop homepage v2"] .hv-pledge {
  display: inline-block;
  margin-top: 26px;
  padding: 14px 28px;
  border-radius: 14px;
  background: #eceef6;
  box-shadow: 6px 6px 16px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),
              -5px -5px 12px rgba(255,255,255,.95);
  /* ⛔ HE ASKED FOR THIS ONE BOLD AND TWICE THE SIZE. 20 -> 40, and 700 —
     the ONLY thing on this homepage above weight 600. That is deliberate and
     it is his call: everything else came down from 800 at his instruction, and
     this line is the one that asks for the sale, so it is allowed to shout.
     Anyone tempted to "tidy" it back to 600 for consistency: do not. */
  font-size: 40px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -.02em;
  color: #8a7222;
  animation: hv-rise 4.6s ease-in-out infinite;
}
@keyframes hv-rise {
  0%        { transform: translateY(16px); opacity: 0; }
  14%       { transform: translateY(0);    opacity: 1; }
  80%       { transform: translateY(0);    opacity: 1; }
  100%      { transform: translateY(-8px); opacity: 0; }
}
/* ⛔ A line that never stops moving is the hardest kind for anyone who has
   asked their machine to stop animating — and this one carries the offer, so
   it must never simply disappear for them. Held still and fully visible. */
@media (prefers-reduced-motion: reduce) {
  [data-screen-label="Desktop homepage v2"] .hv-pledge {
    animation: none;
    transform: none;
    opacity: 1;
  }
}

/* ==========================================================================
   HOME3 — THE CLIENT LOGO STRIP. A continuous marquee, his choice of three.
   --------------------------------------------------------------------------
   ⛔ HOW THE SEAMLESS LOOP WORKS, because it looks like a trick and is not:
   homeV2 prints the logo list TWICE into one track. The animation then slides
   the track exactly -50%, which is precisely the width of the first copy — so
   the moment it ends, the second copy is sitting exactly where the first
   began and the jump back to 0 is invisible. Any other percentage stutters.
   The gap is a flex `gap`, so the space between the last logo of copy one and
   the first of copy two is the same as every other space. Change the gap and
   the loop still works; change the -50% and it will not.

   Speed is a duration, not a distance, so a company with three logos and one
   with ten both move at a readable pace rather than one crawling.

   It pauses under the cursor: somebody who wants to read a client's name
   should not have to chase it across the screen.

   The soft fade at both ends is a mask, not a gradient overlay — an overlay
   would need to know the page background, and this section sits on the same
   #eceef6 as everything else today but might not tomorrow.
   ========================================================================== */
.hp-marq {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.hp-marq-track {
  display: flex;
  align-items: center;
  gap: 72px;
  width: max-content;
  animation: hp-marq-run 34s linear infinite;
}
.hp-marq:hover .hp-marq-track { animation-play-state: paused; }
@keyframes hp-marq-run {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.hp-marq-item {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 64px;
}
/* Logos arrive at every shape a customer's brand happens to be. Bounding both
   sides and using contain means a tall square mark and a long wordmark both
   sit on the same optical line without either being squashed. */
.hp-marq-item img {
  max-height: 64px;
  max-width: 190px;
  width: auto;
  height: auto;
  object-fit: contain;
}
@media (max-width: 820px) {
  .hp-marq-track { gap: 44px; }
  .hp-marq-item, .hp-marq-item img { height: 46px; max-height: 46px; }
  .hp-marq-item img { max-width: 140px; }
}
/* Movement that never stops is the hardest kind for anyone who has asked
   their machine to stop animating. This one halts completely. */
@media (prefers-reduced-motion: reduce) {
  .hp-marq-track { animation: none; }
  .hp-marq { overflow-x: auto; }
}


/* ==========================================================================
   HOME6 (22 Aug 2026) — THE PHONE, REACHING THIS PAGE AT LAST.
   --------------------------------------------------------------------------
   ⛔ THE WHOLE PHONE LAYOUT ABOVE IS SCOPED `.ds2`, AND THIS PAGE HAS NO SUCH
   CLASS. It has been switched off since HOME1 replaced the homepage, silently,
   for two days. The class is deliberately NOT added: four of the eleven
   `.ds2` rules that live outside a media query would reach the DESKTOP page
   as well — the images, the language selector, the dropdown hover and, for
   anyone who asks their machine to stop animating, the hero itself.
   So the phone layout is stated again here, for this page only, and nothing
   outside this page can feel it.

   ⛔ EVERY LINE CARRIES !important AND THAT IS THE POINT. homeV2() writes its
   padding, its grid columns and its font sizes INLINE. An inline declaration
   beats a stylesheet rule at ANY specificity, so without !important each rule
   below would be present, correct, and completely powerless — which is what a
   rule that was never written also looks like.

   Sizes: his own Mobile.dc.html for type and cards, his developer's 22 Aug
   delivery for the column counts. 860px, not the developer's 820, because
   860 is where this app already swaps the top menu for a burger.
   ========================================================================== */
@media (max-width: 860px) {

  /* ⛔ THE ONE LINE THAT MATTERS MOST. The shell carries zoom:.75 so his
     1600px desktop drawing fits a 1536px screen. On a phone it does the
     opposite — it turns a 390px screen into ~520 design px and then shrinks
     every one of them. At full size the phone gets its real width back, and
     the type below is set for that width. */
  [data-screen-label="Desktop homepage v2"] { zoom: 1 !important; }

  /* --- the bands: an inch and a half becomes 20px ------------------------ */
  /* ⛔ ON THE BAND ITSELF, not on its inner div. HOME4 moved the padding onto
     the band, inline, so a rule aimed at the child does nothing at all. */
  [data-screen-label="Desktop homepage v2"] [data-sec] { padding-left: 20px !important; padding-right: 20px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="hero"]     { padding-top: 18px !important; padding-bottom: 10px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="promo"],
  [data-screen-label="Desktop homepage v2"] [data-sec="stats"],
  [data-screen-label="Desktop homepage v2"] [data-sec="offers"],
  [data-screen-label="Desktop homepage v2"] [data-sec="how"],
  [data-screen-label="Desktop homepage v2"] [data-sec="contact"]  { padding-top: 40px !important; padding-bottom: 40px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="features"] { padding-top: 16px !important; padding-bottom: 40px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="reviews"]  { padding-top: 0 !important;    padding-bottom: 40px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="reco"],
  [data-screen-label="Desktop homepage v2"] [data-sec="band"]     { padding-top: 32px !important; padding-bottom: 32px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="clients"]  { padding-top: 32px !important; padding-bottom: 36px !important; }
  /* the quotes band is inset from the page as well as padded inside */
  [data-screen-label="Desktop homepage v2"] [data-sec="quotes"]   { margin: 0 16px 40px !important; padding-top: 32px !important; padding-bottom: 32px !important; }

  /* ⚠️ THE TOP BAR AND THE FOOTER COME IN TOO, AND THIS IS MY JUDGEMENT.
     His instruction "not in header and footer" was about the 1.5 inch DESKTOP
     padding. 48px and 56px on a 390px screen is a quarter of the width. */
  [data-screen-label="Desktop homepage v2"] [data-sec="nav"]  { padding: 10px 18px !important; gap: 10px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="foot"] { padding: 28px 20px !important; }

  /* --- the header: the drawer carries the menu --------------------------- */
  /* Every other page in this app already does this at this same width. The
     six links, the language selector and the two buttons are all in the
     drawer behind the burger, which homeV2 already emits. */
  [data-screen-label="Desktop homepage v2"] [data-sec="nav"] > div:has(.ds2-navlink) { display: none !important; }
  [data-screen-label="Desktop homepage v2"] #ds2Lang, [data-screen-label="Desktop homepage v2"] #navLogin, [data-screen-label="Desktop homepage v2"] #navRegister { display: none !important; }
  /* ⛔ HEIGHT ALONE WAS THE BUG. HOME6 set the height and left the width
     free, so a wide banner logo took about 250px of a 390px screen and cut
     the Login button off the right edge — he photographed it.
     THE RESERVE, counted: burger 42 + language globe 38 + Login ~132 +
     page padding 40 + three gaps 30 = 282, rounded to 296 for slack.
     The 70px floor keeps the mark readable on a 320px phone rather than
     letting it shrink away to nothing. */
  [data-screen-label="Desktop homepage v2"] #pubLogo {
    height: 34px !important;
    width: auto !important;
    max-width: max(70px, calc(100vw - 296px)) !important;
    object-fit: contain !important;
  }

  /* --- one column, except the stats, which stay two --------------------- */
  /* His developer's delivery keeps the four statistics two across on a phone
     and drops everything else to one. Four short numbers read better as a
     block of two than as a column of four. */
  [data-screen-label="Desktop homepage v2"] .hp-grid       { grid-template-columns: 1fr !important; gap: 14px !important; }
  [data-screen-label="Desktop homepage v2"] .hp-grid-stat  { grid-template-columns: 1fr 1fr !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="hero"] > div { grid-template-columns: 1fr !important; gap: 24px !important; }
  [data-screen-label="Desktop homepage v2"] [data-sec="foot"] > div { grid-template-columns: 1fr !important; gap: 28px !important; }
  /* ⛔ the nine-across broadcast wall is NOT touched. Nine across at any size
     is what that scene IS, not a layout that happens to fit. */

  /* --- the cards -------------------------------------------------------- */
  [data-screen-label="Desktop homepage v2"] .hp-grid > div { padding: 18px !important; border-radius: 22px !important; }
  [data-screen-label="Desktop homepage v2"] .hv-panel      { border-radius: 24px !important; padding: 16px !important; }
  /* ⛔ the module card KEEPS its square shape and its percentage picture from
     HOME4. One card per row on a phone makes a square card a good square,
     and the picture still shrinks with it. Nothing here undoes that. */

  /* --- the type --------------------------------------------------------- */
  [data-screen-label="Desktop homepage v2"] .hv-head { font-size: 38px !important; line-height: 1.02 !important; margin-top: 16px !important; }
  [data-screen-label="Desktop homepage v2"] .hv-sub  { font-size: 15px !important; max-width: none !important; }
  /* ⚠️ THE ENGAGEMENT LINE: THE SIZE MOVES, THE WEIGHT DOES NOT. 700 is his,
     deliberately, and it is the only weight above 600 on the page. 40px of it
     wraps to four lines on a phone, so only the size comes down. */
  [data-screen-label="Desktop homepage v2"] .hv-pledge { font-size: 22px !important; padding: 11px 18px !important; margin-top: 18px !important; }

  /* The section headings, matched on the size written into their own style
     attribute — the only handle they have, because homeV2 writes them inline
     and gives them no class. His own file settles them all at 30. */
  /* ⛔ BY NAME, NOT BY NUMBER. These rules used to select each heading by
     writing its desktop pixel size into the selector itself.
     HOME8 raised every unprotected size on the page by 2, which would have
     left every one of those selectors matching nothing, in silence, and the
     phone headlines rendering at full desktop size. The ten headings now
     carry .hv-sec-title (the hero has .hv-head), so the next time he changes
     his mind about a size there is nothing here to hunt for. */
  [data-screen-label="Desktop homepage v2"] .hv-sec-title { font-size: 30px !important; line-height: 1.06 !important; }
  /* The promo price and the statistic numbers are not headings and DID grow
     by 2 — 50 to 52, 40 to 42 — so they are named at their new values. */
  [data-screen-label="Desktop homepage v2"] [style*="font-size:52px"]:not(.hv-sec-title) { font-size: 36px !important; }
  [data-screen-label="Desktop homepage v2"] [style*="font-size:42px"] { font-size: 24px !important; }
  [data-screen-label="Desktop homepage v2"] [style*="font-size:32px"] { font-size: 17px !important; }
  [data-screen-label="Desktop homepage v2"] [style*="font-size:26px"] { font-size: 18px !important; }
  [data-screen-label="Desktop homepage v2"] [style*="font-size:24px"] { font-size: 17px !important; }
  [data-screen-label="Desktop homepage v2"] [style*="font-size:22px"] { font-size: 16px !important; }

  /* --- the two hero buttons and the band buttons stack ------------------- */
  /* ⛔ ANCHORED TO THE BUTTON IDS, NOT TO TODAY'S MARGIN. The first draft of
     this rule matched [style*="margin-top:32px"], copied from the older phone
     block — and HOME5 had already moved that margin to 22px, so it reached
     nothing at all. The new suite caught it on its first run. A selector that
     hard-codes a number he is still adjusting goes dead the next time he
     adjusts it, in silence. The ids are load-bearing: wireLanding() binds
     them, so they cannot be renamed without something else breaking loudly. */
  [data-screen-label="Desktop homepage v2"] div:has(> #bandRegister) {
    flex-direction: column !important; align-items: stretch !important; gap: 10px !important;
  }

  /* --- the client logo strip, at his developer's phone size -------------- */
  [data-screen-label="Desktop homepage v2"] .hp-marq-track { gap: 40px !important; }
  [data-screen-label="Desktop homepage v2"] .hp-marq-item, [data-screen-label="Desktop homepage v2"] .hp-marq-item img { height: 46px !important; max-height: 46px !important; }
  [data-screen-label="Desktop homepage v2"] .hp-marq-item img { max-width: 130px !important; }
}


/* ==========================================================================
   HOME7 (22 Aug 2026) — WHAT HE ASKED FOR AFTER SEEING HOME6 ON HIS PHONE.
   ========================================================================== */

/* --------------------------------------------------------------------------
   AMEERA IS HER FACE AND NOTHING ELSE.
   Her pill carried her picture, a pulsing green dot and the words
   "Ask Ameera — Free", and it sat ON TOP of the sticky bar at the bottom of
   every phone screen — the bar's own label reads "I Need This Softw…" in every
   screenshot he sent, because she was covering the rest of it.
   ⚠️ THE LABEL IS A BARE TEXT NODE inside the button, with no element of its
   own, so no selector can reach it. font-size:0 on the button is what removes
   it; the picture is sized in pixels and does not care.
   ⚠️ 0.75pt IS WRITTEN AS 1px. 1pt is 1.333px, so 0.75pt is exactly 1px — and
   a fractional border renders differently on every device, which would give
   him three different rings on three phones.
   ⛔ This is the WEBSITE widget only. mountAskWidget() refuses to run when
   somebody is signed in, so the assistant inside the product is untouched.
   -------------------------------------------------------------------------- */
.ds2-ask-btn {
  font-size: 0 !important;          /* removes the words, which have no element */
  gap: 0 !important;
  padding: 0 !important;
  width: 54px !important;
  height: 54px !important;
  border-radius: 50% !important;
  background: #eceef6 !important;
  border: 1px solid #8a7222 !important;   /* 0.75pt */
  box-shadow: 0 6px 18px rgba(109, 110, 113, .28) !important;
  justify-content: center !important;
  overflow: hidden !important;
}
.ds2-ask-face { width: 46px !important; height: 46px !important; }
/* The pulsing green dot goes with the words — a status light on a picture that
   is now the whole control reads as a fault, not as availability. */
.ds2-ask-dot { display: none !important; }

/* --------------------------------------------------------------------------
   THE PHONE TOP BAR — the language control and Login come back.
   HOME6 hid all three of them, which is the empty gap he circled in red.
   ⛔ REGISTER STAYS HIDDEN. It is the left half of the sticky bar that sits on
   every phone screen already, and putting it in both places is what made the
   hero repeat itself in the first place.
   ⚠️ THE LANGUAGE CONTROL IS A <select>. Its text cannot be replaced by CSS,
   so it is made transparent and a globe is painted behind it. The control is
   still the real one — tapping it opens the phone's own language list, and it
   keeps its title="Language" for anyone using a screen reader.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  [data-screen-label="Desktop homepage v2"] #ds2Lang {
    display: block !important;
    width: 38px !important;
    height: 38px !important;
    padding: 0 !important;
    border-radius: 50% !important;
    color: transparent !important;
    text-indent: -999px !important;
    background-color: #eceef6 !important;
    background-repeat: no-repeat !important;
    background-position: center !important;
    background-size: 21px 21px !important;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a7222' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M3 12h18M12 3a15 15 0 0 1 0 18a15 15 0 0 1 0-18'/%3E%3C/svg%3E") !important;
    -webkit-appearance: none !important;
    appearance: none !important;
  }
  [data-screen-label="Desktop homepage v2"] #navLogin {
    display: block !important;
    padding: 9px 13px !important;
    font-size: 12.5px !important;
    border-radius: 11px !important;
    white-space: nowrap !important;
  }
  [data-screen-label="Desktop homepage v2"] #navRegister { display: none !important; }
}

/* --------------------------------------------------------------------------
   THE SAFETY NET. A label longer than the space it is given must WRAP, never
   push the page wider than the screen — the page shell clips rather than
   scrolls, so an overflow is invisible except as words that stop mid-sentence.
   That is what he photographed, and it was a 36-character button label at a
   width nobody had measured.
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
  [data-screen-label="Desktop homepage v2"] [style*="border-radius"][style*="cursor:pointer"],
  [data-screen-label="Desktop homepage v2"] .hp-bottom > .r {
    overflow-wrap: anywhere !important;
    word-break: normal !important;
    max-width: 100% !important;
  }
  [data-screen-label="Desktop homepage v2"] * { max-width: 100% !important; }
  /* ⛔ the marquees are the one thing that MUST be wider than the screen —
     the client logo track is printed twice so its -50% slide is seamless, and
     capping it would stop the loop dead. */
  [data-screen-label="Desktop homepage v2"] .hp-marq-track, [data-screen-label="Desktop homepage v2"] .hp-marq-track * { max-width: none !important; }
}


/* ==========================================================================
   HOME8 (22 Aug 2026) — AMEERA STOPS SITTING ON THE STICKY BAR.
   --------------------------------------------------------------------------
   HOME7 turned her from a pill into a face, which was the right change and the
   one he asked for — but she still sat ON the bar at the bottom of every phone
   screen and covered its right end. The bar's own label reads "Login My Com…"
   in every screenshot he sent, and that was her, not a layout fault.
   THE ARITHMETIC, from the rule that draws the bar: 14px of padding on top,
   22px underneath, and a button of 15 + 15 padding around ~17px of text — 83px.
   She sits at 96, which clears it with room to spare.
   ⛔ ONLY ON A PHONE. Above 860px the bar is display:none and she belongs where
   she has always been, 22px off the bottom corner.
   ========================================================================== */
@media (max-width: 860px) {
  .ds2-ask { bottom: 96px !important; }
  /* Her panel opens upward from her, so it has to move with her or it opens
     underneath the bar and the first line is unreadable. */
  .ds2-ask-panel { bottom: 64px !important; }

  /* With the second Login gone, the one remaining call to action takes the
     whole bar rather than sitting in half of it. */
  [data-screen-label="Desktop homepage v2"] .hp-bottom > .r { flex: 1 1 100% !important; }
}


/* ==========================================================================
   HOME8b (22 Aug 2026) — THE PHONE HERO WAS STILL CUT, AND THE CULPRIT WAS
   MEASURED ON HIS OWN SCREEN, NOT GUESSED.
   --------------------------------------------------------------------------
   On his Android phone the hero's writing stopped mid-sentence and the cards
   ran off the right edge — while every phone rule above was live and correct.
   Measured on the real page at 390px: the hero grid's single column resolved
   to 457.725px, because a grid column's automatic MINIMUM is the width of the
   widest thing inside it, and the ten-label tab strip in the demo panel wants
   about 426px on one line (plus the panel's own padding, 458). The strip has
   carried per-item shrink-and-ellipsis in its markup from the start — but
   that machinery only engages once the column is ALLOWED to be narrower than
   its content, and a grid cell's default minimum forbids exactly that.
   THE FIX IS PERMISSION, NOT A SIZE. The two hero grid cells may shrink below
   their content; the tabs then ellipsise, the writing wraps at the real
   screen, and no number is written here that he might later change.
   ✅ Proven in his browser before shipping: with only this change the column
   came back to ~350px and nothing in the hero crossed the right edge except
   the decorative glow circle, which is parked off-canvas on purpose — the
   same category as the closed burger drawer.
   ⛔ Phone only. The desktop layout has room, and it is untouched.
   ========================================================================== */
@media (max-width: 860px) {
  [data-screen-label="Desktop homepage v2"] [data-sec="hero"] > div > * { min-width: 0 !important; }
}


/* ==========================================================================
   HOME10 (22 Aug 2026) — LEARN MORE ON EVERY MODULE CARD, AND AMEERA ALIVE.
   --------------------------------------------------------------------------
   1. Every module card gains a visible invitation. The card has opened its
      detail page since HOME1 (the whole card carries the click), but nothing
      on it SAID so. The pill is his own literal golds — the bright pair on
      the pill, the deep pair on the arrow circle, the same two gradients the
      top-bar buttons wear (P5: the golds stay literal, never wired to the
      CMS brand colour).
      · desktop: the words and the arrow, arrow in its own darker circle
      · phone (his spec): ONLY the arrow, in a gold-stroked circle — the
        words leave, the way the top bar sheds its labels at the same width
      The pill is aria-hidden and takes no pointer behaviour of its own: the
      card underneath has always been the control, so the pill can never say
      one thing and do another.
   2. Ameera floats, wears a soft radar ring, and every few seconds a small
      bubble surfaces above her with the inquiry invitation he chose:
      "Need help? Ask Ameera". The bubble is pointer-events:none — it can sit
      over nothing and steal no tap — and everything here stands down while
      the chat panel is open.
   ⚠️ THE RING LIVES ON .ds2-ask, NOT ON THE BUTTON: the HOME7 face rule
      carries overflow:hidden !important, which would clip a ring drawn on
      the button itself. Checked before writing, not after.
   ⚠️ REDUCED MOTION: float and ring stop; the bubble is HELD STILL AND
      VISIBLE rather than removed — it carries the offer, the hv-pledge rule.
   ========================================================================== */

/* --- the Learn More pill, desktop first ----------------------------------- */
[data-screen-label="Desktop homepage v2"] .hp-lm {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
  padding: 8px 9px 8px 20px;
  border-radius: 999px;
  background: linear-gradient(135deg, #bf9b2e, #a8862a);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .02em;
  box-shadow: 5px 5px 13px color-mix(in srgb,#2E7DF0 18%,#a7b0c6),
              -3px -3px 9px rgba(255, 255, 255, .9);
}
[data-screen-label="Desktop homepage v2"] .hp-lm-a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: linear-gradient(135deg, #8a7222, #6f5a18);
  color: #fff;
  transition: transform .25s ease;
}
/* the card's own hover (his designer's press-in) nudges the arrow forward */
[data-screen-label="Desktop homepage v2"] .hph8:hover .hp-lm-a { transform: translateX(4px); }

/* --- on the phone the words leave and the circle stays --------------------- */
@media (max-width: 860px) {
  [data-screen-label="Desktop homepage v2"] .hp-lm {
    display: flex !important;
    justify-content: flex-end !important;
    padding: 0 !important;
    margin-top: 12px !important;
    background: none !important;
    box-shadow: none !important;
    gap: 0 !important;
  }
  [data-screen-label="Desktop homepage v2"] .hp-lm-t { display: none !important; }
  [data-screen-label="Desktop homepage v2"] .hp-lm-a {
    width: 40px !important;
    height: 40px !important;
    background: #eceef6 !important;
    color: #8a7222 !important;
    border: 1px solid #8a7222 !important;   /* 0.75pt, the Ameera ring's weight */
    box-shadow: 4px 4px 10px color-mix(in srgb,#2E7DF0 16%,#a7b0c6),
                -3px -3px 8px rgba(255, 255, 255, .95) !important;
  }
}

/* --- Ameera: float, ring, and the surfacing bubble ------------------------- */
.ds2-ask-btn { animation: hv-ask-float 4.5s ease-in-out infinite; }
@keyframes hv-ask-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
.ds2-ask::after {
  content: "";
  position: absolute;
  top: -3px; left: -3px; right: -3px; bottom: -3px;
  border-radius: 50%;
  border: 2px solid rgba(191, 155, 46, .55);
  animation: hv-ask-ring 2.6s ease-out infinite;
  pointer-events: none;
}
@keyframes hv-ask-ring {
  0%   { transform: scale(1);    opacity: .8; }
  70%  { transform: scale(1.55); opacity: 0; }
  100% { transform: scale(1.55); opacity: 0; }
}
.ds2-ask-bub {
  position: absolute;
  right: 4px;
  bottom: calc(100% + 12px);
  white-space: nowrap;
  padding: 9px 14px;
  border-radius: 14px 14px 3px 14px;
  background: #fff;
  color: #8a7222;
  font-size: 13.5px;
  font-weight: 600;
  border: 1px solid rgba(191, 155, 46, .5);
  box-shadow: 0 10px 26px rgba(109, 110, 113, .28);
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  animation: hv-ask-bub 14s ease-in-out infinite 3s;
}
@keyframes hv-ask-bub {
  0%, 3%   { opacity: 0; transform: translateY(6px); }
  7%, 30%  { opacity: 1; transform: translateY(0); }
  36%, 100% { opacity: 0; transform: translateY(6px); }
}
/* while she is actually talking, the attention machinery stands down */
.ds2-ask:has(.ds2-ask-panel.open) .ds2-ask-bub { display: none; }
.ds2-ask:has(.ds2-ask-panel.open)::after { display: none; }

@media (prefers-reduced-motion: reduce) {
  .ds2-ask-btn { animation: none; }
  .ds2-ask::after { display: none; }
  .ds2-ask-bub { animation: none; opacity: 1; transform: none; }
  [data-screen-label="Desktop homepage v2"] .hp-lm-a { transition: none; }
}


/* ==========================================================================
   HOME10b (22 Aug 2026) — THE PILL MOVES TO THE CORNER, SHRINKS, AND STOPS
   FIGHTING THE SQUARE.
   --------------------------------------------------------------------------
   He saw HOME10 live and reported the pill CUTTING on both screens, and asked
   for it smaller and in the right corner. The mechanism: the module card has
   been a LOCKED SQUARE since HOME45 (its height comes from its width and it
   clips its overflow), so a pill added to the normal flow had nowhere to go —
   it pushed past the bottom edge and was sliced. On the phone his own
   screenshot shows the square was ALREADY slicing the last line of a
   description before any pill arrived — one column makes the card narrow,
   phone type makes the words tall, and the square cannot stretch.
   THE FIX, TWO HALVES:
   · both screens: the pill leaves the flow and pins to the card's lower
     right, smaller — it now lives in the card's spare corner and can never
     push anything anywhere.
   · phone only: the square is RELEASED (height follows the words) and the
     card reserves room at the bottom for the arrow circle. ⚠️ The picture
     well's height is a PERCENTAGE of the card (a HOME45 rule), and a
     percentage of a released height resolves to nothing — so on the phone
     the well keeps the same look by RATIO instead (16:9 is exactly what
     56.25% is). The desktop square, and the measured percentage that lives
     on it, are untouched.
   ========================================================================== */
[data-screen-label="Desktop homepage v2"] .hp-grid-mod > div { position: relative; }
[data-screen-label="Desktop homepage v2"] .hp-lm {
  position: absolute;
  right: 14px;
  bottom: 12px;
  margin-top: 0;
  padding: 5px 6px 5px 14px;
  gap: 8px;
  font-size: 12.5px;
}
[data-screen-label="Desktop homepage v2"] .hp-lm-a { width: 22px; height: 22px; }
[data-screen-label="Desktop homepage v2"] .hp-lm-a svg { width: 13px; height: 13px; }

@media (max-width: 860px) {
  /* the square lets go; the words decide the height; the corner is reserved */
  [data-screen-label="Desktop homepage v2"] .hp-grid-mod > div {
    aspect-ratio: auto !important;
    padding-bottom: 62px !important;
  }
  /* the picture well keeps its exact desktop proportion, by ratio not by a
     percentage that no longer has anything to stand on */
  [data-screen-label="Desktop homepage v2"] .hp-grid-mod > div > div:first-child {
    height: auto !important;
    aspect-ratio: 16 / 9 !important;
  }
  [data-screen-label="Desktop homepage v2"] .hp-lm { right: 14px !important; bottom: 14px !important; }
  [data-screen-label="Desktop homepage v2"] .hp-lm-a { width: 36px !important; height: 36px !important; }
  [data-screen-label="Desktop homepage v2"] .hp-lm-a svg { width: 16px; height: 16px; }
}

/* ==== DASH1 (8 Sep 2026) — item 31: the two-column dashboard ================
   His designer's 2-column handoff, drawn with the app's OWN surface, radius and
   soft shadow tokens (body.soft) - not the handoff's. Layout only: nothing here
   colours a number or a card differently from before.
   Breakpoints: the rail drops under the columns at 1100px - NOT the handoff's
   1500. His own screen runs Windows scaling, so a 1830px-wide window is about
   1460 CSS px, and at 1500 the first preview showed him ONE column with the
   rail pushed below (8 Sep, "why you changed the layout?"). Two columns hold
   down to ~1100 (rail ~350px, four tiles across). Phones: the app's own 820
   (the handoff said 900; this app's every other rule is 820). */
.dash1-two{display:grid;grid-template-columns:minmax(0,1.3fr) 0 minmax(0,1fr);gap:22px;align-items:start}
.dash1-main{min-width:0}
.dash1-kpis.cards{grid-template-columns:1fr 1fr}
.dash1-sections{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
.dash1-block{min-width:0}
.dash1-block>.panel{margin:0}
/* a section is half a column now: its table must shrink, not push into the
   divider (his screenshot, 8 Sep - the leave request row was cut), and a
   panel head with three buttons must wrap instead of squeezing its title */
.dash1-block table{width:100%;table-layout:auto}
.dash1-block td,.dash1-block th{white-space:normal;overflow-wrap:anywhere;padding:10px 12px}
.dash1-block .panel-head{flex-wrap:wrap;gap:8px}
.dash1-slots{display:flex;gap:14px;margin:4px 0 18px}
.dash1-slot{flex:1;min-height:56px;border:2px dashed var(--border);border-radius:14px;background:transparent;color:var(--primary-ink);font:inherit;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px}
.dash1-slot.del{color:var(--danger)}
.dash1-slot:disabled{opacity:.45;cursor:default}
.dash1-slot.on{border-style:solid;border-color:var(--danger);background:color-mix(in srgb,var(--danger) 10%,transparent)}
.dash1-picking .dash1-block{outline:2px dashed color-mix(in srgb,var(--danger) 60%,transparent);outline-offset:3px;cursor:pointer;border-radius:var(--radius)}
.dash1-divider{position:relative;width:0;align-self:stretch;border-left:2px dashed color-mix(in srgb,var(--primary) 55%,transparent)}
.dash1-divider::before,.dash1-divider::after{content:"";position:absolute;left:-4px;width:6px;height:6px;border-radius:999px;background:var(--primary)}
.dash1-divider::before{top:0}.dash1-divider::after{bottom:0}
.dash1-divider span{position:absolute;top:50%;left:0;transform:translate(-50%,-50%) rotate(90deg);font-size:9.5px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:var(--primary-ink);background:var(--surface);border:1px solid var(--border);padding:6px 10px;border-radius:9px;box-shadow:var(--shadow);white-space:nowrap}
.dash1-rail{position:sticky;top:calc(var(--topbar-h,64px) + 14px);min-width:0}
.dash1-rail .panel{margin-bottom:14px}
.dash1-pm{display:flex;gap:6px}
.dash1-pm button{width:30px;height:30px;border-radius:9px;border:1px solid var(--border);background:var(--surface);box-shadow:var(--shadow);color:var(--primary-ink);font:inherit;font-weight:800;font-size:15px;cursor:pointer;line-height:1}
.dash1-qa{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding:10px 16px 16px}
.dash1-tile{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;background:transparent;border:0;font:inherit;font-size:11.5px;color:var(--muted);cursor:pointer;padding:0;text-align:center}
.dash1-tile .ic{position:relative;width:62px;height:56px;border-radius:16px;background:var(--surface);border:1px solid var(--border);box-shadow:var(--shadow);display:flex;align-items:center;justify-content:center;color:var(--primary-ink)}
.dash1-tile .ic svg{width:22px;height:22px}
.dash1-tile .ic{transition:transform .12s ease,box-shadow .16s ease}
.dash1-tile:hover .ic{transform:translateY(-2px)}
.dash1-tile:active .ic{transform:translateY(0)}
.dash1-tile .bd{position:absolute;top:-6px;right:-6px;min-width:18px;height:18px;line-height:18px;border-radius:999px;background:var(--primary);color:#fff;font-style:normal;font-size:11px;font-weight:700;text-align:center;padding:0 4px}
.dash1-tile .rm{display:none;position:absolute;top:-7px;left:-7px;width:18px;height:18px;border-radius:999px;background:var(--danger);color:#fff;font-style:normal;font-size:12px;line-height:18px;text-align:center}
.dash1-editing .dash1-tile:not(.add) .rm{display:block}
.dash1-editing .dash1-tile:not(.add){animation:dash1wob .28s ease-in-out infinite alternate}
@keyframes dash1wob{from{transform:rotate(-1.1deg)}to{transform:rotate(1.1deg)}}
.dash1-tile.add .ic{border:2px dashed var(--border);background:transparent;box-shadow:none;font-size:22px;color:var(--primary-ink)}
.dash1-tile.drag{opacity:.4}
#dash1Sc{padding:6px 14px 12px}
.dash1-sc{display:flex;align-items:center;gap:10px;padding:11px 14px;margin:8px 0 0;border:1px solid var(--border);border-radius:14px;width:100%;background:var(--surface);box-shadow:var(--shadow);text-align:left;font:inherit;color:var(--text);cursor:pointer;transition:transform .12s ease,box-shadow .16s ease}
.dash1-sc .tx{flex:1;min-width:0}
.dash1-sc b{font-size:13px;display:block}
.dash1-sc small{color:var(--muted)}
.dash1-sc .chev{color:var(--muted);font-size:18px}
.dash1-sc:hover{transform:translateY(-2px)}
.dash1-sc:active{transform:translateY(0)}
.dash1-picking-sc .dash1-sc{outline:2px dashed color-mix(in srgb,var(--danger) 60%,transparent);outline-offset:3px}
.dash1-tip{padding:12px 16px;border-radius:12px;background:var(--surface);border:1px solid var(--border);box-shadow:var(--shadow);color:var(--muted);font-size:12px}
.dash1-pick{display:flex;align-items:center;gap:12px;width:100%;padding:10px 20px;border:0;border-top:1px solid var(--border);background:transparent;text-align:left;font:inherit;color:var(--text);cursor:pointer}
.dash1-pick:first-child{border-top:0}
.dash1-pick:hover{background:color-mix(in srgb,var(--primary) 8%,transparent)}
.dash1-pick .ic{width:34px;height:34px;border-radius:10px;background:color-mix(in srgb,var(--primary) 10%,transparent);color:var(--primary-ink);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.dash1-pick .ic svg{width:18px;height:18px}
.dash1-pick b{font-size:13.5px;display:block}
.dash1-pick small{color:var(--muted)}
/* SR1 (item 34) — the picker's type-to-search box. Deliberately the same
   recipe as .nav-search (line ~896) and .perm-search, so it reads as the
   control this app already has rather than a new one. .d1top carries the
   "no top border" that :first-child can no longer see once the first row is
   filtered out of sight. */
.dash1-psearch{padding:10px 20px 4px}
.dash1-psearch input{width:100%;padding:9px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:13px}
.dash1-psearch input:focus{outline:none;border-color:var(--primary)}
.dash1-pick.d1top{border-top:0}
/* SEC1 (item 33) — a LAUNCHER section. It is deliberately quiet: a module that
   has no dashboard data must not look like one that does, so it is one row
   high, carries no number, and says plainly what it is. */
.dash1-launch .panel-head{align-items:center}
.dash1-launch h3{display:flex;align-items:center;gap:9px}
.dash1-launch h3 svg{width:17px;height:17px;color:var(--primary-ink)}
.dash1-launch small{color:var(--muted)}
/* body.soft — the SAME emboss, hover and press as the stat cards on the left
   (his words, 8 Sep: "hovering & shadow style should be same as the left
   panel"). The recipe is .req-card's, copied line for line: raised = --nm-out,
   hover = the deeper 8px pair, press = --nm-in. Nothing new invented. */
body.soft .dash1-tile .ic,body.soft .dash1-sc,body.soft .dash1-pm button{border:none !important;box-shadow:var(--nm-out) !important;background:var(--surface) !important}
body.soft .dash1-tile:hover .ic,body.soft .dash1-sc:hover,body.soft .dash1-pm button:hover{box-shadow:8px 8px 18px var(--nm-dark), -8px -8px 18px var(--nm-light) !important}
body.soft .dash1-tile:active .ic,body.soft .dash1-sc:active,body.soft .dash1-pm button:active{box-shadow:var(--nm-in) !important}
body.soft .dash1-tile.add .ic{box-shadow:none !important;border:2px dashed var(--border) !important;background:transparent !important}
body.soft .dash1-tip,body.soft .dash1-slot{box-shadow:var(--nm-in) !important;border:none !important;background:var(--surface) !important}
body.soft .dash1-slot.on{box-shadow:var(--nm-in),0 0 0 2px var(--danger) !important}
@media(max-width:1100px){.dash1-two{grid-template-columns:1fr}.dash1-divider{display:none}.dash1-rail{position:static}}
@media(max-width:820px){.dash1-kpis.cards,.dash1-sections{grid-template-columns:1fr}.dash1-qa{grid-template-columns:repeat(4,1fr)}.dash1-slots{flex-direction:column}}

/* ==== SITE1 (9 Sep 2026) — item 33: the PM summary on the dashboard ========
   HIS ANSWER 118 = kha, so this block is ALWAYS ON and sits between the stat
   cards and the hideable sections. It is deliberately NOT a .dash1-block, so
   SEC1's Delete Section pick-mode cannot select it — a hideable-looking thing
   that refuses to hide would be worse than one that never offered.
   ⛔ THE EMBOSS AND HOVER ARE .req-card's OWN RECIPE, copied rather than
   invented — he caught exactly this on 8 Sep when DASH1's tiles did not match
   the cards beside them (DASH1-DESIGN 4.1 ta). The shadow tokens are the
   app's; the ones his designer sent (6px 6px 14px) are the ones he rejected on
   17 Aug and the gate refuses them. */
.site1-pm{margin:14px 0 0}
.site1-pm-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:12px 16px 16px}
.site1-pm-cell{display:flex;flex-direction:column;align-items:flex-start;gap:2px;
  padding:12px 14px;border:1px solid var(--border);border-radius:12px;background:var(--surface);
  cursor:pointer;text-align:left;min-width:0;transition:box-shadow .15s ease,transform .15s ease}
.site1-pm-cell b{font-size:22px;line-height:1.1;color:var(--primary-ink)}
.site1-pm-cell small{font-size:12px;color:var(--muted);overflow-wrap:anywhere}
.site1-pm-cell:hover{transform:translateY(-1px)}
body.soft .site1-pm-cell{box-shadow:var(--nm-out);border:none}
body.soft .site1-pm-cell:hover{box-shadow:8px 8px 16px var(--nm-dark),-8px -8px 16px var(--nm-light)}
body.soft .site1-pm-cell:active{box-shadow:var(--nm-in)}
@media(max-width:820px){.site1-pm-grid{grid-template-columns:1fr}}


/* ==========================================================================
   GOLD1 — HIS INSTRUCTION, 11 SEPTEMBER 2026, after he was shown 20 live
   screens (10 light, 10 dark) of his own app with these exact rules injected
   in the browser. His words: "its better now to changing the text color into
   gold instead of filling the entire title bar, go ahead with this".

   THREE CHANGES, AND NOTHING ELSE:
     1. the small grey text (--muted) becomes PURE BLACK in light and PURE
        WHITE in dark — "need to more visible, in everywhere where this
        existing now in the software". 124 rules read var(--muted), so this
        one line reaches all of them without touching a single selector.
     2. every TABLE HEADER's text becomes the brand gold. NO FILL — he looked
        at a gold-filled bar first and chose the text instead.
     3. the PANEL HEADING and its sub-line (Employees / 1 active..., Today's
        log, Clients / The customers you invoice...) follow the same gold.

   ⛔ IT IS var(--primary-ink), NOT A GOLD LITERAL. --primary-ink is written
   onto documentElement by applyPlatformBrand() (app.js:78) as shade(primary,
   0.28) — the brand gold darkened until it is legible as TEXT (4.7:1 on
   white; the raw #BF9B2E is 2.6:1 and fails WCAG AA). Change the brand in
   Super Admin -> Homepage CMS -> Brand & Colours and these headers follow.
   In the dark theme it is var(--sky) = tint(primary, 0.42) (app.js:85), a
   LIGHT gold, because on a near-black surface the dark ink disappears.
   ⚠️ MEASURED 11 Sep: style.css's own [data-theme="dark"]{--primary-ink:#D9BA57}
   at line 42 IS DEAD — applyPlatformBrand writes --primary-ink as an INLINE
   style on <html>, and an inline style beats any stylesheet rule. That is why
   the dark rules below use --sky and not --primary-ink. Do not "simplify"
   them back.

   SCOPE: body.soft = the signed-in application only. The public marketing
   site keeps its own greys; his item 37 (page content) is still open and
   unstarted, and nothing here should pre-empt it.
   ========================================================================== */
body.soft{ --muted:#000000; }
[data-theme="dark"] body.soft{ --muted:#ffffff; }

body.soft th{ color:var(--primary-ink); }
[data-theme="dark"] body.soft th{ color:var(--sky); }

body.soft .panel-head h3,
body.soft .panel-head small{ color:var(--primary-ink); }
[data-theme="dark"] body.soft .panel-head h3,
[data-theme="dark"] body.soft .panel-head small{ color:var(--sky); }

/* RESTORE 1 — .af-report-tbl th (line 978) is WHITE TEXT ON A BRAND FILL, and
   its selector weighs the same as "body.soft th", so without this line the
   rule above would win by being later and paint gold on dark gold. */
body.soft .af-report-tbl th{ color:#fff; }

/* RESTORE 2 — a placeholder is a HINT, not content. It is the one rule in the
   file that borrows --muted for placeholder text; pure black would make an
   empty box look like a filled one. */
body.soft .e2-srch input::placeholder{ color:#94a3b8; }


/* ===== CM1-VOICE (12 Sep 2026) — the recording bar he asked for ==========
   His screenshot, 12 Sep: [trash] [red dot] [timer] [wave] [pause] [send].
   ⛔ APPENDED ONLY. Not one line above this point was edited.
   ⛔ .rec-wave above is left exactly as it is — the old recorder's wave. It is
      no longer built by app.js, but deleting a rule proves nothing and risks
      something, so it stays. */
.wabar{display:flex;align-items:center;gap:10px;flex:1;min-width:0;height:46px;
  padding:0 8px 0 14px;background:var(--card,#fff);border:1px solid var(--border);
  border-radius:999px;box-shadow:0 2px 10px rgba(15,23,42,.10)}
.wabar .wa-trash,.wabar .wa-pause{flex:0 0 auto;width:30px;height:30px;border:none;
  background:none;cursor:pointer;display:grid;place-items:center;border-radius:50%;font-size:16px}
.wabar .wa-trash{color:var(--text)}
.wabar .wa-trash:hover{background:rgba(220,38,38,.12);color:var(--danger)}
.wabar .wa-pause{color:var(--danger)}
.wabar .wa-pause:hover{background:rgba(220,38,38,.12)}
.wabar .wa-pause.play{color:var(--text)}
.wabar .wa-dot{flex:0 0 auto;width:10px;height:10px;border-radius:50%;
  background:var(--danger);animation:wablink 1.1s infinite}
.wabar .wa-dot.off{animation:none;background:var(--muted)}
@keyframes wablink{0%,100%{opacity:1}50%{opacity:.15}}
.wabar .wa-time{flex:0 0 auto;font-size:15px;font-weight:600;color:var(--text);
  min-width:38px;font-variant-numeric:tabular-nums}
.wabar .wa-wave{flex:1;min-width:0;display:flex;align-items:center;justify-content:flex-end;
  gap:2px;height:26px;overflow:hidden}
.wabar .wa-wave i{display:block;width:3px;height:4px;flex:0 0 auto;border-radius:2px;
  background:var(--muted);transition:height .06s linear}
.wabar .wa-lock{flex:0 0 auto;font-size:12px;color:var(--muted);white-space:nowrap;
  opacity:.8;user-select:none}
.wabar .wa-lock.on{color:var(--danger);opacity:1;font-weight:700}
.wabar .wa-send{flex:0 0 auto;width:42px;height:42px;border-radius:50%;border:none;
  cursor:pointer;background:var(--primary-grad);color:#fff;font-size:18px;
  display:grid;place-items:center;box-shadow:0 2px 6px rgba(15,23,42,.25)}
.wabar .wa-send:active{transform:scale(.94)}
@media (max-width:520px){.wabar{gap:7px;padding-left:10px}.wabar .wa-lock{display:none}}
/* ===== end CM1-VOICE ==================================================== */


/* ===== CM2-DELETE (12 Sep 2026) — the two-choice menu and the 🚫 line =====
   ⛔ APPENDED ONLY. Not one line above this point was edited. */
.cm2-menu{position:fixed;z-index:120;background:var(--card,#fff);border:1px solid var(--border);
  border-radius:12px;box-shadow:0 12px 34px rgba(15,23,42,.20);padding:6px;min-width:196px}
.cm2-menu button{display:block;width:100%;text-align:left;padding:10px 12px;border:none;
  background:none;cursor:pointer;border-radius:8px;font-size:13.5px;color:var(--text);font-family:inherit}
.cm2-menu button:hover:not([disabled]){background:var(--bg)}
.cm2-menu button[disabled]{opacity:.38;cursor:not-allowed}
.cm2-menu button.dang{color:var(--danger);font-weight:600}
.cm2-menu .cm2-why{padding:0 12px 7px;font-size:11.5px;color:var(--muted);line-height:1.45}
.chat-msg .bubble.cm2-gone{font-style:italic;opacity:.75}
.chat-log .chat-msg{-webkit-touch-callout:none}
/* ===== end CM2-DELETE =================================================== */


/* ===== CM3-PLAY (12 Sep 2026) — a voice note plays inside its bubble =====
   His words: "it should play on the same place in chatbox, like whatsapp".
   ⛔ APPENDED ONLY. Not one line above this point was edited.
   The fill uses currentColor so it is white on his gold outgoing bubble and
   dark on the incoming one, without naming either. */
.cm3-voice{display:inline-flex;align-items:center;gap:8px;min-width:180px;
  max-width:240px;margin-top:3px;vertical-align:middle}
.cm3-voice .cm3-btn{flex:0 0 auto;width:30px;height:30px;border-radius:50%;
  border:none;cursor:pointer;font-size:11px;line-height:1;display:grid;
  place-items:center;background:rgba(255,255,255,.30);color:inherit;
  font-family:inherit;padding:0}
.chat-msg.admin .cm3-voice .cm3-btn{background:var(--border);color:var(--text)}
.cm3-voice .cm3-btn:active{transform:scale(.93)}
.cm3-voice .cm3-bar{flex:1;min-width:0;height:4px;border-radius:3px;
  background:rgba(255,255,255,.35);cursor:pointer;overflow:hidden}
.chat-msg.admin .cm3-voice .cm3-bar{background:var(--border)}
.cm3-voice .cm3-bar i{display:block;height:100%;width:0;border-radius:3px;
  background:currentColor;opacity:.92;transition:width .1s linear}
.cm3-voice .cm3-t{flex:0 0 auto;font-size:11px;opacity:.85;min-width:30px;
  text-align:right;font-variant-numeric:tabular-nums}
@media (max-width:520px){.cm3-voice{min-width:150px;gap:6px}}
/* ===== end CM3-PLAY ==================================================== */
