/* ==========================================================================
   Musoma COHAS — Student Registration System
   Design system + component styling on top of Bootstrap 5.3
   ========================================================================== */

:root{
  /* Official MuCOHAS institutional brand (Blue/Green/Yellow/Black from the logo) — pulled
     directly from mucohas-brand.css, the shared brand stylesheet the e-Library admin panel
     and public site both load. 500/600/800 are the three exact brand values (blue-light/
     blue/blue-dark); 50-400 and 900 are interpolated/extrapolated to fill out a usable scale. */
  --brand-50:#e9f1fb;  --brand-100:#cfe2f6; --brand-200:#9fc5ed;
  --brand-300:#6fa8e3; --brand-400:#478bd8; --brand-500:#1e6fd4;
  --brand-600:#1454a8; --brand-700:#0f4690; --brand-800:#0d3d7a; --brand-900:#0a2d57;
  --brand:#1454a8; --brand-ink:#0d3d7a;

  --mucohas-green:#22a047; --mucohas-green-dark:#1a8038;

  --accent:#f4c430;              /* official MuCOHAS yellow — secondary emphasis (card-top accent line) */
  --accent-dark:#d4a820;

  --bg:#f3f6f9;
  --surface:#ffffff;
  --surface-2:#f8fafb;
  --border:#e5e9ef;
  --border-strong:#d3dae2;
  --text:#263445;
  --muted:#64748b;

  --radius-sm:.5rem;
  --radius:.85rem;
  --radius-lg:1.15rem;

  --shadow-xs:0 1px 2px rgba(15,23,42,.06);
  --shadow-sm:0 1px 3px rgba(15,23,42,.08), 0 1px 2px rgba(15,23,42,.04);
  --shadow:0 6px 18px -6px rgba(15,23,42,.16), 0 3px 8px -4px rgba(15,23,42,.10);
  --shadow-lg:0 24px 48px -12px rgba(15,23,42,.28);

  /* Bootstrap overrides. Bootstrap 5.3's own CSS pairs most color tokens with an
     "-rgb" twin (used wherever a component needs an alpha-blended tint, e.g.
     rgba(var(--bs-primary-rgb), .08)) — overriding only the hex variant leaves
     those alpha-blended spots silently rendering Bootstrap's stock blue/gray
     underneath, which is why .text-muted, plain links, .nav-pills and outline-
     button hover tints were still off-brand everywhere despite --bs-primary
     itself being teal. Every hex override below needs its -rgb twin kept in sync. */
  --bs-body-bg:var(--bg); --bs-body-bg-rgb:243,246,249;
  --bs-body-color:var(--text); --bs-body-color-rgb:38,52,69;
  --bs-body-font-family:'Inter Variable','Inter',system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --bs-border-color:var(--border);
  --bs-primary:var(--brand); --bs-primary-rgb:20,84,168;
  --bs-secondary-color:var(--muted); --bs-secondary-color-rgb:100,116,139;
  --bs-link-color:var(--brand-ink); --bs-link-color-rgb:13,61,122;
  --bs-link-hover-color:var(--brand-900); --bs-link-hover-color-rgb:10,45,87;
  --bs-emphasis-color:var(--text); --bs-emphasis-color-rgb:38,52,69;
  /* Same brand-sync treatment for success/warning as primary above — the e-Library's own
     mucohas-brand.css maps --bs-success/--bs-warning to the official green/yellow too. */
  --bs-success:var(--mucohas-green); --bs-success-rgb:34,160,71;
  --bs-warning:var(--accent); --bs-warning-rgb:244,196,48;
}

*{ box-sizing:border-box; }
[hidden]{ display:none !important; }
body{
  background:
    radial-gradient(1200px 500px at 100% -10%, rgba(28,58,114,.06), transparent 60%),
    radial-gradient(900px 500px at -10% 0%, rgba(240,180,41,.05), transparent 55%),
    var(--bg);
  font-feature-settings:"cv02","cv03","cv04","ss01";
  -webkit-font-smoothing:antialiased;
  letter-spacing:.1px;
}
::selection{ background:var(--brand-200); }

/* thin, unobtrusive scrollbars */
*{ scrollbar-width:thin; scrollbar-color:#c7ced8 transparent; }
*::-webkit-scrollbar{ width:10px; height:10px; }
*::-webkit-scrollbar-thumb{ background:#c7ced8; border-radius:20px; border:2px solid transparent; background-clip:content-box; }
*::-webkit-scrollbar-thumb:hover{ background:#aab3bf; background-clip:content-box; }

/* ---------- top progress bar ---------- */
#app-progress{
  position:fixed; inset:0 0 auto 0; height:3px; z-index:3000;
  background:linear-gradient(90deg,var(--brand-500),var(--brand-300));
  transform:scaleX(0); transform-origin:0 50%;
  transition:transform .3s ease, opacity .35s ease .2s; opacity:0;
  box-shadow:0 0 12px rgba(20,184,166,.6);
}
#app-progress.on{ opacity:1; }

/* ---------- global loading overlay ---------- */
.app-loader{
  position:fixed; inset:0; z-index:2500;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  background:rgba(244,247,249,.72); backdrop-filter:blur(3px) saturate(120%);
  animation:fadeIn .15s ease both;
}
.app-loader[hidden]{ display:none !important; }
.app-loader .spinner-border{ color:var(--brand)!important; }

/* ---------- entrance animation ---------- */
@keyframes fadeIn{ from{opacity:0} to{opacity:1} }
@keyframes fadeInUp{ from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:none} }
.fade-in{ animation:fadeInUp .32s cubic-bezier(.2,.7,.2,1) both; }
.wizard-step{ animation:fadeInUp .24s ease both; }
main.container-fluid > .row > [class*="col"] .card{ animation:fadeInUp .34s ease both; }
main.container-fluid > .row > [class*="col"]:nth-child(2) .card{ animation-delay:.04s; }
main.container-fluid > .row > [class*="col"]:nth-child(3) .card{ animation-delay:.08s; }
main.container-fluid > .row > [class*="col"]:nth-child(4) .card{ animation-delay:.12s; }
@media (prefers-reduced-motion:reduce){ *{ animation:none!important; transition:none!important; } }

/* ==========================================================================
   Navbar
   ========================================================================== */
/* Bootstrap's own [data-bs-theme=dark] rule sets --bs-navbar-color etc. directly on
   this element, so a :root-level override of those same variables loses (a direct
   match on the element always beats an inherited value, no matter its specificity or
   source order) — that was leaving nav labels at Bootstrap's default 55% white,
   compounded with this app's own opacity on .nav-link to a barely-legible ~51%.
   Redeclaring them here, on a selector that matches this same element, is what
   actually wins and brings inactive labels up to a solid, legible white. */
.navbar[data-bs-theme="dark"]{
  --bs-navbar-color: rgba(255,255,255,.92);
  --bs-navbar-hover-color: #fff;
  --bs-navbar-active-color: #fff;
  --bs-navbar-brand-color: #fff;
  --bs-navbar-brand-hover-color: #fff;
}
.navbar.bg-primary{
  background:linear-gradient(180deg,var(--brand-700),var(--brand-800))!important;
  position:sticky; top:0; z-index:1040;
  box-shadow:0 1px 0 rgba(255,255,255,.06) inset, 0 8px 24px -16px rgba(15,23,42,.5);
  transition:box-shadow .25s ease, padding .25s ease;
  padding-top:.6rem; padding-bottom:.6rem;
}
.navbar.is-scrolled{ box-shadow:0 10px 30px -12px rgba(15,23,42,.45); }
/* The navbar above (z-index 1040) sits below Bootstrap's modal (1055), so a modal
   always renders on top of it — but a modal's own default top margin (1.75rem) is
   smaller than the navbar's height (~59px), so every modal in the app visually starts
   overlapping the navbar instead of floating below it. Push every modal's content down
   to actually clear it. */
.modal{ padding-top:64px; }
.navbar-brand{ letter-spacing:.2px; }
.navbar-brand img{ box-shadow:0 2px 8px rgba(0,0,0,.18); }
.navbar .nav-link{ border-radius:.6rem; padding:.45rem .7rem!important; transition:background .18s ease,color .18s ease;
  display:flex; align-items:center; gap:.4rem; white-space:nowrap; }
.navbar .nav-link:hover{ background:rgba(255,255,255,.12); }
.navbar .nav-link.active{ background:rgba(255,255,255,.18); font-weight:600; }

/* Custom "navbar-expand" breakpoint: hamburger on phone/tablet, full row on
   PC. Bootstrap's own -lg mixin (992px) is the right cutoff for that, but at
   that width this app's 9 top-level items + brand + search + profile chip
   don't fit at their normal (mobile) size — so above 992px several things
   shrink (icons drop off top-level links, brand name hides, search narrows,
   role badge hides) specifically to buy back the room a plain row needs.
   The navbar's own container is capped at the same 1200px as the page
   content below it (matching the original design — a full-bleed dark bar
   isn't what was wanted), so this compact sizing is permanent at any width
   from 992px up rather than easing off again on a wider monitor: the
   container never actually gets wider than ~1170px usable regardless of
   how wide the window is. Full icons/labels/search remain in the collapsed
   menu below 992px, where there's no horizontal pressure since everything
   stacks vertically. */
@media (min-width:992px){
  .navbar-expand-custom{ flex-wrap:nowrap; justify-content:flex-start; }
  .navbar-expand-custom .navbar-nav{ flex-direction:row; }
  .navbar-expand-custom .navbar-nav .dropdown-menu{ position:absolute; }
  .navbar-expand-custom .navbar-collapse{ display:flex!important; flex-basis:auto; }
  .navbar-expand-custom .navbar-toggler{ display:none; }
  .navbar .navbar-collapse{ gap:.1rem; }
  .navbar .navbar-nav{ flex-wrap:nowrap; align-items:center; gap:0; }
  .navbar .navbar-nav > .nav-item > .nav-link{ padding:.45rem .22rem!important; font-size:.74rem; }
  .navbar .navbar-nav > .nav-item > .nav-link > .bi{ display:none; }
  .navbar-brand span{ display:none; }
  /* Search and profile need to stay legible rather than being crushed to fit —
     so they keep a real, readable width/size and the *labels* (brand name,
     nav icons) are what give way first at this breakpoint instead. */
  #navSearch{ flex:0 1 130px; min-width:90px; }
  .navbar .user-menu .u-name, .navbar .user-menu .badge{ display:none; }
}
#navSearch #navSearchInput{ width:100%; }
@media (max-width:991.98px){ #navSearch{ width:100%; margin:.25rem 0 .5rem; } }
.navbar .dropdown-menu{ border:none; box-shadow:var(--shadow); border-radius:var(--radius); padding:.35rem; margin-top:.5rem;
  max-height:min(75vh,560px); overflow-y:auto; overscroll-behavior:contain; }
.navbar .dropdown-header{ font-size:.68rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; padding:.5rem .7rem .2rem; }
.navbar .dropdown-item{ border-radius:.55rem; padding:.42rem .7rem; font-weight:500; }
.navbar .dropdown-item:hover{ background:var(--brand-50); color:var(--brand-900); }
.navbar .btn-outline-light{ border-color:rgba(255,255,255,.35); border-radius:.6rem; }
.navbar .btn-outline-light:hover{ background:rgba(255,255,255,.14); border-color:rgba(255,255,255,.5); }
/* Notification bell + profile avatar: a real, clearly-clickable size at every
   width, not just whatever was left over after the nav links took their share. */
.navbar .nav-avatar-btn, .app-topbar .nav-avatar-btn{ min-height:38px; padding-top:.4rem; padding-bottom:.4rem; }
.navbar .nav-avatar-btn > .bi, .app-topbar .nav-avatar-btn > .bi{ font-size:1.25rem; line-height:1; }
.navbar .notif-menu .dropdown-item.bg-light:hover, .app-topbar .notif-menu .dropdown-item.bg-light:hover{ background:var(--brand-50)!important; }

/* ==========================================================================
   App shell — left sidebar (collapsible, off-canvas on mobile via Bootstrap's
   own .offcanvas-lg) + a slim sticky top bar for search/notifications/user
   menu. Replaces the old full-width top navbar for every signed-in page;
   the logged-out branch (login, etc.) still uses the plain .navbar above.
   ========================================================================== */
.app-shell{ display:flex; align-items:stretch; min-height:100vh; }

.app-sidebar{
  width:272px; flex-shrink:0;
  background:linear-gradient(180deg,var(--brand-900),var(--brand-800));
  color:rgba(255,255,255,.92);
  display:flex; flex-direction:column;
}
@media (min-width:992px){
  .app-sidebar{ position:sticky; top:0; height:100vh; max-height:100vh; z-index:1030; transition:width .2s ease; }
  .app-sidebar.collapsed{ width:76px; }
}
.app-sidebar .offcanvas-header{ display:none; } /* we build our own header below instead of Bootstrap's default */

.sidebar-header{
  display:flex; align-items:center; gap:.6rem; padding:1rem .9rem;
  border-bottom:1px solid rgba(255,255,255,.1); flex-shrink:0;
}
.sidebar-brand{ display:flex; align-items:center; gap:.6rem; color:#fff; text-decoration:none; min-width:0; flex:1 1 auto; }
.sidebar-brand-text{ font-weight:700; font-size:1.05rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sidebar-collapse-btn{
  border:none; background:rgba(255,255,255,.08); color:rgba(255,255,255,.85);
  width:28px; height:28px; border-radius:8px; display:inline-flex; align-items:center; justify-content:center;
  flex-shrink:0; transition:background .18s ease, transform .2s ease;
}
.sidebar-collapse-btn:hover{ background:rgba(255,255,255,.16); color:#fff; }
/* The "collapsed" (icon-only) state only ever applies at lg+ — its toggle button is
   itself lg-only (d-none d-lg-inline-flex) and the class is restored from localStorage
   unconditionally on load, so without this guard a desktop-collapsed preference would
   also strip every label out of the mobile offcanvas drawer, leaving it icon-only and
   unreadable with no way to undo it from a phone. */
@media (min-width:992px){
  .app-sidebar.collapsed .sidebar-brand-text{ display:none; }
  .app-sidebar.collapsed .sidebar-collapse-btn i{ transform:rotate(180deg); }
}

.sidebar-nav{ flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; padding:.5rem 0 1.25rem; }
.sidebar-nav::-webkit-scrollbar{ width:6px; }
.sidebar-nav::-webkit-scrollbar-thumb{ background:rgba(255,255,255,.15); border-radius:3px; }
.sidebar-menu{ list-style:none; margin:0; padding:0; }

.menu-group{ margin:0; }
.menu-label{
  display:flex; align-items:center; width:100%; margin:1.1rem 0 .4rem; padding:0 1rem;
  border:none; background:transparent; cursor:pointer; text-align:left;
}
.menu-group:first-child .menu-label{ margin-top:.3rem; }
.menu-label-text{
  font-size:.68rem; font-weight:700; text-transform:uppercase;
  letter-spacing:.08em; color:var(--accent); opacity:.85; transition:opacity .18s ease;
}
.menu-label:hover .menu-label-text{ opacity:1; }
.menu-label-chevron{
  font-size:.65rem; color:rgba(255,255,255,.3); margin-left:auto;
  transition:transform .25s ease, opacity .2s ease; flex-shrink:0;
}
.menu-label[aria-expanded="false"] .menu-label-chevron{ transform:rotate(-90deg); }

/* Bootstrap's .collapse/.collapsing classes already animate the height smoothly —
   this is just list/box reset so the <ul> doesn't look like a plain nested list. */
.submenu{ list-style:none; margin:0; padding:0; }

@media (min-width:992px){
  .app-sidebar.collapsed .menu-label{ height:1px; margin:.6rem .5rem; overflow:hidden; background:rgba(255,255,255,.08); padding:0; pointer-events:none; }
  .app-sidebar.collapsed .menu-label-text, .app-sidebar.collapsed .menu-label-chevron{ display:none; }
  /* icon-only mode always shows every group's items, regardless of what a section's own
     expand/collapse state was left at — Bootstrap's collapse JS is bypassed here. A
     *nested* branch (e.g. Register student -> Single student/Bulk import) is the one
     exception: there's no room for indented children at 76px wide, so it collapses
     down to just its own parent icon instead, like any other leaf item. */
  .app-sidebar.collapsed .submenu:not(.submenu-nested){ display:block!important; height:auto!important; visibility:visible!important; }
  .app-sidebar.collapsed .submenu-nested{ display:none!important; }
}

.sidebar-menu li > a, .menu-item-toggle{
  display:flex; align-items:center; width:100%; gap:.7rem; padding:.55rem .9rem;
  margin:.1rem .6rem; border-radius:.6rem; border:1px solid transparent;
  color:rgba(255,255,255,.82); text-decoration:none; font-size:.85rem;
  background:transparent; text-align:left; cursor:pointer;
  transition:background .18s ease, color .18s ease;
}
.sidebar-menu li > a:hover, .menu-item-toggle:hover{ background:rgba(255,255,255,.08); color:#fff; }
/* Active item: a gold-outlined pill (not a solid fill) — the icon sits in its own
   chip, both picking up a gold tint, matching the e-Library admin panel's active-nav
   treatment instead of our old solid-yellow-background style. */
.sidebar-menu li > a.active, .menu-item-toggle.active{
  background:rgba(244,196,48,.18); box-shadow:inset 3px 0 0 var(--accent);
  color:#fff; font-weight:600;
}
.sidebar-menu li > a.active .menu-icon, .menu-item-toggle.active .menu-icon{ background:rgba(244,196,48,.22); color:var(--accent); }
.menu-icon{
  display:inline-flex; align-items:center; justify-content:center;
  width:1.85rem; height:1.85rem; flex-shrink:0; font-size:1rem;
  border-radius:.5rem; background:rgba(255,255,255,.08);
  transition:background .18s ease, margin .2s ease;
}
.sidebar-menu li > a:hover .menu-icon, .menu-item-toggle:hover .menu-icon{ background:rgba(255,255,255,.14); }
.menu-title{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; flex:1 1 auto; min-width:0; opacity:1; transition:opacity .15s ease; }
.sidebar-menu li > a .badge{ font-size:.62rem; }
.sidebar-menu li > a.menu-logout{ color:#fca5a5; }
.sidebar-menu li > a.menu-logout:hover{ background:rgba(252,165,165,.12); color:#fecaca; }

/* Nested parent item (e.g. "Register student" -> "Single student" / "Bulk import") —
   a real two-level branch, matching the e-Library admin panel's "Register User" ->
   "Single user"/"Batch upload" pattern: its own toggle button, a gold chevron that
   rotates on open, and children indented against a left guide line. */
.menu-item-chevron{
  font-size:.65rem; color:rgba(255,255,255,.4); margin-left:auto; flex-shrink:0;
  transition:transform .25s ease;
}
.menu-item-toggle[aria-expanded="true"] .menu-item-chevron{ transform:rotate(180deg); }
.menu-item-toggle.active .menu-item-chevron{ color:var(--accent); }
.submenu-nested{
  margin:.15rem 0 .15rem 1.55rem; padding-left:.75rem;
  border-left:2px solid rgba(255,255,255,.15);
}
.submenu-nested li > a{
  padding:.4rem .7rem; margin:.1rem .3rem .1rem 0; font-size:.8rem;
  color:rgba(255,255,255,.72);
}
.submenu-nested li > a:hover{ background:rgba(255,255,255,.08); color:#fff; }
.submenu-nested li > a.active{
  background:rgba(244,196,48,.18); box-shadow:inset 3px 0 0 var(--accent);
  color:#fff; font-weight:600;
}
@media (min-width:992px){
  .app-sidebar.collapsed .menu-title, .app-sidebar.collapsed .sidebar-menu li > a .badge,
  .app-sidebar.collapsed .menu-item-chevron{ display:none; opacity:0; }
  .app-sidebar.collapsed .sidebar-menu li > a, .app-sidebar.collapsed .menu-item-toggle{
    justify-content:center; padding:.6rem; margin:.15rem .5rem;
  }
  .app-sidebar.collapsed .sidebar-menu li > a .menu-icon, .app-sidebar.collapsed .menu-item-toggle .menu-icon{ margin:0; }
}

/* Right-hand content column + its own sticky top bar — same blue as the sidebar, matching
   the e-Library admin panel's continuous header band (confirmed from real screenshots). */
.app-content{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; }
.app-topbar{
  position:sticky; top:0; z-index:1020;
  background:linear-gradient(180deg,var(--brand-700),var(--brand-800));
  padding:.55rem 1rem; display:flex; align-items:center; gap:.6rem;
  box-shadow:0 1px 0 rgba(255,255,255,.06) inset, 0 8px 24px -16px rgba(15,23,42,.5);
}
.app-topbar .nav-avatar-btn.btn-outline-secondary{
  color:rgba(255,255,255,.9); border-color:rgba(255,255,255,.3);
}
/* The user-menu trigger reads as a round gold-accented avatar (matching the e-Library
   admin panel's circular profile photo), instead of a plain outline button. */
.app-topbar .user-menu .nav-avatar-btn{ border-radius:999px; padding-left:.4rem; }
.app-topbar .user-menu .nav-avatar-btn > .bi-person-circle{ font-size:1.5rem; color:var(--accent); }
.app-topbar .user-menu .badge{
  background:rgba(244,196,48,.22)!important; color:#fff!important;
  border-color:var(--accent)!important;
}
.app-topbar .nav-avatar-btn.btn-outline-secondary:hover{
  background:rgba(255,255,255,.12); border-color:rgba(255,255,255,.5); color:#fff;
}
.app-topbar .btn-outline-secondary.d-lg-none{ color:rgba(255,255,255,.9); border-color:rgba(255,255,255,.3); }
.app-topbar .btn-outline-secondary.d-lg-none:hover{ background:rgba(255,255,255,.12); color:#fff; }
.app-topbar #navSearch .bi-search{ color:rgba(255,255,255,.55)!important; }
.app-topbar #navSearchInput{ background:rgba(255,255,255,.14); border-color:rgba(255,255,255,.22); color:#fff; }
.app-topbar #navSearchInput::placeholder{ color:rgba(255,255,255,.65); }
.app-topbar #navSearchInput:focus{ background:#fff; color:var(--text); border-color:var(--brand-400); }
.app-topbar #navSearchInput:focus::placeholder{ color:var(--muted); }
.app-topbar #navSearch:focus-within .bi-search{ color:var(--muted)!important; }
.app-main{ flex:1 1 auto; width:100%; max-width:1200px; margin:0 auto; padding:1.25rem 1.25rem 2rem; }
@media (max-width:767.98px){ .app-main{ padding:1rem .85rem 1.5rem; } }

/* Topbar's search box must actually shrink (and the user-menu's text give way
   first) instead of either one being crushed to an unreadable sliver — same
   "labels give way before the controls do" rule the old navbar used. */
.app-topbar #navSearch{ flex:1 1 180px; min-width:0; max-width:320px; }
.app-topbar .nav-avatar-btn{ flex-shrink:0; }
@media (max-width:575.98px){
  .app-topbar #navSearch{ flex:1 1 auto; max-width:none; }
  .app-topbar .user-menu .u-name, .app-topbar .user-menu .badge{ display:none; }
}

/* ==========================================================================
   Typography scale — one family (Inter) across the whole system, sized/
   weighted per element so dense tables and forms stay readable. Component
   rules further down (badges, stat cards, kv lists, stepper…) refine these
   base defaults for their own context and take precedence as normal.
   ========================================================================== */
:root{ --bs-body-font-size:14px; --bs-body-font-weight:400; --bs-body-line-height:1.55; }
h1,h2,h3,h4,h5,h6{ font-weight:600; letter-spacing:-.01px; }
.navbar-brand{ font-weight:700; font-size:1.2rem; }
.btn{ font-weight:600; font-size:.85rem; letter-spacing:.1px; }
table{ font-size:.82rem; }
.form-control, .form-select{ font-size:.85rem; }
.card-header{ font-weight:600; font-size:.95rem; }
[data-countup]{ font-weight:700; }

/* Horizontal "tick all" submission checklists (documents, per-semester supplies) */
.check-row{ display:flex; flex-wrap:wrap; align-items:center; gap:.4rem 1.1rem;
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-sm); padding:.6rem .9rem; }
.check-row .form-check{ margin:0; padding-left:1.6em; }
.check-row .form-check-input{ margin-left:-1.6em; }
.check-row .chk-all + label{ font-weight:600; }
.check-row .sep{ width:1px; align-self:stretch; background:var(--border-strong); }

/* Dashboard "Recent registrations" — compact, name on a single line, no h-scroll */
/* Compact data tables (dashboard "recent", the students list, and similar listings) —
   smaller type, tight rows, names/values kept to one line instead of wrapping. */
.dash-recent, .tbl-compact{ font-size:.82rem; }
.dash-recent th, .dash-recent td, .tbl-compact th, .tbl-compact td{ padding-top:.4rem; padding-bottom:.4rem; }
.dash-recent th, .tbl-compact th{ font-size:.7rem; letter-spacing:.03em; text-transform:uppercase; color:var(--muted); }
.dash-recent .badge, .tbl-compact .badge{ font-size:.68rem; }
.tbl-compact td.text-nowrap, .tbl-compact th.text-nowrap{ white-space:nowrap; }
.dash-recent th, .dash-recent td{ white-space:nowrap; }

/* ==========================================================================
   Cards
   ========================================================================== */
.card{
  position:relative;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--radius);
  box-shadow:var(--shadow-sm);
  transition:box-shadow .22s ease;
  overflow:hidden;
}
/* The accent stripe sits on a positioned ::before so it still paints on top of an
   opaque .card-header/.accordion-button (a plain background layer on .card would
   paint behind those and disappear). It is drawn as a plain square-cornered bar and
   relies on the card's own `overflow:hidden` to clip it to the card's real corner
   curve — giving its own ::before a border-radius instead doesn't work, because a
   radius bigger than half the bar's 4px height gets silently clamped to a tight,
   uneven curve that visibly doesn't "bend" like the card's actual corner (the
   previous, wrong approach). Clipping via the parent is the only way the stripe's
   corner matches the card's corner exactly, at any --radius value.
   `overflow:hidden` here does mean any absolutely-positioned content meant to escape
   the card (a dropdown, a Tom Select panel) would get clipped too — handled by
   giving Tom Select `dropdownParent:'body'` (assets/app.js) so it always portals out,
   and by the explicit `overflow:visible` escape hatch on #expectedBox below for the
   one hand-rolled dropdown that still lives inside its card. */
.card::before{
  content:""; position:absolute; top:0; left:0; right:0; height:4px;
  background:linear-gradient(90deg,var(--brand-600),var(--brand-400),var(--accent));
  pointer-events:none;
}
.card:hover{ box-shadow:var(--shadow); }
/* Bootstrap's accordion CSS sets its own (smaller) corner radii on .accordion-item;
   force our card radius back so an accordion item used as a card (Academic → Modules)
   doesn't end up with mismatched corners. */
.accordion-item.card{ border-radius:var(--radius)!important; }
.accordion-item.card .accordion-button{ border-top-left-radius:var(--radius)!important; border-top-right-radius:var(--radius)!important; }
.accordion-item.card .accordion-button.collapsed{ border-radius:var(--radius)!important; }
/* Branded teal highlight on an open accordion panel, instead of Bootstrap's default blue. */
.accordion-button:not(.collapsed){ background:var(--brand-50); color:var(--brand-900); box-shadow:inset 0 -1px 0 var(--border); }
.accordion-button:focus{ box-shadow:0 0 0 .2rem rgba(28,58,114,.15); }
.accordion-button:not(.collapsed)::after{ filter:hue-rotate(130deg) saturate(1.4); }
.card-header{
  background:var(--surface)!important;
  border-bottom:1px solid var(--border);
  font-weight:600; padding:.85rem 1.1rem;
  border-top-left-radius:var(--radius)!important; border-top-right-radius:var(--radius)!important;
}
.card-body{ padding:1.1rem 1.15rem; }
.card-footer{ background:var(--surface)!important; border-top:1px solid var(--border); }
.list-group-flush .list-group-item{ padding:.7rem 1.1rem; border-color:var(--border); }
.list-group-item{ background:transparent; }
.list-group-item-action{ transition:background .15s ease, padding-left .15s ease; }
.list-group-item-action:hover{ background:var(--brand-50); padding-left:1.4rem; }

/* Card-header icon chip — small tinted rounded square behind a header icon,
   matching the colored icon-chip treatment used elsewhere (stat-ico, menu-icon). */
.card-header-ico{
  display:inline-flex; align-items:center; justify-content:center;
  width:26px; height:26px; border-radius:7px; flex:none; font-size:.85rem;
  background:var(--brand-50); color:var(--brand-700);
}

/* Empty state — icon + message, used in a card/table in place of a bare "no X yet" line */
.empty-state{ text-align:center; padding:2.5rem 1rem; }
.empty-state-ico{
  width:52px; height:52px; border-radius:50%; margin:0 auto .85rem;
  display:flex; align-items:center; justify-content:center;
  background:var(--brand-50); color:var(--brand-300); font-size:1.5rem;
}
.empty-state p{ margin:0 0 .5rem; color:var(--muted); font-size:.85rem; }

/* stat card (dashboard) */
.stat-card{ position:relative; overflow:hidden; transition:transform .22s cubic-bezier(.2,.7,.2,1), box-shadow .22s ease; }
.stat-card:hover{ transform:translateY(-3px); box-shadow:var(--shadow-lg); }
.stat-card .stat-ico{
  width:42px; height:42px; border-radius:12px; display:flex; align-items:center; justify-content:center;
  font-size:1.15rem; flex:none; color:#fff;
  box-shadow:0 8px 16px -6px var(--_icoShadow, rgba(28,58,114,.45));
}
.stat-card .stat-val{ font-size:1.5rem; font-weight:700; line-height:1.1; font-variant-numeric:tabular-nums; letter-spacing:-.02em; overflow-wrap:anywhere; color:#0b1324; }
/* Money values keep to one line instead of wrapping mid-number — a smaller size
   buys back the room a long "TZS 222,072,600" needs at the card's width. */
.stat-card .stat-val[data-money]{ font-size:1.1rem; white-space:nowrap; overflow-wrap:normal; }
.stat-card .stat-lbl{ font-size:.72rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); font-weight:600; }
/* The label/value column is a flex item next to the fixed-width icon; without
   min-width:0 a flex item won't shrink below its content's natural width, so a long
   value (e.g. "TZS 1,000,000") pushes past the card edge and gets clipped by the
   card's overflow:hidden instead of wrapping. */
.stat-card .card-body > div:last-child{ min-width:0; }
@media (max-width:420px){
  .stat-card .stat-val{ font-size:1.15rem; }
  .stat-card .stat-val[data-money]{ font-size:.85rem; }
}
/* Solid-fill icon badges (white glyph on a flat/gradient tone colour, soft colour-matched
   glow) — confirmed against real e-Library dashboard screenshots; its stat-card icons are
   solid squares, not light tints (an earlier pass here got that wrong from reading the
   library's CSS alone, without seeing the rendered page). */
.tone-primary .stat-ico{ background:linear-gradient(135deg,var(--brand-500),var(--brand-700)); --_icoShadow:rgba(20,84,168,.45); }
.tone-success .stat-ico{ background:linear-gradient(135deg,#3fc26a,var(--mucohas-green)); --_icoShadow:rgba(34,160,71,.45); }
.tone-danger  .stat-ico{ background:linear-gradient(135deg,#f87171,#dc2626); --_icoShadow:rgba(220,38,38,.4); }
.tone-muted   .stat-ico{ background:linear-gradient(135deg,#94a3b8,#475569); --_icoShadow:rgba(71,85,105,.35); }
.tone-accent  .stat-ico{ background:linear-gradient(135deg,#f8d566,var(--accent)); --_icoShadow:rgba(244,196,48,.4); }

/* Stat cards get a left accent bar instead of the generic .card top gradient bar —
   same per-tone colors as the icon badge above, matching the e-Library admin panel's
   stat-card treatment (equal selector specificity to .card::before, so this wins by
   simply coming later in the file). */
.stat-card::before{
  top:0; right:auto; bottom:0; left:0; width:4px; height:auto;
  border-radius:var(--radius) 0 0 var(--radius);
  background:var(--brand-700);
}
.tone-primary.stat-card::before{ background:var(--brand-700); }
.tone-success.stat-card::before{ background:var(--mucohas-green); }
.tone-danger.stat-card::before{ background:#dc2626; }
.tone-muted.stat-card::before{ background:#475569; }
.tone-accent.stat-card::before{ background:var(--accent); }

/* Dashboard "Good morning" hero banner — gradient greeting card with quick actions,
   matching the e-Library admin panel's dashboard header (confirmed from real screenshots). */
.dash-hero{
  border-radius:var(--radius-lg); overflow:hidden; position:relative;
  background:linear-gradient(120deg,var(--brand-800) 0%,var(--brand-600) 55%,#0d7a6e 100%);
  box-shadow:var(--shadow);
}
.dash-hero-inner{
  position:relative; z-index:1; padding:1.5rem 1.75rem;
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:1.25rem;
}
.dash-hero h1{ color:#fff; }
.dash-hero-eyebrow{ color:var(--accent); font-size:.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.1em; margin-bottom:.2rem; }
.dash-hero-chip{
  display:inline-flex; align-items:center; gap:.35rem; font-size:.78rem; font-weight:600;
  color:rgba(255,255,255,.92); background:rgba(255,255,255,.14); border-radius:2rem; padding:.3rem .75rem;
}
.dash-hero-chip-warn{ background:rgba(220,38,38,.35); color:#fecaca; }
.dash-hero-actions{ display:flex; flex-wrap:wrap; gap:.5rem; }
.dash-hero .btn-outline-light{ border-color:rgba(255,255,255,.4); font-weight:600; }
.dash-hero .btn-outline-light:hover{ background:rgba(255,255,255,.16); border-color:rgba(255,255,255,.6); }

/* Gold CTA button — the library's primary accent colour used for its one standout action
   (e.g. "Scan desk"); reused here for the hero's main call-to-action. */
.btn-accent{ background:var(--accent); border-color:var(--accent); color:#3f2d00; font-weight:700; }
.btn-accent:hover{ background:var(--accent-dark); border-color:var(--accent-dark); color:#3f2d00; }

/* Compact page-header banner — every other staff page's version of the dashboard hero
   above (icon + title + optional one-line description, no greeting/actions), matching
   the e-Library admin panel's inner-page headers (e.g. "Register User"). */
.page-hero{
  border-radius:var(--radius-lg); overflow:hidden;
  background:linear-gradient(120deg,var(--brand-800) 0%,var(--brand-600) 55%,#0d7a6e 100%);
  box-shadow:var(--shadow);
}
.page-hero-inner{ padding:1.1rem 1.4rem; display:flex; align-items:center; gap:1rem; }
.page-hero-icon{
  width:44px; height:44px; border-radius:12px; flex-shrink:0;
  background:rgba(255,255,255,.14); color:var(--accent);
  display:flex; align-items:center; justify-content:center; font-size:1.3rem;
}
.page-hero-text{ min-width:0; }
.page-hero-text h1{ color:#fff; }
.page-hero-text p{ color:rgba(255,255,255,.78); font-size:.85rem; }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn{
  --bs-btn-border-radius:.65rem;
  font-weight:600; letter-spacing:.1px;
  transition:transform .12s ease, box-shadow .18s ease, background-color .18s ease, border-color .18s ease;
}
.btn-primary:hover, .btn-success:hover{ transform:translateY(-1px); }
.btn:active{ transform:translateY(1px); }
.btn-primary{
  --bs-btn-bg:var(--brand); --bs-btn-border-color:var(--brand);
  --bs-btn-hover-bg:var(--brand-800); --bs-btn-hover-border-color:var(--brand-800);
  --bs-btn-active-bg:var(--brand-900); --bs-btn-active-border-color:var(--brand-900);
  box-shadow:0 8px 18px -8px rgba(28,58,114,.6);
}
.btn-success{ --bs-btn-bg:var(--mucohas-green); --bs-btn-border-color:var(--mucohas-green); --bs-btn-hover-bg:var(--mucohas-green-dark); --bs-btn-hover-border-color:var(--mucohas-green-dark); box-shadow:0 8px 18px -8px rgba(34,160,71,.55); }
.btn-outline-secondary{ --bs-btn-color:var(--muted); --bs-btn-border-color:var(--border-strong); --bs-btn-hover-bg:var(--surface-2); --bs-btn-hover-color:var(--text); --bs-btn-hover-border-color:var(--border-strong); }
.btn-outline-primary{ --bs-btn-color:var(--brand-700); --bs-btn-border-color:var(--brand-300); --bs-btn-hover-bg:var(--brand-700); --bs-btn-hover-border-color:var(--brand-700); }
.btn-link{ text-decoration:none; font-weight:600; }
.btn-link:hover{ text-decoration:none; }
.btn-sm{ --bs-btn-border-radius:.5rem; }
.btn:focus-visible{ outline:none; box-shadow:0 0 0 .22rem rgba(28,58,114,.25); }

/* ==========================================================================
   Forms
   ========================================================================== */
.form-label{ font-size:.78rem; font-weight:600; color:var(--muted); margin-bottom:.3rem; letter-spacing:.02em; }
.form-control, .form-select{
  border-radius:.65rem; border-color:var(--border-strong); padding:.5rem .75rem;
  background-color:var(--surface); color:var(--text);
  transition:border-color .15s ease, box-shadow .15s ease;
}
/* Bootstrap reserves ~2.25rem on the right of a .form-select for its arrow icon;
   the shared padding rule above overwrote that down to .75rem, so the arrow sat
   on top of long option text instead of beside it. */
.form-select{ padding-right:2.25rem; }
.form-control::placeholder{ color:#9aa6b2; }
.form-control:focus, .form-select:focus{
  border-color:var(--brand-500);
  box-shadow:0 0 0 .22rem rgba(28,58,114,.16);
}
.form-control:disabled, .form-select:disabled{ background:var(--surface-2); }
.input-group-text{ background:var(--surface-2); border-color:var(--border-strong); border-radius:.65rem; color:var(--muted); }
.form-check-input:checked{ background-color:var(--brand); border-color:var(--brand); }
.form-check-input:focus{ border-color:var(--brand-400); box-shadow:0 0 0 .22rem rgba(28,58,114,.16); }
.form-text{ font-size:.76rem; }

/* Tom Select — restyle the bootstrap5 theme to match .form-control exactly */
.ts-wrapper.single .ts-control, .ts-wrapper .ts-control{
  border-radius:.65rem!important; border-color:var(--border-strong)!important;
  background-color:var(--surface)!important; min-height:calc(1.5em + 1rem + 2px); padding:.5rem .75rem;
  box-shadow:none!important;
}
.ts-wrapper.focus .ts-control{
  border-color:var(--brand-500)!important; box-shadow:0 0 0 .22rem rgba(28,58,114,.16)!important;
}
.ts-wrapper.is-invalid .ts-control{ border-color:#dc3545!important; }
.ts-dropdown{ border-radius:.65rem; border-color:var(--border-strong); box-shadow:var(--shadow); overflow:hidden; }
.ts-dropdown .active{ background-color:var(--brand-50); color:var(--brand-ink); }
.ts-wrapper .ts-control > input{ font-family:inherit; }
.ts-wrapper.single .ts-control{ font-size:.85rem; }

/* Lucide icons replace Bootstrap-Icons glyphs (see the shim in app.js) — sized to
   match the surrounding text like a font icon would, colour inherited via currentColor. */
svg.lucide{ width:1em; height:1em; stroke-width:1.75; vertical-align:-.15em; flex-shrink:0; }

/* SweetAlert2 — compact, standard-size popup (default SweetAlert2 sizing runs large) */
.swal2-popup{
  border-radius:var(--radius); font-family:var(--bs-body-font-family);
  width:22rem!important; padding:1.1rem 1.3rem 1.4rem!important; font-size:.85rem;
}
.swal2-icon{ width:3em!important; height:3em!important; margin:.6rem auto .4rem!important; }
.swal2-icon .swal2-icon-content{ font-size:1.6em!important; }
.swal2-title{ font-size:1.02rem; padding:0 0 .2rem; margin:0; }
.swal2-html-container{ font-size:.82rem; margin:.4rem 0 0; line-height:1.45; }
.swal2-actions{ margin:1.1rem 0 0; gap:.5rem; }
.swal2-styled{ padding:.4rem .95rem!important; font-size:.82rem!important; }
.swal2-styled.swal2-confirm{ border-radius:.6rem!important; box-shadow:none!important; }
.swal2-styled.swal2-cancel{ border-radius:.6rem!important; background:var(--surface-2)!important; color:var(--text)!important; box-shadow:none!important; }

/* ==========================================================================
   Tables
   ========================================================================== */
.table{ --bs-table-bg:transparent; margin-bottom:0; }
.table thead th{
  font-size:.68rem; text-transform:uppercase; letter-spacing:.07em; font-weight:700;
  color:var(--muted); background:var(--surface-2);
  border-bottom:1px solid var(--border); padding:.7rem .9rem; white-space:nowrap;
}
.table tbody td{ padding:.7rem .9rem; border-color:var(--border); vertical-align:middle; }
.table tbody tr{ transition:background .12s ease; }
.table-hover tbody tr:hover{ background:var(--brand-50); }
.table tfoot td{ border-color:var(--border); background:var(--surface-2); }
.table-responsive{ border-radius:var(--radius); }
.card > .table-responsive:last-child{ border-bottom-left-radius:var(--radius); border-bottom-right-radius:var(--radius); }
.fee-line{ font-variant-numeric:tabular-nums; letter-spacing:-.01em; white-space:nowrap; }
.tbl-fees td, .tbl-fees th{ vertical-align:middle; white-space:nowrap; }
.tbl-fees .num{ text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums; }
.tbl-fees .charge-ico{ width:26px; height:26px; border-radius:7px; display:inline-flex; align-items:center;
  justify-content:center; background:var(--brand-50); color:var(--brand-700); font-size:.8rem; flex:none; }
.btn-icon{ --bs-btn-padding-x:.4rem; --bs-btn-padding-y:.2rem; line-height:1; }
.unit-note{ font-weight:400; font-size:.7rem; color:var(--muted); }

/* batch payment rows */
.batch-form tbody tr.row-off{ opacity:.45; }
.batch-form tbody tr.row-off .charge-ico{ filter:grayscale(1); }
.batch-form .bl-amt{ max-width:8rem; }

/* card-header tabs */
.nav-tabs{ --bs-nav-tabs-link-active-color:var(--brand-800); border-bottom-color:var(--border); }
.nav-tabs .nav-link{ color:var(--muted); font-weight:600; border:0; border-bottom:2px solid transparent; border-radius:0; padding:.5rem .85rem; }
.nav-tabs .nav-link:hover{ color:var(--brand-700); border-bottom-color:var(--brand-200); }
.nav-tabs .nav-link.active{ color:var(--brand-800); background:transparent; border-bottom-color:var(--brand-600); }
/* .nav-pills compiles its active state as a literal hex in Bootstrap's own CSS
   rather than var(--bs-primary), so the global brand override above can't reach
   it — same class of bug as the tabs above, fixed the same way. */
.nav-pills .nav-link{ color:var(--muted); font-weight:600; border-radius:.6rem; }
.nav-pills .nav-link:hover{ color:var(--brand-700); background:var(--brand-50); }
.nav-pills .nav-link.active, .nav-pills .show > .nav-link{ color:#fff; background:var(--brand); }

/* Pill-shaped, color-coded row-action buttons (view/edit/delete/process) —
   matches the e-Library admin panel's table action buttons exactly
   (css/mucohas-actions.css). */
.row-act{
  display:inline-flex; align-items:center; gap:.32rem;
  padding:.3rem .75rem; border-radius:999px; font-size:.78rem; font-weight:700;
  border:1.5px solid transparent; text-decoration:none; line-height:1.2;
  transition:background-color .18s ease, color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.row-act:hover{ transform:translateY(-1px); color:#fff; }
.row-act--view{ background:#dcf4e4; color:#0d5a29; border-color:#b7e4c7; }
.row-act--view:hover{ background:#1a8038; border-color:#1a8038; }
.row-act--edit{ background:#dfeafd; color:#0a3a8a; border-color:#bcd3f7; }
.row-act--edit:hover{ background:var(--brand-600); border-color:var(--brand-600); }
.row-act--delete{ background:#fde3e1; color:#9f1a11; border-color:#f5bcb7; }
.row-act--delete:hover{ background:#c62828; border-color:#c62828; }
.row-act--process{ background:#fff0c4; color:#744500; border-color:#f3d98c; }
.row-act--process:hover{ background:#9a5f00; border-color:#9a5f00; }
.row-act:focus-visible{ outline:3px solid rgba(20,84,168,.35); outline-offset:2px; }

/* ==========================================================================
   Badges / pills / tags
   ========================================================================== */
.badge{ font-weight:600; letter-spacing:.02em; border-radius:.5rem; padding:.34em .6em; }
.badge.text-bg-light{ background:var(--surface-2)!important; color:var(--muted)!important; }
.badge.text-bg-info{ background:#e0f2fe!important; color:#0369a1!important; }
.badge.text-bg-warning{ background:#fef3c7!important; color:#92400e!important; }
.badge.text-bg-success{ background:#dcfce7!important; color:#166534!important; }
.badge.text-bg-danger{ background:#fee2e2!important; color:#991b1b!important; }
.badge.text-bg-secondary{ background:#e2e8f0!important; color:#475569!important; }
.badge.text-bg-dark{ background:var(--text)!important; }
.badge.text-bg-primary{ background:var(--brand-100)!important; color:var(--brand-800)!important; }

.pill{ display:inline-block; padding:.22em .65em; border-radius:999px; font-size:.7rem; font-weight:700; letter-spacing:.02em; }
.pill-paid{ background:#dcfce7; color:#166534; }
.pill-partial{ background:#fef3c7; color:#92400e; }
.pill-unpaid{ background:#fee2e2; color:#991b1b; }
.tag{ display:inline-block; padding:.2em .55em; border-radius:.45rem; font-size:.7rem; font-weight:600; }
.tag-green{ background:#dcfce7; color:#166534; }
.tag-amber{ background:#fef3c7; color:#92400e; }
.tag-grey{ background:#e2e8f0; color:#475569; }

/* ==========================================================================
   Alerts
   ========================================================================== */
.alert{ border:1px solid transparent; border-left-width:4px; border-radius:.7rem; box-shadow:var(--shadow-xs); }
.alert-success{ background:#eaf7ee; border-color:#bde6c8; border-left-color:var(--mucohas-green); color:#0f5c25; }
.alert-danger{ background:#fef2f2; border-color:#fecaca; border-left-color:#dc2626; color:#991b1b; }
.alert-warning{ background:#fffbeb; border-color:#fde68a; border-left-color:#d97706; color:#92400e; }
.alert-info{ background:#eff6ff; border-color:#bfdbfe; border-left-color:var(--accent); color:#1e40af; }

/* ==========================================================================
   Accordion / offcanvas
   ========================================================================== */
.accordion{ --bs-accordion-border-color:var(--border); --bs-accordion-btn-focus-box-shadow:0 0 0 .22rem rgba(28,58,114,.16); }
.accordion-button{ font-weight:600; padding:.8rem 1.05rem; }
.accordion-button:not(.collapsed){ background:var(--brand-50); color:var(--brand-900); box-shadow:none; }
.accordion-button:not(.collapsed)::after{ filter:hue-rotate(120deg) saturate(1.4); }
.accordion-body{ padding:1rem 1.05rem; }
.accordion-flush .accordion-item:last-child{ border-bottom:0; }
.offcanvas{ border-left:1px solid var(--border)!important; }
.offcanvas-header{ border-bottom:1px solid var(--border); }

/* key/value list */
dl.kv{ display:grid; grid-template-columns:minmax(140px,190px) 1fr; gap:.4rem 1rem; margin:0; font-size:.85rem; }
dl.kv dt{ color:var(--muted); font-weight:500; }
dl.kv dd{ margin:0; font-weight:600; word-break:break-word; }
@media (max-width:576px){ dl.kv{ grid-template-columns:1fr; gap:.15rem; } dl.kv dd{ margin-bottom:.55rem; } }

/* Profile tabs (narrow sidebar column) — stack label above value so each detail
   gets the card's full width and renders on one line instead of wrapping. */
#profileTabsCard .tab-pane dl.kv{ grid-template-columns:1fr; gap:0; }
#profileTabsCard .tab-pane dl.kv dt{ font-size:.72rem; text-transform:uppercase; letter-spacing:.03em; margin-top:.7rem; }
#profileTabsCard .tab-pane dl.kv dt:first-child{ margin-top:0; }
#profileTabsCard .tab-pane dl.kv dd{ white-space:nowrap; overflow-x:auto; }

/* ==========================================================================
   Wizard stepper
   ========================================================================== */
.stepper{ display:flex; margin:.25rem 0 1.5rem; }
.stepper .step{ flex:1 1 0; text-align:center; position:relative; padding:0 .25rem; min-width:74px; }
.stepper .step::before{
  content:""; position:absolute; top:19px; left:-50%; width:100%; height:2px;
  background:var(--border-strong); z-index:0;
}
.stepper .step:first-child::before{ display:none; }
.stepper .step.done::before, .stepper .step.active::before{ background:var(--brand-400); }
.stepper .dot{
  position:relative; z-index:1; width:40px; height:40px; border-radius:50%;
  background:var(--surface); border:2px solid var(--border-strong); color:var(--muted);
  display:flex; align-items:center; justify-content:center; margin:0 auto .45rem; font-weight:700;
  transition:all .28s cubic-bezier(.2,.7,.2,1);
}
.stepper .step.active .dot{
  border-color:var(--brand); color:var(--brand-700);
  box-shadow:0 0 0 5px rgba(28,58,114,.14); transform:scale(1.06);
}
.stepper .step.done .dot{ background:var(--brand); border-color:var(--brand); color:#fff; }
.stepper .step.done .dot::after{ content:"\2713"; font-size:1rem; }
.stepper .step.done .dot span{ display:none; }
.stepper .label{ font-size:.74rem; color:var(--muted); font-weight:500; }
.stepper .step.active .label{ color:var(--brand-800); font-weight:700; }
.stepper .step.done .label{ color:var(--brand-700); }
@media (max-width:520px){ .stepper .label{ display:none; } .stepper{ margin-bottom:1rem; } }

/* fee preview / collapse chevrons */
.list-collapse-toggle{ user-select:none; }
.list-collapse-toggle .bi-chevron-right{ transition:transform .2s ease; }
.list-collapse-toggle[aria-expanded="true"] .bi-chevron-right{ transform:rotate(90deg); }
.cursor-pointer{ cursor:pointer; }

/* ==========================================================================
   Auth (login / change password)
   ========================================================================== */
.auth-card{
  border:none; border-radius:var(--radius-lg); overflow:hidden;
  box-shadow:var(--shadow-lg);
}
.auth-card .auth-accent{ height:5px; background:linear-gradient(90deg,var(--brand-600),var(--brand-400),var(--accent)); }
.auth-logo{
  width:84px; height:84px; border-radius:22px; object-fit:contain; padding:12px;
  background:var(--surface); box-shadow:0 10px 24px -10px rgba(28,58,114,.5), 0 0 0 1px var(--border);
}
.auth-wrap{ min-height:calc(100dvh - 160px); display:flex; align-items:center; }

/* Split-screen staff login — dark "marketing" panel + a plain light panel with the
   actual form, matching the e-Library admin panel's login page (confirmed from real
   screenshots). Only login.php opts into this (sets $authSplit before requiring
   head.php/foot.php) — every other page keeps the normal navbar+container chrome. */
.auth-split{ min-height:100dvh; display:flex; }
.auth-split-left{
  flex:1 1 50%; flex-direction:column; justify-content:center; gap:1.75rem;
  padding:3.5rem 4rem; position:relative; overflow:hidden; color:#fff;
  background:
    linear-gradient(160deg,var(--brand-900) 0%,var(--brand-700) 60%,var(--brand-500) 100%);
}
.auth-split-left::before{
  content:""; position:absolute; inset:0; pointer-events:none; opacity:.5;
  background-image:
    linear-gradient(rgba(255,255,255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.06) 1px, transparent 1px);
  background-size:42px 42px;
  mask-image:radial-gradient(circle at 30% 30%, #000 0%, transparent 75%);
}
.auth-split-brand{ display:flex; align-items:center; gap:.75rem; position:relative; z-index:1; }
.auth-split-brand img{ width:48px; height:48px; border-radius:14px; background:rgba(255,255,255,.1); padding:8px; object-fit:contain; }
.auth-split-headline{ font-size:2.5rem; font-weight:800; line-height:1.12; color:#fff; letter-spacing:-.02em; position:relative; z-index:1; max-width:560px; }
.auth-split-headline .text-accent{ color:var(--accent)!important; }
.auth-split-sub{ color:rgba(255,255,255,.78); font-size:1.02rem; max-width:480px; position:relative; z-index:1; }
.auth-split-features{ display:flex; flex-direction:column; gap:.7rem; position:relative; z-index:1; }
.auth-split-feature{
  display:flex; gap:.9rem; align-items:flex-start; background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.1); border-radius:var(--radius); padding:.85rem 1.1rem;
}
.auth-split-feature-ico{
  width:40px; height:40px; border-radius:10px; background:var(--accent); color:#3f2d00;
  display:flex; align-items:center; justify-content:center; flex-shrink:0; font-size:1.05rem;
}
.auth-split-feature .small{ color:rgba(255,255,255,.72); }
.auth-split-right{ flex:1 1 50%; display:flex; align-items:center; justify-content:center; padding:2rem 1.25rem; background:var(--bg); }
/* The form sits in an actual elevated card (background/border/shadow/padding),
   not bare fields floating on the page — matching the e-Library login panel's
   card treatment. Entrance animation via .fade-in (fadeInUp, defined above). */
.auth-split-card{
  position:relative; width:100%; max-width:420px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lg); padding:2.25rem 2rem; overflow:hidden;
}
.auth-split-card::before{
  content:""; position:absolute; top:0; left:0; right:0; height:4px;
  background:linear-gradient(90deg,var(--brand-600),var(--brand-400),var(--accent));
}
.auth-split-avatar{
  width:84px; height:84px; border-radius:50%; object-fit:contain; padding:14px;
  background:var(--surface); box-shadow:0 10px 24px -10px rgba(28,58,114,.5), 0 0 0 1px var(--border);
  display:block; margin:0 auto 1rem;
  animation:popIn .4s .1s cubic-bezier(.2,.8,.2,1.2) both;
}
.auth-split-card .form-control, .auth-split-card .input-group-text{
  background:var(--brand-50); border-color:transparent; border-radius:.85rem;
  padding-top:.65rem; padding-bottom:.65rem;
}
.auth-split-card .input-group-text{ border-top-right-radius:0; border-bottom-right-radius:0; }
.auth-split-card .form-control{ border-top-left-radius:0; border-bottom-left-radius:0; }
.auth-split-card .form-control:focus{ background:var(--surface); border-color:var(--brand-400); }
.auth-split-card .input-group{ transition:transform .18s ease; }
.auth-split-card .input-group:focus-within{ transform:translateY(-1px); }
@keyframes popIn{ from{opacity:0; transform:scale(.7)} to{opacity:1; transform:scale(1)} }
@media (max-width:575.98px){ .auth-split-card{ padding:1.75rem 1.4rem; } }

/* ==========================================================================
   Misc
   ========================================================================== */
h1,h2,h3,h4,h5{ letter-spacing:-.01em; color:#0b1324; }
h1.h4, h1.h3{ font-weight:700; }
.text-primary{ color:var(--brand-700)!important; }
a, a:hover, a:focus{ text-decoration:none; }
.toast{ border-radius:.7rem; box-shadow:var(--shadow); }
footer{ color:var(--muted); }
hr{ border-color:var(--border); opacity:1; }
.empty-state{ text-align:center; padding:2.5rem 1rem; color:var(--muted); }
.empty-state .bi{ font-size:2rem; opacity:.5; display:block; margin-bottom:.5rem; }

@media print{
  #app-progress, .navbar, footer, .app-loader, .no-print, .btn, .offcanvas{ display:none!important; }
  body{ background:#fff; }
  .card{ box-shadow:none; border:1px solid #dee2e6; break-inside:avoid; }
  a{ color:inherit; }
}
