:root{
  --gl-bg:#0f172a; --gl-panel:#ffffff; --gl-ink:#1e293b;
  --gl-accent:#2563eb; --gl-line:#e2e8f0;
}
.gl-body{background:#f1f5f9;color:var(--gl-ink);}
.gl-navbar{background:var(--gl-bg);}
.gl-navbar .navbar-brand,.gl-navbar a{color:#fff;}
.gl-sub{color:#60a5fa;font-weight:600;}
.gl-userchip{color:#cbd5e1;font-size:.9rem;}
.gl-role{display:inline-block;margin-left:.4rem;padding:.1rem .45rem;border-radius:6px;
  background:#1e293b;color:#93c5fd;font-size:.72rem;}
.gl-card{background:var(--gl-panel);border:1px solid var(--gl-line);border-radius:14px;
  padding:1.1rem 1.25rem;box-shadow:0 1px 2px rgba(15,23,42,.04);}
.gl-logo{font-size:1.4rem;font-weight:700;}
.gl-tabs .nav-link{background:#e2e8f0;color:#334155;}
.gl-tabs .nav-link.active{background:var(--gl-accent);color:#fff;}
.gl-cardinput{letter-spacing:.12em;}
.gl-stat{background:#fff;border:1px solid var(--gl-line);border-radius:14px;padding:.85rem 1rem;text-align:center;}
.gl-stat-n{font-size:1.6rem;font-weight:700;line-height:1;}
.gl-stat-l{font-size:.72rem;color:#64748b;text-transform:uppercase;letter-spacing:.04em;margin-top:.25rem;}
.gl-warn .gl-stat-n{color:#d97706;} .gl-danger .gl-stat-n{color:#dc2626;}
.gl-footer{margin-top:auto;padding:.9rem 1rem;text-align:center;color:#94a3b8;
  font-size:.78rem;border-top:1px solid var(--gl-line);background:#fff;}
.gl-ver{color:#2563eb;font-weight:600;}
.gl-foot-sep{margin:0 .4rem;color:#cbd5e1;}
.gl-body{min-height:100vh;display:flex;flex-direction:column;overflow-x:hidden;}
html,body{max-width:100%;}

/* ── Layout shell with sidebar (v0.2) ── */
.gl-shell{display:flex;align-items:stretch;min-height:calc(100vh - 56px - 46px);
  width:100%;max-width:100%;}
.gl-main{flex:1 1 auto;padding:1.5rem 2rem;min-width:0;max-width:100%;overflow-x:hidden;}
/* Inner wrapper keeps content from stretching edge-to-edge on wide screens
   while staying fully fluid on tablets/laptops. */
.gl-main > *{max-width:1400px;}
@media (max-width:992px){
  .gl-main{padding:1rem 1.1rem;}
}
.gl-burger{background:transparent;border:0;color:#fff;font-size:1.25rem;
  line-height:1;cursor:pointer;padding:.25rem .5rem;border-radius:6px;}
.gl-burger:hover{background:rgba(255,255,255,.12);}
/* v0.14: show the burger as "active" while the sidebar is pinned open.
   The burger lives in the navbar (outside the shell), so it carries its
   own state class toggled by JS rather than relying on a shell ancestor. */
.gl-burger.gl-burger-pinned{background:rgba(255,255,255,.18);}

/* Sidebar */
.gl-sidebar{flex:0 0 230px;width:230px;background:#0f172a;border-right:1px solid #1e293b;
  transition:width .18s ease, flex-basis .18s ease;overflow:hidden;}
.gl-nav{display:flex;flex-direction:column;padding:.6rem .5rem;gap:.15rem;}
.gl-nav-item{display:flex;align-items:center;gap:.7rem;padding:.6rem .7rem;border-radius:8px;
  color:#cbd5e1;text-decoration:none;font-size:.92rem;white-space:nowrap;}
.gl-nav-item:hover{background:#1e293b;color:#fff;}
.gl-nav-item.active{background:#2563eb;color:#fff;}
.gl-nav-item.disabled{color:#64748b;cursor:default;}
.gl-nav-item.disabled:hover{background:transparent;color:#64748b;}
.gl-nav-ico{width:1.2rem;text-align:center;font-size:1rem;flex:0 0 auto;}
.gl-nav-txt{flex:1 1 auto;}
.gl-soon{font-size:.62rem;background:#1e293b;color:#94a3b8;border-radius:5px;
  padding:.05rem .35rem;text-transform:uppercase;letter-spacing:.04em;}

/* Collapsed state: hide labels, narrow rail */
.gl-collapsed .gl-sidebar{flex-basis:56px;width:56px;}
.gl-collapsed .gl-nav-txt,.gl-collapsed .gl-soon{display:none;}
.gl-collapsed .gl-nav-item{justify-content:center;gap:0;}

/* ── Auto-minimize sidebar (v0.14) ──────────────────────────────────
   Default behaviour: the shell carries `gl-auto` and starts collapsed to
   the 56px icon rail, giving the user maximum working canvas. On a
   pointer device the rail expands to full width on hover WITHOUT
   reflowing the main content — it overlays instead — so a single glance
   at an icon, then hover, reveals the labels. Clicking the burger PINS
   the sidebar open (adds `gl-pinned`), which overrides the auto-collapse
   until toggled off again. On touch tablets (no hover) the rail stays as
   icons and each icon navigates directly, or the burger pins it open. */

/* When auto mode is on, the rail sits above the main content so expanding
   it does not push the canvas around. */
.gl-auto .gl-sidebar{position:relative;z-index:1040;}

/* Auto + not pinned = collapsed rail by default. */
.gl-auto:not(.gl-pinned) .gl-sidebar{flex-basis:56px;width:56px;}
.gl-auto:not(.gl-pinned) .gl-nav-txt,
.gl-auto:not(.gl-pinned) .gl-soon{display:none;}
.gl-auto:not(.gl-pinned) .gl-nav-item{justify-content:center;gap:0;}

/* Hover-to-peek: on pointer devices, hovering the collapsed rail expands
   it as an overlay (absolute) so the 1400px canvas underneath never
   shifts. Guarded by the hover media feature so touch devices are
   unaffected. */
@media (hover:hover) and (pointer:fine){
  .gl-auto:not(.gl-pinned) .gl-sidebar{
    position:absolute;top:0;bottom:0;left:0;}
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover{
    flex-basis:230px;width:230px;
    box-shadow:4px 0 18px rgba(2,6,23,.35);}
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-txt,
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-soon{display:inline;}
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-item{
    justify-content:flex-start;gap:.7rem;}
  /* Reserve the rail's 56px so the overlay doesn't cover content. */
  .gl-auto:not(.gl-pinned) .gl-main{margin-left:56px;}
}

/* On small screens, sidebar overlays instead of squashing content */
@media (max-width:768px){
  .gl-sidebar{position:fixed;top:56px;bottom:0;left:0;z-index:1030;}
  .gl-collapsed .gl-sidebar{flex-basis:0;width:0;border:0;}
  /* On phones the auto rail also overlays; reset the desktop margin. */
  .gl-auto:not(.gl-pinned) .gl-main{margin-left:0;}
  .gl-auto .gl-sidebar{position:fixed;}
}

/* ── Tablet tuning (v0.19) — Samsung Galaxy Tab A11+ ────────────────
   The A11+ is ~1200px wide in portrait and ~1920px in landscape, and it is a
   TOUCH device (no hover, coarse pointer) — so the desktop hover-to-peek path
   never fires. We keep the icon rail (max working canvas) and let the burger
   pin the labelled sidebar open. On coarse-pointer devices we widen the tap
   targets and, when the rail expands (pinned), overlay it so the canvas never
   reflows. Canvas padding is trimmed so more width is usable for the app. */
@media (pointer:coarse){
  /* Bigger, easier-to-tap nav icons on the rail. */
  .gl-auto:not(.gl-pinned) .gl-nav-item{padding-top:.85rem;padding-bottom:.85rem;}
  /* v0.22: restore the normal collapsible sidebar on tablets. The burger
     pins/unpins it and it sits in-flow (same as desktop) so it reads as a
     proper collapsible sidebar rather than an overlay. */
}

/* Portrait tablets (incl. A11+ portrait ~1200px): trim canvas padding and let
   content use the full usable width. Icon rail stays put for maximum canvas. */
@media (min-width:769px) and (max-width:1280px) and (orientation:portrait){
  .gl-main{padding:1rem 1.1rem;}
  .gl-main > *{max-width:100%;}
}

/* Landscape tablets (A11+ landscape ~1920px): comfortable padding, keep the
   1400px content cap so lines don't get too long on the wide panel. */
@media (min-width:1281px) and (orientation:landscape) and (pointer:coarse){
  .gl-main{padding:1.25rem 1.75rem;}
}

/* ── Tracker ── */
.gl-card{max-width:100%;}
.gl-card .table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch;}
.gl-tracker-table{margin-bottom:0;}
.gl-count-badge{font-size:.75rem;font-weight:600;padding:.25rem .7rem;border-radius:20px;
  background:#dbeafe;color:#1e40af;}
.gl-count-badge.all{background:#d1fae5;color:#065f46;}
.gl-tracker-table th{font-size:.72rem;text-transform:uppercase;letter-spacing:.03em;
  color:#64748b;border-bottom:2px solid var(--gl-line);}
.gl-tracker-table td a{text-decoration:none;font-weight:600;}
.gl-row-overdue{background:#fff5f5;}
.gl-prio{font-size:.72rem;font-weight:600;padding:.1rem .5rem;border-radius:6px;}
.gl-prio-routine{background:#e2e8f0;color:#475569;}
.gl-prio-urgent{background:#fef3c7;color:#92400e;}
.gl-prio-critical{background:#fee2e2;color:#991b1b;}
.gl-tag-mother,.gl-tag-aliquot{display:inline-block;font-size:.6rem;font-weight:700;
  border-radius:4px;padding:0 .3rem;margin-left:.3rem;vertical-align:middle;}
.gl-tag-mother{background:#ede9fe;color:#6d28d9;}
.gl-tag-aliquot{background:#cffafe;color:#0e7490;}
/* On narrower screens, drop the least-critical columns so the table fits.
   Columns: 1 AR, 2 Product, 3 Name, 4 Type, 5 Stage, 6 Status, 7 Priority,
            8 Due, 9 Expiry, 10 Registered By. */
@media (max-width:1100px){
  .gl-tracker-table th:nth-child(2),.gl-tracker-table td:nth-child(2),  /* Product */
  .gl-tracker-table th:nth-child(10),.gl-tracker-table td:nth-child(10) /* Registered By */
  {display:none;}
}
@media (max-width:850px){
  .gl-tracker-table th:nth-child(4),.gl-tracker-table td:nth-child(4),  /* Type */
  .gl-tracker-table th:nth-child(9),.gl-tracker-table td:nth-child(9)   /* Expiry */
  {display:none;}
}

/* ── Detail ── */
.gl-detail-head{border-left:5px solid #2563eb;}
.gl-detail-ar{font-size:1.4rem;font-weight:700;color:#1e293b;}
.gl-detail-name{font-size:1rem;color:#334155;margin-top:.15rem;}
.gl-detail-product{font-size:.82rem;color:#2563eb;font-weight:600;margin-top:.1rem;}
.gl-status{font-size:.78rem;font-weight:600;padding:.3rem .8rem;border-radius:20px;
  background:#d1fae5;color:#065f46;}
.gl-status-disposed,.gl-status-to-be-discarded{background:#fee2e2;color:#991b1b;}
.gl-status-archived{background:#e2e8f0;color:#475569;}
.gl-kv td{padding:.35rem 0;font-size:.86rem;border:0;}
.gl-kv td:first-child{color:#64748b;width:45%;}
.gl-tests,.gl-aliquots{font-size:.86rem;padding-left:1.1rem;}
.gl-tests li,.gl-aliquots li{margin:.15rem 0;}
.gl-aliquots a{text-decoration:none;}
.gl-instructions{background:#fef3c7;border-left:4px solid #d97706;padding:.6rem .8rem;
  border-radius:6px;font-size:.85rem;}

/* Timeline */
.gl-timeline{position:relative;padding-left:1rem;}
.gl-tl-item{position:relative;padding:.5rem 0 .5rem 1rem;border-left:2px solid #e2e8f0;}
.gl-tl-item::before{content:"";position:absolute;left:-7px;top:.7rem;width:12px;height:12px;
  border-radius:50%;background:#cbd5e1;border:2px solid #fff;}
.gl-tl-current::before{background:#10b981;}
.gl-tl-stage{font-weight:600;color:#1e293b;font-size:.9rem;}
.gl-tl-current .gl-tl-stage{color:#059669;}
.gl-tl-meta{font-size:.78rem;color:#64748b;margin-top:.15rem;}
.gl-tl-remark{font-size:.78rem;color:#475569;font-style:italic;margin-top:.15rem;}

/* ── Register wizard (v0.3) ── */
.gl-steps{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;}
.gl-step{display:flex;align-items:center;gap:.45rem;}
.gl-step-n{display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:50%;background:#e2e8f0;color:#64748b;
  font-size:.8rem;font-weight:700;flex:0 0 auto;}
.gl-step-l{font-size:.82rem;color:#94a3b8;white-space:nowrap;}
.gl-step.active .gl-step-n{background:#2563eb;color:#fff;}
.gl-step.active .gl-step-l{color:#1e293b;font-weight:600;}
.gl-step.past .gl-step-n{background:#10b981;color:#fff;}
.gl-step.past .gl-step-l{color:#475569;}
.gl-step-bar{flex:1 1 18px;min-width:18px;height:2px;background:#e2e8f0;}
.gl-step-bar.past{background:#10b981;}
@media (max-width:680px){.gl-step-l{display:none;}}

.gl-ar-banner{border-left:5px solid #2563eb;}
.gl-ar-big{font-size:1.7rem;font-weight:700;letter-spacing:.02em;color:#1e293b;}

.gl-aliquot-choice{display:flex;gap:.75rem;flex-wrap:wrap;}
.gl-radio-card{flex:1 1 220px;border:1px solid var(--gl-line);border-radius:10px;
  padding:.8rem 1rem;cursor:pointer;display:flex;align-items:center;gap:.6rem;}
.gl-radio-card:hover{border-color:#93c5fd;background:#f8fafc;}
.gl-radio-title{font-size:.92rem;}
.gl-ar-chip{display:inline-block;font-family:monospace;font-size:.8rem;
  background:#eff6ff;color:#1e40af;border:1px solid #bfdbfe;border-radius:6px;
  padding:.15rem .5rem;margin:.15rem .25rem .15rem 0;}
.gl-ar-chip.linkable{text-decoration:none;}
.gl-ar-chip.linkable:hover{background:#dbeafe;}

.gl-summary-banner{background:#f5f3ff;border:1px solid #ddd6fe;font-size:.9rem;}
.gl-badge-purple{background:#ede9fe;color:#6d28d9;border-radius:20px;
  padding:.1rem .6rem;font-size:.75rem;font-weight:600;}
.gl-badge-blue{background:#dbeafe;color:#1e40af;border-radius:20px;
  padding:.1rem .6rem;font-size:.75rem;font-weight:600;}

.gl-detail-tabs .nav-link{font-size:.85rem;padding:.4rem .8rem;}
.gl-tally-ok{color:#065f46;background:#d1fae5;border-radius:8px;padding:.5rem .8rem;
  font-size:.86rem;font-weight:600;}
.gl-tally-bad{color:#991b1b;background:#fee2e2;border-radius:8px;padding:.5rem .8rem;
  font-size:.86rem;font-weight:600;}

.gl-done-card{padding:2.2rem 1.5rem;}
.gl-done-check{width:64px;height:64px;border-radius:50%;background:#d1fae5;color:#059669;
  font-size:2rem;display:flex;align-items:center;justify-content:center;margin:0 auto 1rem;}
.gl-done-list{margin-top:1rem;}

/* ── Stage Movement (v0.4) ── */
.gl-move-tabs .nav-link{background:#e2e8f0;color:#334155;margin-right:.4rem;font-size:.9rem;}
.gl-move-tabs .nav-link.active{background:#2563eb;color:#fff;}
.gl-current-stage{font-size:1.1rem;font-weight:700;color:#2563eb;}

/* ── Discard (v0.5) ── */
.gl-review-badge{font-size:.68rem;background:#fef3c7;color:#92400e;border-radius:12px;
  padding:.1rem .5rem;margin-left:.4rem;font-weight:600;}
.gl-dcr{border:1px solid var(--gl-line);border-radius:10px;padding:.8rem 1rem;margin-bottom:.7rem;}
.gl-dcr:last-child{margin-bottom:0;}
.gl-dcr-status{font-size:.7rem;font-weight:600;border-radius:12px;padding:.1rem .55rem;margin-left:.4rem;}
.gl-dcr-status.pending{background:#fef3c7;color:#92400e;}
.gl-dcr-status.approved{background:#d1fae5;color:#065f46;}
.gl-dcr-status.rejected{background:#e2e8f0;color:#475569;}
.gl-dcr-ars{margin:.5rem 0;}
.gl-dcr-reason{font-size:.85rem;color:#475569;background:#f8fafc;border-radius:6px;
  padding:.4rem .6rem;margin-bottom:.5rem;}
.gl-dcr-actions{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;}
.gl-dcr-actions .form-control{max-width:260px;}

/* ── Admin (v0.6) ── */
.gl-pill-green{background:#d1fae5;color:#065f46;border-radius:12px;padding:.1rem .55rem;
  font-size:.72rem;font-weight:600;margin-left:.2rem;}
.gl-pill-grey{background:#e2e8f0;color:#475569;border-radius:12px;padding:.1rem .55rem;
  font-size:.72rem;font-weight:600;margin-left:.2rem;}
.gl-role-badge{background:#eff6ff;color:#1e40af;border:1px solid #bfdbfe;border-radius:6px;
  padding:.1rem .5rem;font-size:.76rem;font-weight:600;}
.gl-edit-cell{background:#f8fafc;}
.gl-master-area{font-family:monospace;font-size:.85rem;}
.gl-perm-table th{font-size:.72rem;white-space:nowrap;}
.gl-lvl{font-size:.68rem;font-weight:700;border-radius:5px;padding:.1rem .4rem;text-transform:uppercase;}
.gl-lvl-full{background:#d1fae5;color:#065f46;}
.gl-lvl-view{background:#dbeafe;color:#1e40af;}
.gl-lvl-create{background:#fef3c7;color:#92400e;}
.gl-lvl-none{background:#f1f5f9;color:#cbd5e1;}

/* ── Audit Trail (v0.7) ── */
.gl-interim-note{background:#fffbeb;border-left:4px solid #d97706;border-radius:6px;
  padding:.7rem 1rem;font-size:.85rem;color:#475569;}
.gl-audit-table th{font-size:.72rem;text-transform:uppercase;letter-spacing:.03em;color:#64748b;}
.gl-audit-action{font-size:.7rem;font-weight:600;border-radius:6px;padding:.1rem .5rem;white-space:nowrap;}
.gl-action-stage-move{background:#dbeafe;color:#1e40af;}
.gl-action-sample-receipt{background:#d1fae5;color:#065f46;}
.gl-action-discard-raised{background:#fef3c7;color:#92400e;}
.gl-action-discard-approved{background:#fee2e2;color:#991b1b;}
.gl-action-discard-rejected{background:#e2e8f0;color:#475569;}

/* ── Barcode & Label (v0.8) ── */
.gl-label-preview{width:177px;height:177px;border:1px solid var(--gl-line);
  border-radius:6px;image-rendering:pixelated;background:#fff;}
.gl-stage-preview{max-width:340px;width:100%;border:1px solid var(--gl-line);
  border-radius:8px;background:#fff;}
.gl-label-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:1rem;}
.gl-label-tile{text-align:center;border:1px solid var(--gl-line);border-radius:10px;
  padding:.6rem;background:#fff;}

/* ── Stage Movement v0.9 ── */
.gl-pill-amber{background:#fef3c7;color:#92400e;border-radius:12px;padding:.1rem .55rem;
  font-size:.72rem;font-weight:600;margin-left:.2rem;}

/* ── Editable role-permission grid (v0.10) ───────────────────────── */
.gl-perm-select{font-size:.72rem;font-weight:600;width:auto;min-width:78px;display:inline-block;text-transform:uppercase;}
.gl-perm-select.gl-lvl-full{background:#d1fae5;color:#065f46;}
.gl-perm-select.gl-lvl-view{background:#dbeafe;color:#1e40af;}
.gl-perm-select.gl-lvl-create{background:#fef3c7;color:#92400e;}
.gl-perm-select.gl-lvl-none{background:#f1f5f9;color:#64748b;}

/* ── Idle auto-logout warning modal (v0.15) ── */
.gl-idle-overlay{position:fixed;inset:0;z-index:2000;display:flex;
  align-items:center;justify-content:center;background:rgba(2,6,23,.55);
  padding:1rem;}
.gl-idle-modal{background:#fff;border-radius:14px;max-width:380px;width:100%;
  padding:1.5rem 1.5rem 1.35rem;box-shadow:0 18px 50px rgba(2,6,23,.4);
  text-align:center;}
.gl-idle-title{font-size:1.15rem;font-weight:700;margin:0 0 .4rem;color:#0f172a;}
.gl-idle-desc{font-size:.95rem;color:#475569;margin:0 0 1.1rem;}
.gl-idle-desc #glIdleCount{font-weight:700;color:#b91c1c;}

/* ── In-app camera scanner (v0.16) ── */
.gl-scan-btn{white-space:nowrap;}
.gl-scan-overlay{position:fixed;inset:0;z-index:2100;display:flex;
  align-items:center;justify-content:center;background:rgba(2,6,23,.7);padding:1rem;}
.gl-scan-modal{background:#fff;border-radius:14px;max-width:460px;width:100%;
  overflow:hidden;box-shadow:0 18px 50px rgba(2,6,23,.5);display:flex;flex-direction:column;
  max-height:92vh;}
.gl-scan-head{display:flex;align-items:center;justify-content:space-between;
  padding:.8rem 1rem;border-bottom:1px solid #e2e8f0;}
.gl-scan-title{font-weight:700;color:#0f172a;}
.gl-scan-x{background:transparent;border:0;font-size:1.5rem;line-height:1;
  color:#64748b;cursor:pointer;padding:0 .3rem;}
/* v0.19 portrait fix: the video box is capped in height so a portrait camera
   stream never balloons to fill the whole screen (which previously blocked the
   background and made aiming hard). object-fit:cover keeps it filled; the
   height cap uses a fraction of the viewport so the modal, message, reticle and
   controls stay visible in BOTH portrait and landscape. */
.gl-scan-videowrap{position:relative;background:#000;width:100%;
  height:46vh;max-height:360px;min-height:200px;overflow:hidden;}
.gl-scan-video{width:100%;height:100%;object-fit:cover;display:block;}
.gl-scan-reticle{position:absolute;inset:16% 12%;border:3px solid rgba(37,99,235,.9);
  border-radius:12px;box-shadow:0 0 0 100vmax rgba(0,0,0,.15);pointer-events:none;}
/* On short landscape (e.g. tablet landscape), shrink the video box further so
   the whole modal fits without scrolling. */
@media (orientation:landscape) and (max-height:600px){
  .gl-scan-videowrap{height:52vh;max-height:300px;}
  .gl-scan-modal{max-width:520px;}
}
.gl-scan-flash .gl-scan-videowrap::after{content:"";position:absolute;inset:0;
  background:rgba(255,255,255,.7);animation:glscanflash .16s ease-out;}
@keyframes glscanflash{from{opacity:1;}to{opacity:0;}}
.gl-scan-msg{padding:.6rem 1rem;font-size:.9rem;color:#475569;min-height:1.2rem;}
.gl-scan-msg.gl-scan-ok{color:#047857;}
.gl-scan-msg.gl-scan-warn{color:#b45309;}
.gl-scan-msg.gl-scan-err{color:#b91c1c;}
.gl-scan-list{padding:0 1rem;overflow-y:auto;max-height:26vh;}
.gl-scan-count{font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;
  color:#64748b;margin:.2rem 0 .4rem;}
.gl-scan-empty{font-size:.85rem;color:#94a3b8;padding:.3rem 0;}
.gl-scan-row{display:flex;align-items:center;justify-content:space-between;
  padding:.3rem .5rem;border:1px solid #e2e8f0;border-radius:8px;margin-bottom:.3rem;}
.gl-scan-code{font-family:monospace;font-size:.85rem;color:#0f172a;}
.gl-scan-del{background:transparent;border:0;color:#b91c1c;font-size:1.1rem;
  line-height:1;cursor:pointer;padding:0 .3rem;}
.gl-scan-actions{display:flex;gap:.5rem;padding:.8rem 1rem;border-top:1px solid #e2e8f0;}

/* ── Register mode landing (v0.17) ── */
.gl-mode-card{display:flex;flex-direction:column;text-align:left;min-height:210px;}
.gl-mode-ico{font-size:1.8rem;line-height:1;margin-bottom:.5rem;}
.gl-mode-disabled{opacity:.72;background:#f8fafc;}

/* ── Thumb-reachable floating scan button (v0.20) ──────────────────
   A fixed bottom-right FAB so the scan action is always under the right
   thumb while the tablet is held right-handed (sidebar is on the left, so
   the right edge is free). Used on scan-capable pages. It reuses the normal
   [data-gl-scan] wiring — it's just positioned for reach. */
/* v0.24: the scan FAB defaults to the RIGHT edge, vertically centered (best
   thumb reach when the tablet is held right-handed in landscape). It is
   DRAGGABLE — the user can drop it anywhere and the position is remembered
   (per-device, via localStorage). JS sets inline left/top when moved; until
   then these defaults apply. */
.gl-scan-fab{position:fixed;right:1rem;top:50%;transform:translateY(-50%);z-index:1200;
  display:inline-flex;align-items:center;gap:.6rem;
  padding:1rem 1.8rem;border:0;border-radius:999px;
  background:#0f766e;color:#fff;font-weight:700;font-size:1.15rem;
  box-shadow:0 8px 22px rgba(2,6,23,.35);cursor:grab;
  touch-action:none;user-select:none;-webkit-user-select:none;}
.gl-scan-fab:hover{background:#0e6b63;}
.gl-scan-fab.gl-dragging{cursor:grabbing;opacity:.9;box-shadow:0 12px 30px rgba(2,6,23,.5);}
/* When JS has positioned it (inline left/top set), neutralise the default
   right/top/transform so inline coordinates take full effect. */
.gl-scan-fab.gl-positioned{right:auto;transform:none;}
.gl-scan-fab .gl-fab-ico{font-size:1.7rem;line-height:1;}
/* Content no longer needs bottom clearance (FAB is on the right edge), but keep
   a little so a bottom-dropped FAB doesn't sit on the footer. */
@media (pointer:coarse){ .gl-main{padding-bottom:2rem;} }

/* ── Fit-on-one-page tuning for tablet landscape (v0.20) ───────────
   Trim vertical rhythm on the key operational pages so they fit an A11+
   landscape screen without scrolling where the content reasonably allows.
   Long lists / big forms will still scroll — that's expected. */
@media (orientation:landscape) and (pointer:coarse){
  .gl-main{padding-top:1rem;padding-bottom:1rem;}
  .gl-card{margin-bottom:.85rem;}
  .gl-instructions{margin-bottom:.85rem;padding:.6rem .8rem;font-size:.9rem;}
  h1.h4{margin-bottom:.6rem !important;}
}

/* ── Slow-connection resilience UI (v0.20) ─────────────────────────── */
.gl-netbar{position:fixed;top:0;left:0;height:3px;width:100%;
  background:#14b8a6;transform-origin:left;transform:scaleX(0);
  transition:transform .3s ease;z-index:3000;opacity:0;}
.gl-netbar.gl-netbar-active{opacity:1;}
.gl-spin{display:inline-block;width:.8em;height:.8em;vertical-align:-.05em;
  border:2px solid rgba(255,255,255,.5);border-top-color:#fff;border-radius:50%;
  animation:glspin .6s linear infinite;}
@keyframes glspin{to{transform:rotate(360deg);}}
.gl-slow-toast{position:fixed;left:50%;bottom:1.25rem;transform:translateX(-50%) translateY(20px);
  background:#0f172a;color:#fff;padding:.6rem 1rem;border-radius:999px;
  font-size:.9rem;box-shadow:0 8px 22px rgba(2,6,23,.4);z-index:3000;
  opacity:0;transition:opacity .2s ease,transform .2s ease;pointer-events:none;}
.gl-slow-toast.gl-slow-show{opacity:1;transform:translateX(-50%) translateY(0);}

/* ── Platform additions (Phase 0): app-grouped sidebar ───────────── */
.gl-nav-group{display:flex;flex-direction:column;gap:.15rem;margin-top:.5rem;}
.gl-nav-group-label{color:#64748b;font-size:.7rem;text-transform:uppercase;
  letter-spacing:.08em;padding:.35rem .7rem .15rem;}
.gl-auto:not(.gl-pinned) .gl-nav-group-label,
.gl-collapsed .gl-nav-group-label{display:none;}

/* ══════════════════════════════════════════════════════════════════
   P31 — enterprise refinement.
   Keeps the existing slate theme and the gl-auto/gl-pinned rail
   mechanics; refines density, hierarchy and the icon treatment so the
   product reads as an HRMS rather than an admin scaffold.
   ══════════════════════════════════════════════════════════════════ */

:root{
  --gl-rail:56px;
  --gl-rail-open:248px;
  --gl-ink-2:#475569;
  --gl-ink-3:#64748b;
  --gl-nav-ink:#94a3b8;
  --gl-nav-hover:#1e293b;
  --gl-ok:#059669;
  --gl-warn:#d97706;
  --gl-danger:#dc2626;
}

/* ── Sidebar chrome ─────────────────────────────────────────────── */
.gl-sidebar{
  background:linear-gradient(180deg,#0f172a 0%,#111d33 100%);
  border-right:1px solid #1e293b;
  scrollbar-width:thin;scrollbar-color:#334155 transparent;
}
.gl-sidebar::-webkit-scrollbar{width:6px;}
.gl-sidebar::-webkit-scrollbar-thumb{background:#334155;border-radius:3px;}

.gl-nav{padding:.5rem .5rem 1rem;}

.gl-nav-item{
  color:var(--gl-nav-ink);
  font-size:.86rem;font-weight:500;
  margin-bottom:1px;
  transition:background .12s ease,color .12s ease;
  position:relative;
}
.gl-nav-item:hover{background:var(--gl-nav-hover);color:#e2e8f0;}
.gl-nav-item.active{
  background:rgba(37,99,235,.16);
  color:#fff;font-weight:600;
}
/* Accent bar rather than a filled pill: at 25+ items a solid blue block
   dominates the whole rail. */
.gl-nav-item.active::before{
  content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;
  border-radius:0 3px 3px 0;background:#3b82f6;
}

/* Icons are inline SVG, sized by the parent so the rail and the expanded
   sidebar share one rule. Sitting in a fixed-width box keeps every label
   on the same left edge regardless of glyph width. */
.gl-nav-ico{
  flex:0 0 20px;width:20px;height:20px;
  display:inline-flex;align-items:center;justify-content:center;
}
.gl-nav-ico svg{width:17px;height:17px;stroke-width:1.7;}
.gl-nav-txt{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* Section labels: the thing that makes 30 links legible. */
.gl-nav-group{margin-top:.35rem;}
.gl-nav-group-label{
  display:block;padding:.75rem .7rem .3rem;
  font-size:.66rem;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:#64748b;
}
.gl-nav-section-label{
  display:block;padding:.6rem .7rem .25rem;
  font-size:.66rem;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:#475569;
}
/* In the collapsed rail, labels would wrap into noise: replace each with a
   hairline so the grouping survives without the text. */
.gl-auto:not(.gl-pinned) .gl-nav-group-label,
.gl-auto:not(.gl-pinned) .gl-nav-section-label,
.gl-collapsed .gl-nav-group-label,
.gl-collapsed .gl-nav-section-label{
  font-size:0;padding:.4rem .5rem;margin:0;
}
.gl-auto:not(.gl-pinned) .gl-nav-group-label::after,
.gl-auto:not(.gl-pinned) .gl-nav-section-label::after,
.gl-collapsed .gl-nav-group-label::after,
.gl-collapsed .gl-nav-section-label::after{
  content:"";display:block;height:1px;background:#1e293b;
}
.gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-group-label,
.gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-section-label{
  font-size:.66rem;padding:.6rem .7rem .25rem;
}
.gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-group-label::after,
.gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-section-label::after{
  display:none;
}

.gl-auto:not(.gl-pinned) .gl-sidebar:hover,
.gl-pinned .gl-sidebar{width:var(--gl-rail-open);flex-basis:var(--gl-rail-open);}

/* ── Top bar ────────────────────────────────────────────────────── */
.gl-navbar{
  border-bottom:1px solid #1e293b;
  box-shadow:0 1px 3px rgba(15,23,42,.18);
  min-height:56px;
}
.gl-navbar .navbar-brand{font-size:1.02rem;letter-spacing:-.01em;}
.gl-userchip{
  display:inline-flex;align-items:center;gap:.5rem;
  padding:.28rem .6rem;border-radius:999px;background:#1e293b;
  font-size:.82rem;color:#e2e8f0;
}
.gl-role{background:#0b1220;color:#93c5fd;margin-left:0;}

/* ── Page furniture ─────────────────────────────────────────────── */
.gl-page-head{
  display:flex;justify-content:space-between;align-items:flex-start;
  gap:1rem;margin-bottom:1.15rem;padding-bottom:.9rem;
  border-bottom:1px solid var(--gl-line);
}
.gl-page-title{margin:0;font-size:1.28rem;font-weight:650;letter-spacing:-.01em;}
.gl-page-sub{
  margin:.3rem 0 0;font-size:.83rem;color:var(--gl-ink-3);
  max-width:70ch;line-height:1.5;
}

.gl-card{padding:0;overflow:hidden;}
.gl-card > .card-body{padding:1.1rem 1.25rem;}
.gl-card-head{
  background:#f8fafc;border-bottom:1px solid var(--gl-line);
  padding:.6rem 1.25rem;font-size:.78rem;font-weight:650;
  text-transform:uppercase;letter-spacing:.05em;color:var(--gl-ink-2);
}

/* Tables read as data, not as boxes. */
.gl-card .table{margin-bottom:0;}
.gl-card .table > thead th{
  background:#f8fafc;border-bottom:1px solid var(--gl-line);
  font-size:.72rem;font-weight:650;text-transform:uppercase;
  letter-spacing:.04em;color:var(--gl-ink-3);padding:.6rem .75rem;
}
.gl-card .table > tbody td{padding:.6rem .75rem;vertical-align:middle;}
.gl-card .table > tbody tr:last-child td{border-bottom:0;}

.gl-badge-ok{background:#dcfce7;color:#166534;font-weight:600;}
.gl-badge-warn{background:#fef3c7;color:#92400e;font-weight:600;}
.gl-badge-danger{background:#fee2e2;color:#991b1b;font-weight:600;}
.gl-badge-muted{background:#f1f5f9;color:#64748b;font-weight:600;}
.gl-badge-info{background:#dbeafe;color:#1e40af;font-weight:600;}

.form-label.small{
  font-weight:600;color:var(--gl-ink-2);margin-bottom:.25rem;font-size:.78rem;
}
.form-text.small{font-size:.74rem;color:var(--gl-ink-3);}

/* ── Dashboard tiles ────────────────────────────────────────────── */
.gl-tile{
  display:block;background:#fff;border:1px solid var(--gl-line);
  border-radius:12px;padding:1rem 1.1rem;text-decoration:none;color:inherit;
  transition:border-color .12s ease,box-shadow .12s ease,transform .12s ease;
  height:100%;
}
.gl-tile:hover{
  border-color:#bfdbfe;box-shadow:0 4px 12px rgba(15,23,42,.07);
  transform:translateY(-1px);color:inherit;
}
.gl-tile-ico{
  width:34px;height:34px;border-radius:9px;background:#eff6ff;color:#2563eb;
  display:inline-flex;align-items:center;justify-content:center;margin-bottom:.6rem;
}
.gl-tile-ico svg{width:18px;height:18px;stroke-width:1.8;}
.gl-tile-title{font-size:.92rem;font-weight:650;margin:0;}
.gl-tile-sub{font-size:.78rem;color:var(--gl-ink-3);margin:.2rem 0 0;line-height:1.45;}

.gl-kpi{
  background:#fff;border:1px solid var(--gl-line);border-radius:12px;
  padding:.9rem 1.05rem;
}
.gl-kpi-n{font-size:1.55rem;font-weight:700;line-height:1.1;letter-spacing:-.02em;}
.gl-kpi-l{
  font-size:.7rem;color:var(--gl-ink-3);text-transform:uppercase;
  letter-spacing:.05em;margin-top:.2rem;font-weight:600;
}
.gl-kpi-ok .gl-kpi-n{color:var(--gl-ok);}
.gl-kpi-warn .gl-kpi-n{color:var(--gl-warn);}
.gl-kpi-danger .gl-kpi-n{color:var(--gl-danger);}

.gl-empty{padding:2.25rem 1rem;text-align:center;color:var(--gl-ink-3);}
.gl-empty-title{font-weight:600;color:var(--gl-ink-2);margin-bottom:.2rem;}

@media (max-width:992px){
  .gl-page-head{flex-direction:column;gap:.6rem;}
}

/* ══════════════════════════════════════════════════════════════════════
   P31 — enterprise shell refinement
   Additive: everything above still applies. This layer fixes what made the
   sidebar read as unfinished (Unicode glyphs, unfoldable sections, flat
   hierarchy) and tightens the content canvas.
   ══════════════════════════════════════════════════════════════════════ */

:root{
  --gl-rail:56px; --gl-rail-open:248px;
  --gl-ink-dim:#94a3b8; --gl-ink-soft:#cbd5e1;
  --gl-rail-bg:#0f172a; --gl-rail-hover:#1e293b; --gl-rail-line:#1e293b;
  --gl-radius:10px;
}

/* ── Icons ──────────────────────────────────────────────────────────
   The icon set is inline SVG (gl_icon). Sizing lives here so every icon
   in the rail, the dashboard tiles and the page headers matches. */
.gl-nav-ico{width:20px;height:20px;display:inline-flex;align-items:center;
  justify-content:center;flex:0 0 20px;font-size:0;}
.gl-nav-ico svg{width:18px;height:18px;display:block;}
.gl-nav-item.active .gl-nav-ico svg{stroke-width:2;}

.gl-sidebar{width:var(--gl-rail-open);flex-basis:var(--gl-rail-open);
  display:flex;flex-direction:column;}
.gl-auto:not(.gl-pinned) .gl-sidebar{flex-basis:var(--gl-rail);width:var(--gl-rail);}
@media (hover:hover) and (pointer:fine){
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover{
    flex-basis:var(--gl-rail-open);width:var(--gl-rail-open);}
  .gl-auto:not(.gl-pinned) .gl-main{margin-left:var(--gl-rail);}
}

.gl-nav{flex:1 1 auto;overflow-y:auto;overflow-x:hidden;gap:.1rem;
  scrollbar-width:thin;scrollbar-color:#334155 transparent;}
.gl-nav::-webkit-scrollbar{width:6px;}
.gl-nav::-webkit-scrollbar-thumb{background:#334155;border-radius:3px;}

.gl-nav-item{border-radius:var(--gl-radius);font-size:.875rem;
  padding:.5rem .65rem;position:relative;transition:background .12s ease;}
.gl-nav-item.active{box-shadow:inset 3px 0 0 rgba(255,255,255,.75);}

/* ── Group + section hierarchy ───────────────────────────────────── */
.gl-nav-group{margin-top:.5rem;}
.gl-nav-group-label{display:block;color:var(--gl-ink-dim);font-size:.66rem;
  text-transform:uppercase;letter-spacing:.09em;font-weight:600;
  padding:.5rem .7rem .25rem;white-space:nowrap;}
.gl-nav-admin{margin-top:auto;border-top:1px solid var(--gl-rail-line);
  padding-top:.35rem;}

/* Sections fold. A full HRMS sidebar is ~30 links; without folding the
   person doing payroll scrolls past recruitment every time. */
.gl-nav-section{margin:.1rem 0;}
.gl-nav-section-label{display:flex;align-items:center;gap:.4rem;cursor:pointer;
  color:var(--gl-ink-dim);font-size:.68rem;text-transform:uppercase;
  letter-spacing:.07em;font-weight:600;padding:.4rem .7rem .3rem;
  white-space:nowrap;list-style:none;user-select:none;}
.gl-nav-section-label::-webkit-details-marker{display:none;}
.gl-nav-section-label:hover{color:var(--gl-ink-soft);}
.gl-sec-txt{flex:1 1 auto;}
.gl-sec-caret{width:0;height:0;flex:0 0 auto;
  border-left:4px solid currentColor;border-top:3.5px solid transparent;
  border-bottom:3.5px solid transparent;transition:transform .15s ease;}
.gl-nav-section[open] > .gl-nav-section-label .gl-sec-caret{transform:rotate(90deg);}
.gl-nav-section-items{display:flex;flex-direction:column;gap:.1rem;}

/* ── Collapsed rail ──────────────────────────────────────────────────
   Labels vanish, so each item needs another way to identify itself.
   `data-label` becomes a tooltip; section labels become a hairline rule
   rather than disappearing entirely, which keeps the grouping legible. */
.gl-collapsed .gl-nav-group-label,
.gl-auto:not(.gl-pinned) .gl-nav-group-label{
  height:1px;padding:0;margin:.55rem .7rem;overflow:hidden;
  background:var(--gl-rail-line);color:transparent;}
.gl-collapsed .gl-nav-section-label,
.gl-auto:not(.gl-pinned) .gl-nav-section-label{
  height:1px;padding:0;margin:.4rem .9rem;overflow:hidden;
  background:var(--gl-rail-line);color:transparent;}
.gl-collapsed .gl-nav-section[open] > .gl-nav-section-label,
.gl-auto:not(.gl-pinned) .gl-nav-section[open] > .gl-nav-section-label{
  background:var(--gl-rail-line);}

@media (hover:hover) and (pointer:fine){
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-group-label,
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-section-label{
    height:auto;background:none;color:var(--gl-ink-dim);
    padding:.4rem .7rem .3rem;margin:0;}
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-nav-group-label{
    padding:.5rem .7rem .25rem;}

  /* Tooltip for the collapsed rail. */
  .gl-auto:not(.gl-pinned) .gl-sidebar:not(:hover) .gl-nav-item::after{
    content:attr(data-label);position:absolute;left:calc(100% + 10px);top:50%;
    transform:translateY(-50%);background:#1e293b;color:#f1f5f9;
    padding:.3rem .55rem;border-radius:6px;font-size:.75rem;white-space:nowrap;
    opacity:0;pointer-events:none;transition:opacity .12s ease;z-index:1050;
    box-shadow:0 4px 12px rgba(2,6,23,.4);}
  .gl-auto:not(.gl-pinned) .gl-sidebar:not(:hover) .gl-nav-item:hover::after{opacity:1;}
}

/* ── Rail toggle ─────────────────────────────────────────────────── */
.gl-rail-toggle{display:flex;align-items:center;gap:.7rem;width:calc(100% - 1rem);
  margin:.35rem .5rem .5rem;padding:.45rem .65rem;background:transparent;
  border:0;border-radius:var(--gl-radius);color:var(--gl-ink-dim);
  font-size:.8rem;cursor:pointer;white-space:nowrap;}
.gl-rail-toggle:hover{background:var(--gl-rail-hover);color:#fff;}
.gl-rail-caret{width:0;height:0;flex:0 0 20px;margin-left:2px;
  border-right:5px solid currentColor;border-top:4px solid transparent;
  border-bottom:4px solid transparent;transition:transform .15s ease;}
.gl-auto:not(.gl-pinned) .gl-rail-caret{transform:rotate(180deg);}
.gl-collapsed .gl-rail-toggle .gl-nav-txt,
.gl-auto:not(.gl-pinned) .gl-rail-toggle .gl-nav-txt{display:none;}
@media (hover:hover) and (pointer:fine){
  .gl-auto:not(.gl-pinned) .gl-sidebar:hover .gl-rail-toggle .gl-nav-txt{display:inline;}
}
@media (max-width:768px){.gl-rail-toggle{display:none;}}

/* ── Page furniture ──────────────────────────────────────────────── */
.gl-page-head{display:flex;justify-content:space-between;align-items:flex-start;
  gap:1rem;flex-wrap:wrap;margin-bottom:1.1rem;padding-bottom:.9rem;
  border-bottom:1px solid var(--gl-line);}
.gl-page-title{font-size:1.3rem;font-weight:600;margin:0 0 .2rem;
  letter-spacing:-.01em;}
.gl-page-sub{color:#64748b;font-size:.85rem;margin:0;max-width:70ch;line-height:1.5;}
.gl-page-actions{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;}

.gl-crumb{font-size:.75rem;color:#94a3b8;margin-bottom:.35rem;}
.gl-crumb a{color:#64748b;text-decoration:none;}
.gl-crumb a:hover{color:var(--gl-accent);text-decoration:underline;}

/* Cards and tables: flatter, tighter, less rounded than Bootstrap default —
   enterprise density rather than consumer roominess. */
.card{border:1px solid var(--gl-line);border-radius:12px;
  box-shadow:0 1px 2px rgba(15,23,42,.04);}
.card-header{background:#f8fafc;border-bottom:1px solid var(--gl-line);
  font-size:.82rem;letter-spacing:.01em;}
.table{font-size:.875rem;}
.table > thead{background:#f8fafc;}
.table > thead th{font-weight:600;font-size:.72rem;text-transform:uppercase;
  letter-spacing:.05em;color:#64748b;border-bottom:1px solid var(--gl-line);
  padding:.6rem .75rem;}
.table > tbody td{padding:.6rem .75rem;vertical-align:middle;}
.table-hover > tbody > tr:hover > *{background:#f8fafc;}
code{font-size:.85em;color:#475569;background:#f1f5f9;padding:.1rem .3rem;
  border-radius:4px;}

.form-label{font-weight:500;color:#475569;margin-bottom:.25rem;}
.form-control,.form-select{border-color:#cbd5e1;}
.form-control:focus,.form-select:focus{border-color:var(--gl-accent);
  box-shadow:0 0 0 3px rgba(37,99,235,.12);}
.form-text{color:#94a3b8;}

.badge{font-weight:500;letter-spacing:.01em;}

/* Empty states read as guidance, not as an error. */
.gl-empty{text-align:center;padding:2.5rem 1rem;color:#94a3b8;}
.gl-empty-title{color:#475569;font-weight:500;margin-bottom:.3rem;}

/* Stat tiles on the dashboard. */
.gl-tile{background:#fff;border:1px solid var(--gl-line);border-radius:12px;
  padding:1rem 1.1rem;display:flex;gap:.9rem;align-items:flex-start;
  text-decoration:none;color:inherit;transition:border-color .12s,box-shadow .12s;}
.gl-tile:hover{border-color:#c7d2fe;box-shadow:0 2px 10px rgba(15,23,42,.06);
  color:inherit;}
.gl-tile-ico{width:36px;height:36px;border-radius:9px;background:#eef2ff;
  color:var(--gl-accent);display:flex;align-items:center;justify-content:center;
  flex:0 0 36px;}
.gl-tile-ico svg{width:19px;height:19px;}
.gl-tile-val{font-size:1.45rem;font-weight:600;line-height:1.1;}
.gl-tile-lbl{font-size:.78rem;color:#64748b;margin-top:.1rem;}
