/* =====================================================================
   PES Support — shared design system
   ---------------------------------------------------------------------
   This file is the single source of truth for design tokens (colors,
   spacing, radius, shadows) and every reusable component class (buttons,
   cards, tables, forms, badges, chips, modal, toast, sidebar/topbar
   layout). Page-specific rules belong in static/css/<app>/style.css and
   should only ever build on top of the classes defined here — never
   redefine a token.

   Theme switching uses the project's existing convention: the
   `data-bs-theme="light|dark"` attribute on <html>, persisted under the
   `pes-theme` localStorage key (see base.html / dashboard_base.html).
   ===================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700;900&family=Noto+Sans:wght@400;500;600;700;800&display=swap');

:root{
  --primary:#00377B;
  --primary-600:#002A5E;
  --primary-100:#EEEEFD;
  --primary-text:var(--primary);
  --success:#17C671;
  --success-100:#E8FBF1;
  --warning:#FF9533;
  --warning-100:#FEF6E6;
  --danger:#F1416C;
  --danger-100:#FDEEF2;
  --info:#17A2E0;
  --info-100:#E8F8FE;
  --vip:#B8860B;
  --vip-100:#FBF3E0;

  --bg:#F5F6FB;
  --surface:#FFFFFF;
  --surface-2:#FAFBFE;
  --border:#E8EAF3;
  --text:#1B1E2B;
  --muted:#7C8098;
  --muted-2:#A1A5B7;
  --shadow: 0 2px 10px rgba(24,28,50,0.06), 0 1px 2px rgba(24,28,50,0.05);
  --shadow-lg: 0 12px 32px rgba(24,28,50,0.10);
  --radius: 8px;

  --sidebar-width: 264px;
  --sidebar-width-collapsed: 84px;
  --topbar-height: 70px;
}

html[data-bs-theme="dark"]{
  --bg:#14161F;
  --surface:#1B1E2B;
  --surface-2:#20222F;
  --border:#2A2D3D;
  --text:#EDEEF5;
  --muted:#8A8DA3;
  --muted-2:#63667A;
  --primary-100:#25264A;
  --success-100:#123326;
  --warning-100:#332A14;
  --danger-100:#3A1C28;
  --info-100:#122C38;
  --vip:#E0B84B;
  --vip-100:#332C15;
  --shadow: 0 2px 8px rgba(0,0,0,0.25);
  --shadow-lg: 0 16px 40px rgba(0,0,0,0.45);
  /* --primary itself stays the same navy in dark mode (buttons rely on
     that), but that navy is unreadable as body-copy text on a dark
     surface — this token is for text-color usages only. */
  --primary-text:#5EA3FF;
}

/* ---------------------------- reset / base ---------------------------- */
*{ box-sizing:border-box; }
html,body{ margin:0; padding:0; }
body{
  font-family:'Inter', sans-serif;
  background:var(--bg);
  color:var(--text);
  -webkit-font-smoothing:antialiased;
  transition:background .25s ease,color .25s ease;
}
h1,h2,h3,h4,.brand{ font-family:'Sora', sans-serif; }
h1,h2,h3,h4{ margin:0; color:var(--text); }
a{ color:inherit; text-decoration:none; }
button{ font-family:inherit; color:inherit; }
img{ max-width:100%; }
::selection{ background:var(--primary); color:#fff; }

/* Thin, theme-matching scrollbar everywhere (page scroll, table-wrap,
   dropdown menus) instead of the browser/OS's default chunky scrollbar. */
*{ scrollbar-width:thin; scrollbar-color:var(--border) transparent; }
::-webkit-scrollbar{ width:9px; height:9px; }
::-webkit-scrollbar-track{ background:transparent; }
::-webkit-scrollbar-thumb{ background-color:var(--border); border-radius:20px; }
::-webkit-scrollbar-thumb:hover{ background-color:var(--muted); }

/* ------------------------------- layout -------------------------------- */
.app{ display:flex; min-height:100vh; }

.sidebar{
  width:var(--sidebar-width); flex-shrink:0; background:var(--bg);
  border-right:none;
  display:flex; flex-direction:column;
  position:fixed; top:0; left:0; bottom:0; z-index:40;
  transition:transform .25s ease, width .2s ease, background .25s ease;
}
.sidebar.collapsed{ width:var(--sidebar-width-collapsed); }
.sidebar.dark{ background:var(--primary); }

.main{
  margin-left:var(--sidebar-width); flex:1; display:flex; flex-direction:column;
  min-width:0; transition:margin-left .2s ease;
}
.sidebar.collapsed ~ .main{ margin-left:var(--sidebar-width-collapsed); }

.topbar{
  height:var(--topbar-height); background:var(--bg);
  display:flex; align-items:center; gap:16px; padding:0 26px;
  position:sticky; top:0; z-index:30;
}

.content{ padding:45px 30px 60px; max-width:1360px; width:100%; margin:0 auto; }

.content-shell{
  /* No isolation:isolate — that would trap descendant modals' z-index inside
     this element's own stacking context, so they could never paint above
     the sidebar/topbar (which sit outside it with their own z-index). */
  position:relative; margin:0 22px 22px; border:1px solid var(--border);
  border-radius:26px; box-shadow:var(--shadow-lg); overflow:hidden;
  flex:1; display:flex; flex-direction:column;
  /* Distinct from .card's solid --surface white/dark, so cards actually
     stand out instead of blending into the shell behind them. */
  background:linear-gradient(180deg, var(--surface-2) 0%, var(--bg) 100%);
}
.content-shell::before{
  content:""; position:absolute; top:-120px; right:-100px; width:320px; height:320px; z-index:0;
  background:radial-gradient(circle, color-mix(in srgb, var(--primary) 10%, transparent) 0%, transparent 70%);
  pointer-events:none;
}
.content-shell .content{ flex:1; position:relative; z-index:1; }

.overlay{ position:fixed; inset:0; background:rgba(0,0,0,.4); z-index:35; display:none; }
.overlay.show{ display:block; }

/* ------------------------------ sidebar -------------------------------- */
.sidebar-brand{ display:flex; align-items:center; gap:10px; padding:18px 22px 14px; }
.sidebar-brand img.logo-mark{ width:36px; height:36px; border-radius:10px; object-fit:contain; flex-shrink:0; }
.sidebar-brand img.logo-full{ height:34px; width:auto; display:block; }
.sidebar.collapsed .sidebar-brand{ padding:18px 0 14px; justify-content:center; }
.sidebar.collapsed .sidebar-brand img.logo-full{ display:none; }
.sidebar:not(.collapsed) .sidebar-brand img.logo-mark{ display:none; }
html[data-bs-theme="dark"] .sidebar-brand img.logo-full-light{ display:none; }
html:not([data-bs-theme="dark"]) .sidebar-brand img.logo-full-dark{ display:none; }

.sidebar-scroll{ flex:1; overflow-y:auto; padding:6px 14px 20px; }
.nav-heading{
  font-size:11px; font-weight:700; letter-spacing:.09em; color:var(--muted-2);
  text-transform:uppercase; padding:16px 12px 8px;
}
.sidebar.collapsed .nav-heading{ display:none; }

.nav-item{
  display:flex; align-items:center; gap:12px; width:100%;
  padding:9px 12px; margin:2px 0; border-radius:8px; border:none; background:transparent;
  color:var(--muted); font-size:14px; font-weight:500; text-align:left;
  cursor:pointer; position:relative; transition:background .15s ease,color .15s ease;
}
.nav-item svg{ width:18px; height:18px; flex-shrink:0; }
.nav-item:hover{ background:var(--surface-2); color:var(--text); }
.nav-item.active{ background:var(--primary-100); color:var(--primary-text); font-weight:700; }
.sidebar.dark .nav-item{ color:rgba(255,255,255,.82); }
.sidebar.dark .nav-item:hover{ background:rgba(255,255,255,.08); color:#fff; }
.sidebar.dark .nav-item.active{ background:rgba(255,255,255,.14); color:#fff; }
.sidebar.collapsed .nav-item{ justify-content:center; }
.sidebar.collapsed .nav-item span.label,
.sidebar.collapsed .nav-item .nav-badge,
.sidebar.collapsed .nav-group-btn svg.chev{ display:none; }
.sidebar.collapsed .nav-children{ display:none !important; }

.nav-badge{
  margin-left:auto; background:var(--danger); color:#fff; font-size:10.5px;
  font-weight:700; padding:1px 7px; border-radius:20px;
}
.nav-children{
  margin-left:14px; padding-left:14px; border-left:1px solid var(--border);
  display:flex; flex-direction:column; gap:2px; margin-bottom:6px;
}
.sidebar.dark .nav-children{ border-left-color:rgba(255,255,255,.14); }
.nav-children .nav-item{ padding:7px 10px; font-size:12.8px; }

.nav-group-btn svg.chev{ width:14px; height:14px; margin-left:auto; flex-shrink:0; }
.nav-new-btn{
  display:flex; align-items:center; gap:8px; width:100%; margin-top:2px;
  padding:7px 14px; border-radius:999px; border:1px dashed var(--primary-text); background:transparent;
  color:var(--primary-text); font-size:12.2px; font-weight:700; cursor:pointer;
}
.nav-new-btn svg{ width:12px; height:12px; flex-shrink:0; }
.sidebar.dark .nav-new-btn{ border-color:rgba(255,255,255,.4); color:rgba(255,255,255,.9); }
.nav-new-btn.active{ background:var(--primary); border-style:solid; border-color:var(--primary); color:#fff; }
.sidebar.dark .nav-new-btn.active{ background:rgba(255,255,255,.16); border-color:transparent; color:#fff; }

/* "More below" cue — same bouncing-chevron idea as the homepage hero's own
   scroll-down indicator (.hero-ed-scroll), just shorter travel to suit a
   narrow sidebar. JS (initSidebar in scripts.js) toggles .show only while
   .sidebar-scroll actually has content scrolled out of view below, and
   clicking it scrolls the nav list down (see the same click handler). */
.sidebar-scroll-hint{
  display:flex; justify-content:center; padding:2px 0; color:var(--muted); flex-shrink:0;
  width:100%; border:none; background:transparent; font:inherit; cursor:pointer;
  opacity:0; pointer-events:none; transition:opacity .2s ease, color .15s ease;
}
.sidebar-scroll-hint:hover{ color:var(--text); }
.sidebar-scroll-hint svg{ width:15px; height:15px; }
.sidebar-scroll-hint.show{ opacity:1; pointer-events:auto; animation:sidebarScrollBounce 1.8s ease-in-out infinite; }
.sidebar.collapsed .sidebar-scroll-hint{ display:none; }
@keyframes sidebarScrollBounce{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(4px); } }
@media (prefers-reduced-motion:reduce){ .sidebar-scroll-hint.show{ animation:none; } }

.sidebar-footer{ padding:14px; }
.sidebar.collapsed .sidebar-footer{ display:none; }
.upgrade-card{
  background:linear-gradient(135deg,var(--primary),#001F4A);
  border-radius:14px; padding:16px; color:#fff;
}
.upgrade-card h4{ margin:0 0 4px; font-size:14px; color:#fff; }
.upgrade-card p{ margin:0 0 12px; font-size:12px; opacity:.85; line-height:1.4; }
.upgrade-card button{
  background:#fff; color:var(--primary-600); border:none; border-radius:999px;
  padding:7px 12px; font-size:12.5px; font-weight:700; cursor:pointer; width:100%;
}
.sidebar-footer-note{ padding:10px 12px 0; font-size:11px; color:var(--muted); }
.sidebar.dark .sidebar-footer-note{ color:rgba(255,255,255,.5); }
.sidebar.collapsed .sidebar-footer-note{ display:none; }

/* ------------------------------- topbar -------------------------------- */
.search-box{
  flex:1; max-width:420px; display:flex; align-items:center; gap:10px;
  background:var(--surface-2); border:1px solid var(--border); border-radius:999px;
  padding:9px 16px; color:var(--muted); position:relative;
}
.search-box input[type=text]{
  border:none; background:transparent; outline:none; flex:1; padding:0;
  font-size:13.5px; color:var(--text); border-radius:0;
}
.search-box input[type=text]:focus{ border:none; background:transparent; }
.search-box svg{ width:16px; height:16px; flex-shrink:0; display:block; }
/* .icon-btn (defined later, under topbar-right) also sets display:flex —
   needs matching .icon-btn.search-mobile-btn specificity here to actually
   win, a plain .search-mobile-btn{display:none} would otherwise lose that
   tie-break regardless of viewport width. */
.icon-btn.search-mobile-btn{ display:none; }
@media(max-width:820px){ .icon-btn.search-mobile-btn{ display:flex; } }
.search-modal{ max-width:520px; }
.search-modal .search-box{ max-width:none; }

/* Dashboard topbar's own round-logo mark — only relevant on mobile, where
   the sidebar (which normally carries the brand) is off-canvas and hidden. */
.topbar-brand{ display:none; align-items:center; }
.topbar-brand img.logo-mark{ width:32px; height:32px; border-radius:8px; object-fit:contain; }
html[data-bs-theme="dark"] .topbar-brand img.logo-mark-light{ display:none; }
html:not([data-bs-theme="dark"]) .topbar-brand img.logo-mark-dark{ display:none; }
@media(max-width:820px){ .topbar-brand{ display:flex; } }

.topbar-right{ display:flex; align-items:center; gap:8px; margin-left:auto; }
@media(max-width:820px){
  /* Sidebar-toggle hamburger moves from the far left to the far right on
     mobile, past the whole topbar-right cluster — search now lives inside
     .topbar-right itself (see header.html), right before the theme toggle,
     so the final left-to-right order is:
     logo → [search, theme, quick, notif, avatar] → hamburger. */
  .topbar-right{ order:2; }
  /* A little extra clearance from the true screen edge — .icon-btn's solid
     border reads as "flush against the edge" more than a rounded/padded
     pill would at the exact same container inset, so it gets a bit more
     room than .topbar's own gap alone gives it. */
  #sidebarToggleBtn{ order:3; margin-right:4px; }
}
.icon-btn{
  width:40px; height:40px; border-radius:50%; border:1px solid var(--border); background:var(--surface);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--muted); position:relative;
  transition:background .15s ease,color .15s ease; flex-shrink:0;
}
.icon-btn:hover{ background:var(--surface-2); color:var(--text); }
.icon-btn svg{ width:18px; height:18px; }
.dot{
  position:absolute; top:8px; right:9px; width:7px; height:7px; border-radius:50%; background:var(--danger);
  border:1.5px solid var(--surface);
}
.user-chip{
  display:flex; align-items:center; gap:4px; padding:3px; border-radius:999px;
  border:1px solid var(--border); cursor:pointer; background:var(--surface); color:var(--text);
}
.avatar{
  width:32px; height:32px; border-radius:50%; background:linear-gradient(135deg,var(--primary),#001F4A);
  display:flex; align-items:center; justify-content:center; color:#fff; font-weight:700; font-size:13px; flex-shrink:0;
}
.user-chip svg{ flex-shrink:0; margin-right:4px; }

.theme-toggle{ display:flex; align-items:center; gap:6px; background:var(--surface-2); border-radius:999px; padding:4px; border:1px solid var(--border); }
.theme-toggle button{
  border:none; background:none; width:32px; height:32px; border-radius:50%; display:flex; align-items:center;
  justify-content:center; cursor:pointer; color:var(--muted);
}
.theme-toggle button svg{ width:16px; height:16px; }
.theme-toggle button.active{ background:var(--surface); color:var(--primary-text); box-shadow:var(--shadow); }

/* .theme-toggle.compact (see compact_theme_toggle in _topbar_actions.html)
   is the same component used by the public header — deliberately left the
   same 40px-tall size as the base .theme-toggle above at desktop width, to
   match the other 40px .icon-btn buttons (search/quick-actions/bell) beside
   it in that header. It only shrinks together with them below 640px — see
   the responsive section at the end of this file. Shrinking it on its own
   at all sizes (the old behavior) made it visibly smaller than its
   neighbors on desktop, while happening to match them on mobile only by
   coincidence. */

/* ---------------------------- public site header ------------------------ */
/* Rendered by main/base.html's {% block header %} — shared by every public
   page (home, public webforms, login, error pages), not just the home page. */

/* Secondary utility bar above the main navbar, cloned from news.pes.edu's
   cross-site strip — same real PES destinations regardless of which PES
   sub-site you're on. */
.public-utility-bar{ background:var(--primary-600); color:#fff; }
.public-utility-inner{
  width:100%; max-width:1180px; margin:0 auto; padding:6px 24px; display:flex; align-items:center; justify-content:space-between; gap:16px;
  font-size:12px;
}
.utility-back{ display:inline-flex; align-items:center; gap:4px; color:rgba(255,255,255,.85); font-weight:600; white-space:nowrap; }
.utility-back:hover{ color:#fff; }
.utility-back svg{ width:13px; height:13px; }
.utility-links{ display:flex; align-items:center; gap:16px; }
.utility-link{ display:inline-flex; align-items:center; gap:5px; color:rgba(255,255,255,.85); font-weight:600; white-space:nowrap; }
.utility-link:hover{ color:#fff; }
.utility-link svg{ width:13px; height:13px; }
.utility-divider{ width:1px; height:14px; background:rgba(255,255,255,.25); }
.utility-admissions-btn{
  display:inline-flex; align-items:center; gap:5px; padding:5px 14px; border-radius:999px;
  background:var(--warning); color:#fff; font-weight:700; white-space:nowrap; transition:filter .15s ease; font-size:11.5px;
}
.utility-admissions-btn:hover{ filter:brightness(1.06); }
.utility-admissions-btn svg{ width:13px; height:13px; }

/* Full-width mega dropdown (per-campus quick links), cloned from
   news.pes.edu's "Browse by X" panels. Reparented to <body> and positioned
   with JS (see initMegaMenus in scripts.js) — same reasoning as the modal
   reparenting in initModals(): no ancestor's position/transform can then
   trap it away from spanning the full viewport width. */
.mega-panel{
  position:fixed; left:0; right:0; z-index:300; background:var(--surface); border-bottom:1px solid var(--border);
  box-shadow:var(--shadow-lg); display:none; max-height:80vh; overflow-y:auto;
}
.mega-panel.show{ display:block; animation:megaPanelIn .22s cubic-bezier(.22,.85,.32,1); }
@keyframes megaPanelIn{ from{ opacity:0; transform:translateY(-10px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){ .mega-panel.show{ animation:none; } }
/* Background spans the full width (like the header above it); the actual
   content lines up with the header/page content's own max-width instead of
   stretching edge-to-edge. */
.mega-panel-inner{
  max-width:1180px; margin:0 auto; display:grid; grid-template-columns:270px 1fr; min-height:230px;
}
/* Left: the campus's own photo (or its brand color as a flat fallback)
   with its name overlaid — right: the quick-links grid. */
.mega-panel-visual{
  position:relative; padding:24px; display:flex; flex-direction:column; justify-content:flex-end;
  /* This panel has no single campus to photograph (it's the "choose a
     campus" overview, not one campus's own page) — the same navy gradient
     used by the other brand-level decorative panels (hero bands, auth
     screens) stands in as its background instead of a photo. */
  background:linear-gradient(150deg, #061428 0%, var(--primary-600) 55%, var(--primary) 100%);
  background-size:cover; background-position:center; color:#fff; overflow:hidden;
}
.mega-panel-visual::before{
  content:""; position:absolute; inset:0; background:linear-gradient(200deg, rgba(0,0,0,0) 30%, rgba(0,0,0,.68) 100%);
}
.mega-panel-visual-icon{
  position:relative; z-index:1; width:40px; height:40px; border-radius:11px; margin-bottom:auto;
  background:rgba(255,255,255,.2); backdrop-filter:blur(4px); display:flex; align-items:center; justify-content:center;
}
.mega-panel-visual-icon svg{ width:18px; height:18px; }
.mega-panel-visual-title{ position:relative; z-index:1; font-size:20px; font-weight:800; margin:0 0 4px; color:#fff; }
.mega-panel-visual-sub{ position:relative; z-index:1; font-size:12px; color:rgba(255,255,255,.82); }

.mega-panel-links{ padding:26px 0 30px 32px; }
.mega-panel-links-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:18px; }
.mega-panel-eyebrow{ font-size:11px; font-weight:800; letter-spacing:.8px; text-transform:uppercase; color:var(--muted); }
.mega-panel-close{
  width:32px; height:32px; border-radius:50%; border:1px solid var(--border); background:var(--surface);
  display:flex; align-items:center; justify-content:center; color:var(--muted); cursor:pointer; flex-shrink:0;
  transition:background .15s ease, color .15s ease, transform .15s ease;
}
.mega-panel-close:hover{ background:var(--surface-2); color:var(--text); transform:rotate(90deg); }
.mega-panel-close svg{ width:15px; height:15px; }
.mega-panel-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(200px,1fr)); gap:14px; padding-right:32px; }
.mega-item{
  display:flex; align-items:center; gap:12px; padding:16px 18px; border-radius:14px; border:1px solid var(--border);
  background:var(--surface); transition:border-color .2s ease, background .2s ease, transform .15s ease, box-shadow .2s ease;
}
.mega-item:hover{ border-color:var(--accent, var(--primary)); background:var(--surface-2); transform:translateY(-2px); box-shadow:var(--shadow); }
.mega-item.active{ border-color:var(--accent, var(--primary)); background:var(--accent-100, var(--primary-100)); }
.mega-item-icon{
  width:40px; height:40px; border-radius:11px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  background:var(--accent-100, var(--primary-100)); color:var(--accent, var(--primary-text));
}
.mega-item-icon svg{ width:18px; height:18px; }
.mega-item-body{ min-width:0; display:flex; flex-direction:column; gap:2px; }
.mega-item-title{ font-size:13.5px; font-weight:700; color:var(--text); }
.mega-item-sub{ font-size:11.5px; color:var(--muted); }
@media(max-width:760px){
  .mega-panel-inner{ grid-template-columns:1fr; }
  .mega-panel-visual{ padding:20px 24px; min-height:110px; }
  .mega-panel-links{ padding:22px 24px 26px; }
  .mega-panel-grid{ padding-right:0; }
}

/* Full-screen search overlay toggled by #publicSearchToggleBtn (see
   initPublicSearchToggle in scripts.js) — cloned from news.pes.edu's dark
   glassmorphic command-palette search: this panel is deliberately always
   dark-navy-glass regardless of the site's own light/dark toggle, same as
   the hero section. Adapted with a "quick filters" row of campuses instead
   of their tag/press-note filters (no tag taxonomy in this app). */
.search-overlay{ position:fixed; inset:0; z-index:500; display:none; }
.search-overlay.show{ display:block; }
.search-overlay-backdrop{ position:absolute; inset:0; background:rgba(4,12,28,.65); backdrop-filter:blur(8px); }
body.search-overlay-open{ overflow:hidden; }
/* No card/box around this — just the field and the filter chips floating
   directly over the dimmed, blurred backdrop. */
.search-overlay-panel{
  position:relative; z-index:1; max-width:640px; margin:80px auto 0; padding:0 28px;
  animation:searchOverlayIn .22s cubic-bezier(.22,.85,.32,1);
}
@keyframes searchOverlayIn{ from{ opacity:0; transform:translateY(-16px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){ .search-overlay-panel{ animation:none; } }
.search-overlay-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; }
.search-overlay-brand{ display:flex; align-items:center; gap:8px; font-size:12.5px; font-weight:800; letter-spacing:.4px; text-transform:uppercase; color:#ffb648; }
.search-overlay-brand svg{ width:16px; height:16px; }
.search-overlay-close{
  display:flex; align-items:center; gap:8px; border:1px solid rgba(255,255,255,.16); background:rgba(255,255,255,.08);
  border-radius:8px; padding:6px 10px; cursor:pointer; color:rgba(255,255,255,.7);
}
.search-overlay-close:hover{ color:#fff; background:rgba(255,255,255,.14); }
.search-overlay-close svg{ width:14px; height:14px; }
.kbd{
  font-size:10.5px; font-weight:700; padding:2px 6px; border-radius:5px;
  background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.18); color:rgba(255,255,255,.75);
}

.search-overlay-form{
  display:flex; align-items:center; gap:10px; background:#fff; border-radius:14px; overflow:hidden;
  padding:6px 6px 6px 16px; margin-bottom:22px; box-shadow:0 8px 20px rgba(0,0,0,.25);
}
.search-overlay-form svg{ width:18px; height:18px; color:var(--muted); flex-shrink:0; }
/* input[type=search] renders its own native rounded box + border in some
   browsers unless fully reset — left as-is it visually splits into two
   boxes (a small bordered one nested inside the pill), pushing the search
   icon out into the gap to its left. */
.search-overlay-form input{
  flex:1; min-width:0; border:none; outline:none; background:transparent; -webkit-appearance:none; appearance:none;
  box-shadow:none; font-size:15px; font-family:inherit; color:#1b1e2b; padding:8px 0;
}
.search-overlay-form input::-webkit-search-decoration,
.search-overlay-form input::-webkit-search-cancel-button,
.search-overlay-form input::-webkit-search-results-button,
.search-overlay-form input::-webkit-search-results-decoration{
  -webkit-appearance:none;
}
.search-overlay-submit{
  width:38px; height:38px; border-radius:10px; border:none; background:var(--primary); color:#fff;
  display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0; transition:background .15s ease;
}
.search-overlay-submit:hover{ background:var(--primary-600); }
.search-overlay-submit svg{ width:16px; height:16px; }

.search-overlay-section-label{
  display:flex; align-items:center; gap:6px; font-size:11px; font-weight:800; letter-spacing:.6px; text-transform:uppercase;
  color:rgba(255,255,255,.55); margin-bottom:12px;
}
.search-overlay-section-label svg{ width:13px; height:13px; color:#ffb648; }
.search-overlay-filters{ display:flex; flex-wrap:wrap; gap:8px; }
.search-overlay-filter{
  display:inline-flex; align-items:center; gap:7px; padding:8px 14px; border-radius:999px;
  border:1px solid rgba(255,255,255,.14); background:rgba(255,255,255,.07);
  font-size:12.5px; font-weight:700; color:rgba(255,255,255,.85); transition:border-color .15s ease, background .15s ease;
}
.search-overlay-filter:hover{ border-color:rgba(255,255,255,.35); background:rgba(255,255,255,.14); }
.search-overlay-filter .dotc{ width:7px; height:7px; border-radius:50%; flex-shrink:0; }
.search-overlay-hint{ text-align:center; font-size:11.5px; color:rgba(255,255,255,.55); margin-top:20px; }
.search-overlay-hint .kbd{ margin:0 2px; }
@media(max-width:640px){
  .search-overlay-panel{ margin:16px 12px 0; padding:0 8px; }
}

.public-header{ position:sticky; top:0; z-index:40; background:var(--surface); border-bottom:1px solid var(--border); width:100%; }
.public-header-inner{
  width:100%; max-width:1180px; margin:0 auto; padding:10px 24px;
  display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:18px;
}
.public-nav-toggle{ display:none; }
.public-brand{ display:flex; align-items:center; gap:10px; }
.public-brand img.logo-full{ height:48px; width:auto; display:block; }
html[data-bs-theme="dark"] .public-brand img.logo-full-light{ display:none; }
html:not([data-bs-theme="dark"]) .public-brand img.logo-full-dark{ display:none; }
/* Round mark shown instead of the wordmark below 980px (see responsive
   section at the end of this file) — hidden on desktop by default. */
.public-brand img.logo-mark{ width:38px; height:38px; border-radius:10px; object-fit:contain; display:none; }
html[data-bs-theme="dark"] .public-brand img.logo-mark-light{ display:none; }
html:not([data-bs-theme="dark"]) .public-brand img.logo-mark-dark{ display:none; }

.public-nav{ display:flex; align-items:center; justify-content:center; gap:2px; flex-wrap:wrap; }
.public-nav-brand{ display:none; }

.drawer-close-btn{
  display:none; width:32px; height:32px; border-radius:50%; flex-shrink:0;
  border:1px solid var(--border); background:var(--surface-2); color:var(--muted);
  align-items:center; justify-content:center; cursor:pointer;
}
.drawer-close-btn:hover{ background:var(--surface); color:var(--text); }
.drawer-close-btn svg{ width:15px; height:15px; }
.public-nav-link{
  position:relative; display:flex; align-items:center; gap:5px; padding:8px 16px; border-radius:9px; border:none; background:transparent;
  font-size:13.3px; font-weight:600; color:var(--text); cursor:pointer; white-space:nowrap; font-family:inherit;
}
.public-nav-link:hover{ background:var(--surface-2); color:var(--primary-text); }
/* Same gradient-blue pill already used for the sidebar's active campus item
   (.campus-nav-item.active) — a blended navy rather than one flat solid
   color, reused here instead of inventing a third look. */
.public-nav-link.active,
.public-nav-link.active:hover{
  background:linear-gradient(135deg, var(--primary), var(--primary-600)); color:#fff; font-weight:700;
  box-shadow:0 6px 16px color-mix(in srgb, var(--primary) 35%, transparent);
}
.public-nav-link svg.chev{ width:12px; height:12px; opacity:.6; transition:transform .2s ease; }
.public-nav-link.active svg.chev{ transform:rotate(180deg); opacity:1; }
.public-header-right{ display:flex; align-items:center; gap:10px; justify-self:end; }

/* Below 980px the horizontal nav becomes a left-hand off-canvas drawer,
   toggled by #publicNavToggleBtn (see initPublicNav in scripts.js) — mirrors
   the dashboard sidebar's own off-canvas pattern so the two navbars behave
   the same way on mobile instead of the public one just disappearing. */
@media (max-width:980px){
  /* Hamburger reads as "the" menu control, so it belongs at the true right
     edge of the header — last in visual order, past search and the theme
     toggle — rather than first in .public-header-right's DOM order. */
  .public-nav-toggle{ display:flex; order:3; }
  .public-brand img.logo-full{ display:none; }
  .public-brand img.logo-mark{ display:block; }
  .public-nav{
    position:fixed; top:0; left:0; bottom:0; z-index:250; width:280px; max-width:82vw;
    background:var(--surface); flex-direction:column; align-items:stretch; gap:2px;
    padding:20px 14px 20px; overflow-y:auto; box-shadow:var(--shadow-lg);
    transform:translateX(-100%); transition:transform .25s ease;
  }
  .public-nav.open{ transform:translateX(0); }
  .public-nav-brand{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
    padding:0 12px 18px; margin-bottom:10px; border-bottom:1px solid var(--border);
  }
  .public-nav-brand img.logo-full{ height:30px; width:auto; display:block; }
  html[data-bs-theme="dark"] .public-nav-brand img.logo-full-light{ display:none; }
  html:not([data-bs-theme="dark"]) .public-nav-brand img.logo-full-dark{ display:none; }
  .public-nav-brand .drawer-close-btn{ display:flex; }
  /* Every top-level drawer item reads as its own card by default (not just
     on hover/active) — a flat list of plain text rows didn't read as
     clearly tappable as a stack of distinct rounded rows does. Sized to
     match the campus rows below (padding swapped with .mega-item further
     down) so the top-level items read as the "main" rows and the campus
     list reads as the smaller, secondary one — not the other way around. */
  .public-nav-link{ width:100%; justify-content:space-between; background:var(--surface-2); margin-bottom:4px; padding:16px; font-size:14px; }
  .public-nav-link:hover{ background:var(--surface-2); }
  .public-nav .menu-wrap{ width:100%; }

  /* Campus mega-panel is always expanded in the drawer — not a click-to-open
     accordion — since "Campus" is core enough navigation here that it isn't
     worth an extra tap to reveal on mobile. display:block unconditionally
     (not gated on .show) means initMegaMenus()'s click handler becomes a
     harmless no-op for it on mobile; the chevron is force-rotated to match
     "always open" instead of reflecting that now-unused toggle state. */
  .public-nav .mega-panel{
    position:static; display:block; max-height:none; overflow:visible;
    background:transparent; border-bottom:none; box-shadow:none; z-index:auto;
  }
  .public-nav .mega-panel-inner{ display:block; max-width:none; min-height:0; }
  .public-nav .mega-panel-visual{ display:none; }
  .public-nav .mega-panel-links{ padding:4px 0 10px; }
  .public-nav .mega-panel-links-head{ display:none; }
  .public-nav .mega-panel-grid{ display:flex; flex-direction:column; gap:6px; padding-right:0; }
  .public-nav [data-mega-toggle] svg.chev{ transform:rotate(180deg); opacity:1; }
  .public-nav .mega-item{
    padding:8px 12px; border-radius:12px; border:none; background:var(--primary-100);
    transition:background .15s ease;
  }
  .public-nav .mega-item:hover{
    background:color-mix(in srgb, var(--primary) 16%, var(--primary-100)); transform:none; box-shadow:none;
  }
  /* .mega-item.active (the currently-viewed campus) has the same specificity
     as the plain .public-nav .mega-item rule above and would otherwise lose
     to it just by coming first in the file — this restores the distinct
     per-campus accent highlight the desktop dropdown already shows. */
  .public-nav .mega-item.active{
    background:var(--accent-100, var(--primary-100)); border:1.5px solid var(--accent, var(--primary));
  }
  .public-nav .mega-item.active .mega-item-icon{ color:var(--accent, var(--primary)); }
  .public-nav .mega-item.active .mega-item-title{ color:var(--accent, var(--primary)); }
  .public-nav .mega-item-icon{ width:22px; height:22px; border-radius:7px; background:transparent; color:var(--primary-text); }
  .public-nav .mega-item-icon svg{ width:13px; height:13px; }
  .public-nav .mega-item-title{ font-size:13px; }
  .public-nav .mega-item-sub{ display:none; }
}
@media (prefers-reduced-motion:reduce){ .public-nav{ transition:none; } }

/* --------------------------- dropdown menus ---------------------------- */
.menu-wrap{ position:relative; }
.menu-panel{
  position:absolute; top:calc(100% + 8px); right:0; width:280px; background:var(--surface);
  border:1px solid var(--border); border-radius:14px; box-shadow:0 20px 45px rgba(0,20,45,.16);
  z-index:41; display:none; overflow:hidden;
}
.menu-panel.left{ right:auto; left:0; }
.menu-panel.show{ display:block; }
.menu-panel-head{ display:flex; align-items:center; justify-content:space-between; padding:12px 16px; border-bottom:1px solid var(--border); }
.menu-panel-head h4{ margin:0; font-size:14.5px; font-weight:800; color:var(--primary-text); }
.menu-panel-head-actions{ display:flex; align-items:center; gap:10px; }
#notificationsMenu{ width:340px; }
.menu-item{
  width:100%; display:flex; align-items:center; gap:10px; border:none; background:transparent;
  border-radius:9px; padding:9px 10px; cursor:pointer; color:var(--text); font-size:13px; font-weight:600; text-align:left;
}
.menu-item:hover{ background:var(--surface-2); }
.menu-item.danger{ color:var(--danger); }
.menu-item svg{ width:15px; height:15px; flex-shrink:0; }
.menu-body{ padding:8px; }
.menu-divider{ height:1px; background:var(--border); margin:6px 4px; }

/* Profile summary shown at the top of the account dropdown (#profileMenu) —
   the topbar chip itself hides the name/email on mobile to save space, so
   this repeats avatar + name + username inside the dropdown on every size. */
.menu-profile-head{ display:flex; align-items:center; gap:12px; padding:16px; border-bottom:1px solid var(--border); }
.menu-profile-head .avatar{ width:42px; height:42px; border-radius:50%; font-size:15px; }
.menu-profile-head-text{ min-width:0; }
.menu-profile-head-text .name{ font-size:14px; font-weight:700; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.menu-profile-head-text .username{ font-size:12px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.notif-row{
  display:flex; align-items:flex-start; gap:14px; padding:16px 20px; cursor:pointer;
  border-top:1px solid var(--border); position:relative; transition:background .15s ease;
  animation:notifRowIn .3s ease both;
}
@keyframes notifRowIn{ from{ opacity:0; transform:translateX(6px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){ .notif-row{ animation:none; } }
.notif-row:first-child{ border-top:none; }
.notif-row:hover{ background:var(--surface-2); }
.notif-row.unread{ background:color-mix(in srgb, var(--warning) 8%, var(--surface)); }
.notif-row.unread:hover{ background:color-mix(in srgb, var(--warning) 14%, var(--surface)); }
.notif-row.unread::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:linear-gradient(180deg, var(--warning), color-mix(in srgb, var(--warning) 60%, var(--danger)));
}
.notif-icon{
  width:36px; height:36px; border-radius:50%; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  background:var(--surface-2); color:var(--muted); transition:background .15s ease, color .15s ease;
}
.notif-icon svg{ width:16px; height:16px; }
.notif-row.unread .notif-icon{ background:var(--warning-100); color:var(--warning); }
.notif-body{ flex:1; min-width:0; }
.notif-msg{ font-size:12.8px; font-weight:600; color:var(--text); line-height:1.4; }
.notif-row.unread .notif-msg{ font-weight:800; }
.notif-desc{ font-size:12px; color:var(--muted); line-height:1.55; margin-top:4px; }
.notif-time{ font-size:11px; color:var(--muted-2); margin-top:5px; display:flex; align-items:center; gap:7px; flex-wrap:wrap; }
.notif-unread-pill{
  flex-shrink:0; font-size:9px; font-weight:800; text-transform:uppercase; letter-spacing:.5px;
  background:var(--warning); color:#fff; padding:2px 7px; border-radius:20px; line-height:1.5;
}
.notif-read-tick{ flex-shrink:0; display:inline-flex; color:var(--success); opacity:.65; }
.notif-read-tick svg{ width:12px; height:12px; }
.notif-group-head{
  display:flex; align-items:center; gap:8px; font-size:11.5px; font-weight:800; text-transform:uppercase;
  letter-spacing:.5px; color:var(--muted); margin-bottom:12px;
}
.notif-group-count{
  background:var(--surface-2); border:1px solid var(--border); color:var(--muted);
  font-size:10.5px; font-weight:800; padding:1px 8px; border-radius:20px; letter-spacing:0; text-transform:none;
}
/* Full notifications page only — wraps the click-to-read button plus a
   separate row-actions column (Edit/Delete), which can't nest inside the
   button itself since a <button> can't contain other <button> elements. */
.notif-row-outer{ display:flex; align-items:stretch; }
.notif-row-outer.bordered{ border-top:1px solid var(--border); }
.notif-row-outer.unread{ background:color-mix(in srgb, var(--warning) 8%, var(--surface)); }
.notif-row-actions{ display:flex; align-items:center; gap:6px; padding-right:16px; flex-shrink:0; }
.menu-footer-btn{
  width:100%; border:none; border-top:none; background:transparent; color:var(--primary-text);
  font-weight:700; font-size:12.5px; padding:12px 0 10px; cursor:pointer; text-align:center; display:block;
}
.menu-footer-btn:hover{ text-decoration:underline; }

/* -------------------------------- pages -------------------------------- */
.page-head{
  position:relative;
  display:flex; align-items:center; justify-content:space-between; gap:20px; flex-wrap:wrap;
  margin-bottom:32px; padding:2px 0 26px 22px; border-bottom:1px solid var(--border);
  animation:pageHeadIn .45s cubic-bezier(.22,.85,.32,1) both;
}
.page-head::before{
  content:""; position:absolute; left:0; top:2px; bottom:26px; width:4px; border-radius:4px;
  background:linear-gradient(180deg, var(--primary-text) 0%, var(--info) 100%);
}
@keyframes pageHeadIn{ from{ opacity:0; transform:translateY(-8px); } to{ opacity:1; transform:translateY(0); } }
@media (prefers-reduced-motion:reduce){ .page-head{ animation:none; } }
.breadcrumb{
  font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  color:var(--muted-2); margin-bottom:9px;
}
.breadcrumb b{ color:var(--primary-text); font-weight:800; }
.breadcrumb a:hover{ text-decoration:underline; }
.page-head h1{
  font-family:'Sora', sans-serif; font-size:28px; font-weight:800; margin:0 0 7px; letter-spacing:-.02em;
  color:var(--text);
}
@supports ((background-clip:text) or (-webkit-background-clip:text)){
  .page-head h1{
    background:linear-gradient(120deg, var(--text) 40%, var(--primary-text) 150%);
    -webkit-background-clip:text; background-clip:text; color:transparent;
  }
}
.page-head .sub{ color:var(--muted); font-size:13.5px; }

/* Title + trailing actions header (webform builder, error log detail) — no
   background/card at all, just generous spacing and a thin colored accent
   bar echoing .page-head's own left bar, so it reads as part of the same
   design language without literally duplicating the panel treatment. The
   text block always keeps flex:1/min-width:0 so it's the one that shrinks
   first; trailing actions are grouped in .detail-header-actions so they
   wrap/stack as a unit instead of each button/badge wrapping independently. */
.detail-header{
  position:relative;
  display:flex; align-items:center; gap:24px; flex-wrap:wrap;
  margin-bottom:32px; padding:2px 0 26px 22px; border-bottom:1px solid var(--border);
  animation:detailHeaderIn .45s cubic-bezier(.22,.85,.32,1) both;
}
.detail-header::before{
  content:""; position:absolute; left:0; top:2px; bottom:26px; width:4px; border-radius:4px;
  background:linear-gradient(180deg, var(--primary-text) 0%, var(--info) 100%);
}
.detail-header.danger::before{ background:linear-gradient(180deg, var(--danger) 0%, var(--warning) 100%); }
@keyframes detailHeaderIn{ from{ opacity:0; transform:translateY(-8px); } to{ opacity:1; transform:translateY(0); } }
@media (prefers-reduced-motion:reduce){ .detail-header{ animation:none; } }
.detail-header > div:first-child{ flex:1; min-width:0; }
.detail-header-actions{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; flex-shrink:0; }

.detail-header .breadcrumb{ margin-bottom:9px; }

.detail-header-title-row{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-bottom:7px; }
.detail-header h1{
  font-family:'Sora', sans-serif; font-size:28px; font-weight:800; letter-spacing:-.02em; margin:0;
  color:var(--text);
}
@supports ((background-clip:text) or (-webkit-background-clip:text)){
  .detail-header h1{
    background:linear-gradient(120deg, var(--text) 35%, var(--primary-text) 160%);
    -webkit-background-clip:text; background-clip:text; color:transparent;
  }
}
.detail-header .text-sub{ color:var(--muted); font-size:13.5px; }

/* -------------------------------- buttons ------------------------------- */
.btn{
  display:inline-flex; align-items:center; gap:8px; border-radius:999px; border:1px solid transparent;
  padding:10px 18px; font-size:13.5px; font-weight:600; cursor:pointer; transition:all .15s ease;
}
.btn svg{ width:15px; height:15px; }
.btn-primary{ background:var(--primary); color:#fff; }
.btn-primary:hover{ background:var(--primary-600); }
.btn-accent{ background:var(--warning); color:#fff; box-shadow:0 6px 16px rgba(255,149,51,.28); }
.btn-accent:hover{ filter:brightness(0.95); }
/* Public webform submit button (and its builder live-preview mirror) — a
   distinct look from .btn-accent, which is used for admin-side actions
   (Export, Publish) elsewhere. */
.btn-form-submit{ background:linear-gradient(135deg, var(--primary), #001F4A); color:#fff; box-shadow:0 6px 16px color-mix(in srgb, var(--primary) 35%, transparent); }
.btn-form-submit:hover{ filter:brightness(1.08); }
.btn-outline{ background:var(--surface); border-color:var(--border); color:var(--text); }
.btn-outline:hover{ background:var(--surface-2); }
.btn-ghost{ background:transparent; color:var(--muted); border-color:transparent; }
.btn-ghost:hover{ color:var(--text); background:var(--surface-2); }
.btn-sm{ padding:7px 14px; font-size:12.5px; }
.btn-danger{ background:#fff; color:var(--danger); border:1px solid var(--danger-100); }
.btn-danger:hover{ background:var(--danger-100); }
.btn-danger-soft{ background:var(--danger-100); color:var(--danger); }
.btn:disabled{ opacity:.5; cursor:not-allowed; }
.btn-block{ width:100%; justify-content:center; }

/* --------------------------------- cards -------------------------------- */
.card{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
  box-shadow:var(--shadow);
}
.card-head{ padding:24px 28px; border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; }
.card-head h3{ margin:0; font-size:15.5px; }
.card-head .sub{ font-size:12px; color:var(--muted); margin-top:4px; }
.card-body{ padding:30px; transition:opacity .15s ease; }
/* Consistent padding for the templates' one-off "bare" cards (help center,
   webform builder detail boxes, change-password, error-log detail, FAQ
   manager panes, etc.) that don't use the .card-head/.card-body structure —
   previously each had its own ad-hoc inline padding (16/18/20/22/26px), so
   cards looked inconsistent from page to page. */
.compact-card{ padding:30px; }

.grid-stats{ display:grid; grid-template-columns:repeat(4,1fr); gap:18px; margin-bottom:24px; }
.settings-grid-stats-3{ grid-template-columns:repeat(3,1fr); }
.stat-card{
  display:flex; align-items:center; flex-wrap:wrap; gap:14px;
  padding:28px; position:relative; overflow:hidden; isolation:isolate;
  transition:transform .2s ease, box-shadow .2s ease;
}
.stat-card:hover{ transform:translateY(-4px); box-shadow:var(--shadow-lg); }
.stat-card::after{
  content:""; position:absolute; top:-34px; right:-34px; width:120px; height:120px; border-radius:50%; z-index:0;
  background:var(--stat-accent-100, var(--info-100)); opacity:.55;
}
.stat-card.brand{
  background:linear-gradient(135deg, var(--primary) 0%, var(--primary-600) 100%); border-color:var(--primary);
}
.stat-card.brand::after{ background:rgba(255,255,255,.1); }
.stat-top{ display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; position:relative; z-index:1; }
.stat-icon{
  width:44px; height:44px; border-radius:12px; display:flex; align-items:center; justify-content:center; flex-shrink:0;
  position:relative; z-index:1; order:1;
  background:var(--stat-accent-100, var(--info-100)); color:var(--stat-accent, var(--primary));
}
.stat-card.brand .stat-icon{ background:rgba(255,255,255,.18); color:#fff; }
.stat-icon svg{ width:21px; height:21px; }
.stat-change{ font-size:12px; font-weight:700; padding:3px 8px; border-radius:20px; display:flex; align-items:center; gap:3px; }
.stat-change.up{ color:var(--success); background:var(--success-100); }
.stat-change.down{ color:var(--danger); background:var(--danger-100); }
/* Icon+label cluster on the left (order 1/2), the value pushed to the far
   right via margin-left:auto (order 3) and printed noticeably bigger —
   was a plain vertical stack (icon, label, value top-to-bottom on the
   left) before. */
.stat-label{ color:var(--muted); font-size:12.5px; font-weight:600; position:relative; z-index:1; order:2; }
.stat-value{
  font-size:36px; font-weight:800; font-family:'Sora', sans-serif; letter-spacing:-.02em;
  position:relative; z-index:1; order:3; margin-left:auto; text-align:right;
}
.stat-card.brand .stat-label{ color:rgba(255,255,255,.7); }
.stat-card.brand .stat-value{ color:#fff; }

.grid-2col{ display:grid; grid-template-columns:2fr 1fr; gap:18px; margin-bottom:22px; align-items:stretch; }
/* 12-column split (Submissions = col 4, trend chart = col 8) for the
   dashboard's top row — same idea as .grid-2col, just narrow-side-first. */
.grid-2col-4-8{ display:grid; grid-template-columns:1fr 2fr; gap:18px; margin-bottom:22px; align-items:stretch; }
/* Dashboard's "last 4" admin preview cards (see _admin_preview_sections in
   app/views.py) — col-4 each (3 per row) by default, stretched to a common
   row height regardless of how many rows each one actually has (a card
   with 0-1 entries shouldn't be visibly shorter than its full siblings).
   Exactly 4 visible sections is the one count that tiles awkwardly as
   3-then-1, so that specific case switches to a 2-column (2x2) layout via
   .cols-2; exactly 5 leaves the last row's 2nd card widened to fill the
   row instead of trailing off into empty space via .count-5. */
.admin-preview-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:18px; margin-bottom:22px; align-items:stretch; }
.admin-preview-grid.cols-2{ grid-template-columns:repeat(2,1fr); }
.admin-preview-grid.count-5 > :last-child{ grid-column:span 2; }
.admin-preview-grid .card{ display:flex; flex-direction:column; }
/* flex:1 (not also justify-content:center) — real rows stay pinned to the
   top like a normal list even when the card's been stretched taller than
   its content needs; only the truly-empty "Nothing yet" placeholder (the
   sole flex item when there are zero rows) gets auto margins to center
   itself in that leftover space, since centering a lone message reads
   better than it sitting flush against the top with a tall empty card
   below it. */
.admin-preview-grid .recent-list{ flex:1; }
.admin-preview-grid .recent-list .table-empty{ margin:auto 0; }

/* ------------------------- axis-based bar chart (SVG) -------------------- */
.barchart-svg{ width:100%; height:auto; display:block; overflow:visible; }
.barchart-svg .gridline{ stroke:var(--border); stroke-width:1; }
.barchart-svg .axis-label{ font-size:10.5px; fill:var(--muted-2); font-family:'Inter', sans-serif; }
.barchart-svg .bar-label{ font-size:10.5px; fill:var(--muted); font-family:'Inter', sans-serif; }
.barchart-svg .bar-link{ cursor:pointer; }
.barchart-svg .bar-rect{ fill:var(--primary); transition:opacity .15s ease; }
.barchart-svg .bar-link:hover .bar-rect{ opacity:.8; }
.barchart-svg .bar-link:hover .bar-label{ fill:var(--text); font-weight:700; }
.barchart-svg .bar-value{ font-size:10.5px; font-weight:700; fill:var(--text); font-family:'Inter', sans-serif; text-anchor:middle; }

/* ------------------------- multi-line trend chart (SVG) ------------------- */
.trend-chart-svg{ width:100%; height:auto; display:block; overflow:visible; margin-bottom:18px; }
.trend-chart-svg .trend-gridline{ stroke:var(--border); stroke-width:1; }
.trend-chart-svg .axis-label{ font-size:10.5px; fill:var(--muted-2); font-family:'Inter', sans-serif; }
/* Undrawn (dashoffset:1400, no animation) until JS (initChartScrollPlay in
   app/scripts.js) adds .in-view once the chart actually scrolls into the
   viewport — previously the draw animation fired the instant the page
   loaded, so if the chart wasn't on-screen yet you'd missed it entirely by
   the time you scrolled down to it. */
.trend-chart-svg .trend-line-path{
  fill:none; stroke-width:1.75; stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:1400; stroke-dashoffset:1400;
}
.trend-chart-svg.in-view .trend-line-path{ animation:trendLineDraw 5s ease forwards; }
.trend-chart-svg .trend-line-dot{ cursor:pointer; transition:r .15s ease; }
.trend-chart-svg .trend-line-dot:hover{ r:5.5; }
@keyframes trendLineDraw{ to{ stroke-dashoffset:0; } }
@media (prefers-reduced-motion:reduce){
  .trend-chart-svg .trend-line-path{ animation:none; stroke-dashoffset:0; }
}

/* Custom hover tooltip for .trend-line-dot (see initChartTooltips in
   app/scripts.js) — replaces the plain native SVG <title> tooltip, which
   can't be styled. Single reusable element, positioned with JS. */
.chart-tooltip{
  position:fixed; z-index:600; display:none; pointer-events:none;
  background:#0f1524; color:#fff; border-radius:10px; padding:9px 13px;
  box-shadow:0 12px 28px rgba(0,0,0,.35); font-size:12px; white-space:nowrap;
  transform:translate(-50%,-100%); margin-top:-12px;
}
.chart-tooltip.show{ display:block; }
.chart-tooltip::after{
  content:""; position:absolute; top:100%; left:50%; transform:translateX(-50%);
  border:6px solid transparent; border-top-color:#0f1524;
}
.chart-tooltip-date{ color:rgba(255,255,255,.55); font-size:10.5px; margin-bottom:2px; }
.chart-tooltip-row{ display:flex; align-items:center; gap:7px; font-weight:700; }
.chart-tooltip-dot{ width:8px; height:8px; border-radius:50%; flex-shrink:0; }

/* ------------------------------ donut / ring ------------------------------ */
.ring-chart{ display:flex; flex-direction:column; align-items:center; justify-content:center; gap:18px; flex:1; height:100%; }
.ring{
  width:160px; height:160px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center; position:relative;
}
.ring-svg{ width:100%; height:100%; transform:rotate(-90deg); overflow:visible; }
.ring-track{ fill:none; stroke:var(--border); stroke-width:22; }
.ring-arc{
  fill:none; stroke-width:22; stroke-linecap:butt; cursor:pointer;
  transition:stroke-width .15s ease, opacity .15s ease;
  transform-origin:80px 80px;
}
.ring-arc:hover{ stroke-width:26; opacity:.92; }
.ring-legend{ display:flex; flex-wrap:wrap; justify-content:center; gap:14px 20px; }
.ring-legend-row{ display:flex; align-items:center; gap:7px; font-size:12.5px; font-weight:600; color:var(--muted); }
.ring-legend-row .dotc{ width:9px; height:9px; border-radius:50%; flex-shrink:0; }

/* --------------------------------- tables -------------------------------- */
.table-wrap{ overflow-x:auto; }
table{ width:100%; border-collapse:collapse; }
th{
  text-align:left; font-size:11px; text-transform:uppercase; letter-spacing:.06em;
  color:var(--muted-2); font-weight:700; padding:13px 20px; white-space:nowrap;
  background:var(--surface-2);
}
tbody tr{ transition:background .12s ease; }
tbody tr:hover{ background:var(--surface-2); }
td{ padding:14px 20px; font-size:13.5px; border-top:1px solid var(--border); vertical-align:middle; }
tr td:first-child, tr th:first-child{ padding-left:20px; }
tr td:last-child, tr th:last-child{ padding-right:20px; }

/* Label/value detail table (e.g. the submission details modal) — label on
   the left, value on the right, instead of stacking label-above-value. */
.detail-table{ width:100%; border-collapse:collapse; table-layout:fixed; }
.detail-table tr:first-child td{ border-top:none; }
.detail-table td{
  padding:11px 0; border-top:1px solid var(--border); vertical-align:top; font-size:13.5px;
  word-break:break-word; overflow-wrap:anywhere;
}
.detail-table td:first-child{
  width:38%; padding-right:16px; text-transform:uppercase; font-weight:700; font-size:11px;
  letter-spacing:.03em; color:var(--muted);
}
input[type=checkbox]{ width:16px; height:16px; accent-color:var(--primary); cursor:pointer; flex-shrink:0; }

/* Django's CheckboxSelectMultiple widget (e.g. DailyReportForm.category) —
   applies the "checkbox-group" attrs class to *both* the outer container
   and every individual <input>, so these rules are element-scoped (div vs
   input) rather than plain .checkbox-group — otherwise the container's
   flex/padding/border would also land on each checkbox itself. Restyled
   from Django's default one-<div>-per-option list into a wrapped row of
   checkbox pills matching this app's look. */
div.checkbox-group{
  display:flex; flex-wrap:wrap; gap:8px 20px; margin:0; padding:12px 14px;
  border:1px solid var(--border); border-radius:10px; background:var(--surface-2);
}
div.checkbox-group label{
  display:flex; align-items:center; gap:7px; font-size:13px; font-weight:600;
  color:var(--text); text-transform:none; letter-spacing:0; cursor:pointer; white-space:nowrap;
}
.row-title{ display:flex; align-items:center; gap:12px; }
.row-thumb{
  width:40px; height:40px; border-radius:9px; flex-shrink:0; background:var(--primary-100); color:var(--primary-text);
  display:flex; align-items:center; justify-content:center; font-weight:700; font-size:13px;
}
.row-title-text{ font-weight:600; font-size:13.5px; }
.row-sub{ font-size:12px; color:var(--muted); margin-top:3px; }
.row-actions{ display:flex; gap:6px; }
/* Long table text: show at most 3 lines, then "…" (full text in the row's detail modal). */
.clamp-3{ display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3; overflow:hidden; word-break:break-word; }
/* Numbered pager — "Showing 1–50 of 312"  ‹ 1 2 … 7 [8] 9 … 12 › */
.pager{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-top:16px; }
.pager-pages{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.pager-btn{
  min-width:34px; height:34px; padding:0 10px; border-radius:999px; border:1px solid var(--border);
  background:var(--surface); color:var(--text); font-size:13px; font-weight:600;
  display:inline-flex; align-items:center; justify-content:center; transition:background .15s ease, border-color .15s ease;
}
a.pager-btn:hover{ background:var(--surface-2); border-color:color-mix(in srgb, var(--primary) 35%, var(--border)); }
.pager-btn.active{ background:var(--primary); border-color:var(--primary); color:#fff; }
.pager-btn.disabled{ opacity:.4; }
.pager-gap{ color:var(--muted); padding:0 2px; }
tr.row-clickable{ cursor:pointer; }
tr.row-clickable:hover td{ background:var(--surface-2); }
.icon-mini{
  width:30px; height:30px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  color:var(--muted); cursor:pointer; border:1px solid transparent; background:transparent;
}
.icon-mini:hover{ background:var(--surface-2); color:var(--text); }
.icon-mini.danger:hover{ background:var(--danger-100); color:var(--danger); }
.icon-mini svg{ width:15px; height:15px; }
.table-empty{ padding:40px 20px; text-align:center; color:var(--muted); }

/* --------------------------------- badges -------------------------------- */
.badge{
  display:inline-flex; align-items:center; gap:5px; padding:4px 10px; border-radius:20px;
  font-size:11.5px; font-weight:700; white-space:nowrap;
}
.badge .dotc{ width:6px; height:6px; border-radius:50%; background:currentColor; flex-shrink:0; }
.badge-primary{ background:var(--primary-100); color:var(--primary-text); }
.badge-success{ background:var(--success-100); color:var(--success); }
.badge-warning{ background:var(--warning-100); color:#B7860F; }
html[data-bs-theme="dark"] .badge-warning{ color:var(--warning); }
.badge-danger{ background:var(--danger-100); color:var(--danger); }
.badge-info{ background:var(--info-100); color:var(--info); }
.badge-neutral{ background:var(--surface-2); color:var(--muted); }

/* status pills used across submissions/webforms */
.status-new{ background:var(--warning-100); color:#C05F0F; }
html[data-bs-theme="dark"] .status-new{ color:var(--warning); }
.status-contacted{ background:var(--info-100); color:var(--info); }
.status-qualified{ background:var(--primary-100); color:var(--primary-text); }
.status-closed{ background:var(--success-100); color:var(--success); }
.status-rejected{ background:var(--danger-100); color:var(--danger); }
.status-published{ background:var(--success-100); color:var(--success); }
.status-draft{ background:var(--surface-2); color:var(--muted); }

/* --------------------------------- forms --------------------------------- */
.form-grid{ display:grid; grid-template-columns:1fr 1fr; gap:0px 18px; }
.field{ margin-bottom:18px; display:flex; flex-direction:column; gap:7px; }
.field.full{ grid-column:1/-1; }

/* A plain 2-up card grid (e.g. the Email/Phone Support cards on Help
   Center) — kept separate from .form-grid on purpose: .form-grid's 0
   row-gap only works there because each .field supplies its own
   margin-bottom, which a bare .card doesn't have, so reusing .form-grid
   for cards left them touching edge-to-edge once collapsed to one column. */
.card-grid-2{ display:grid; grid-template-columns:1fr 1fr; gap:18px; }
@media(max-width:640px){ .card-grid-2{ grid-template-columns:1fr; } }

/* FAQ manager two-pane layout (templates/faqs/list.html) and the field
   editor's own sub-grids (templates/webforms/_field_row.html) — pulled out
   of inline style="grid-template-columns:…" so they can actually collapse
   at tablet/mobile widths (an inline style would otherwise always beat the
   media-query rules above, regardless of specificity). */
.faq-manager-grid{ display:grid; grid-template-columns:320px 1fr; gap:0 18px; align-items:start; }
.field-row-grid{ display:grid; grid-template-columns:1fr 1.4fr; gap:10px; }
.field-row-detail-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:10px; margin-top:10px; align-items:end; }
.field-row-detail-grid label{ white-space:nowrap; }

/* 12-column dynamic field layout, driven by WebFormField.width (1-12) — used
   by the public submission page, the builder's live preview and the
   webforms "view" modal so a field with width=4 next to one with width=8
   sit side by side in the same row, matching a Bootstrap-style col-N grid. */
.dynamic-form-grid{ display:grid; grid-template-columns:repeat(12, 1fr); gap:16px 14px; }
.dynamic-form-grid .field{ margin-bottom:0; min-width:0; }
/* A long label (e.g. "PES Entrance Test (admissions.pes.edu) Application
   Number") wraps to 2 lines while its row neighbour's label stays on 1 —
   alignDynamicFormLabels() in scripts.js matches that row's label heights so
   both inputs start at the same line. align-items:flex-end (instead of the
   default :center from the base .field label rule) keeps a shorter label's
   text sitting immediately above its own input — any reserved blank space
   shows above the text, not between the text and the input.
   Uses the > direct-child combinator on purpose — the field's own name
   label is always a direct child of .field, but the file upload's
   <label class="upload-box"> and the checkbox/agreement's inline <label>
   are also <label> elements inside .field and must NOT inherit this: for a
   column-direction flex box like .upload-box, align-items is the
   *horizontal* axis, so flex-end would shove its icon/text to the right
   instead of centering them. */
.dynamic-form-grid .field > label{ line-height:1.3; align-items:flex-end; }

/* "Section Heading" field type — plain display text dropped in between real
   fields to break a long form into sections (e.g. "Scoring Details of 12th
   Std"). It's not an input: nothing is submitted or validated for it. */
.form-heading{
  font-family:'Sora', sans-serif; font-size:18px; font-weight:800;
  color:var(--primary); line-height:1.3;
}
.form-heading-sub{ font-size:13px; color:var(--muted); margin-top:4px; line-height:1.5; }
@media(max-width:640px){ .dynamic-form-grid .field{ grid-column:span 12 !important; } }
.field label{
  display:flex; font-size:11.5px; font-weight:800; text-transform:uppercase;
  color:var(--muted); align-items:center; gap:2px;
}
.field .hint{ font-weight:500; text-transform:none; letter-spacing:0; color:var(--muted-2); font-size:11px; }

/* Info-icon hover tooltip — used next to field-builder labels to explain
   what to enter, instead of a permanent caption line under the row. */
.field-hint{
  display:inline-flex; align-items:center; justify-content:center; width:15px; height:15px;
  border-radius:50%; background:var(--surface-2); color:var(--muted-2); cursor:help; position:relative; flex-shrink:0;
}
.field-hint svg{ width:10px; height:10px; }
.field-hint:hover{ background:var(--primary-100); color:var(--primary-text); }
.field-hint::after{
  content:attr(data-tooltip); position:absolute; bottom:calc(100% + 9px); left:50%; transform:translateX(-50%);
  background:var(--text); color:var(--bg); font-size:11.5px; font-weight:500; line-height:1.45;
  padding:8px 10px; border-radius:8px; width:220px; white-space:normal; text-align:left;
  box-shadow:var(--shadow-lg); z-index:60; pointer-events:none; opacity:0; visibility:hidden;
  transition:opacity .12s ease;
}
.field-hint::before{
  content:""; position:absolute; bottom:calc(100% + 4px); left:50%; transform:translateX(-50%);
  border:5px solid transparent; border-top-color:var(--text); z-index:60; pointer-events:none;
  opacity:0; visibility:hidden; transition:opacity .12s ease;
}
.field-hint:hover::after, .field-hint:hover::before{ opacity:1; visibility:visible; }

/* ------------------------------ file/image upload ------------------------------ */
.upload-row{ display:flex; align-items:stretch; gap:14px; }
.upload-box{
  flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; gap:5px; padding:26px 18px; border:1.5px dashed var(--border); border-radius:14px;
  background:var(--surface-2); cursor:pointer;
  transition:border-color .15s ease, background .15s ease, transform .15s ease;
}
.upload-box:hover{
  border-color:var(--primary-text); background:color-mix(in srgb, var(--primary-text) 7%, var(--surface-2));
  transform:translateY(-1px);
}
.upload-box input[type=file]{ display:none; }
.upload-box .upload-icon-wrap{
  width:40px; height:40px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:var(--primary-100); color:var(--primary-text); margin-bottom:4px; transition:transform .2s ease;
}
.upload-box:hover .upload-icon-wrap{ transform:translateY(-2px) scale(1.05); }
.upload-box.has-file .upload-icon-wrap{ background:var(--success-100); color:var(--success); }
.upload-box .upload-title{
  font-size:13.5px; font-weight:700; color:var(--text); text-transform:none; letter-spacing:normal;
}
.upload-box .upload-hint{
  font-size:11.5px; font-weight:500; color:var(--muted); text-transform:none; letter-spacing:normal;
}
.upload-thumb-wrap{
  position:relative; width:80px; height:80px; flex-shrink:0; border-radius:12px; overflow:visible;
  display:none;
}
.upload-thumb-wrap.show{ display:block; }
.upload-thumb-wrap img,
.upload-thumb-wrap .upload-thumb-fallback{
  width:100%; height:100%; border-radius:12px; object-fit:cover; border:1px solid var(--border); background:var(--surface-2);
  display:flex; align-items:center; justify-content:center; color:var(--muted);
}
.upload-thumb-close{
  position:absolute; top:-7px; right:-7px; width:20px; height:20px; border-radius:50%;
  background:var(--text); color:var(--bg); border:2px solid var(--surface); display:flex; align-items:center;
  justify-content:center; cursor:pointer; padding:0;
}
.upload-thumb-close svg{ width:10px; height:10px; }

/* Base input/select/textarea styling — intentionally NOT scoped to .field so
   every text/date/select control looks consistent wherever it's used
   (filter bars, inline table forms, etc.), not only inside a labeled field. */
input[type=text],input[type=url],input[type=email],input[type=tel],
input[type=password],input[type=date],input[type=time],input[type=number],input[type=search],
textarea,select{
  padding:12px 14px; border-radius:8px; border:1.5px solid var(--border);
  background:var(--surface-2); color:var(--text); font-size:13.5px; font-family:inherit; outline:none;
  transition:border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.field input[type=text],.field input[type=url],.field input[type=email],.field input[type=tel],
.field input[type=password],.field input[type=date],.field input[type=time],.field input[type=number],
.field textarea,.field select{ width:100%; }
input[type=color]{ height:42px; padding:3px; border-radius:8px; border:1.5px solid var(--border); background:var(--surface-2); }
input[type=file]{ font-size:12.5px; }
input:hover,textarea:hover,select:hover{ border-color:color-mix(in srgb, var(--primary) 35%, var(--border)); }
input:focus,textarea:focus,select:focus{
  border-color:var(--primary); background:var(--surface);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent);
}
textarea{ resize:vertical; min-height:80px; }

/* ---------------------------- custom select ------------------------------ */
/* Every <select> is progressively enhanced (see initCustomSelects in
   scripts.js) into this button+listbox pair, since a native <select>'s open
   options popup can't be restyled with CSS in any browser. */
.csel{ position:relative; display:inline-block; }
.field .csel{ display:block; width:100%; }
.csel-native{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0,0,0,0); white-space:nowrap; border:0; opacity:0;
}
.csel-trigger{
  width:100%; display:inline-flex; align-items:center; justify-content:space-between; gap:10px;
  padding:12px 14px; border-radius:8px; border:1.5px solid var(--border);
  background:var(--surface-2); color:var(--text); font-size:13.5px; font-family:inherit;
  cursor:pointer; text-align:left; transition:border-color .15s ease, background .15s ease, box-shadow .15s ease;
}
.csel-trigger:hover{ border-color:color-mix(in srgb, var(--primary) 45%, var(--border)); }
.csel.open .csel-trigger, .csel-trigger:focus-visible{
  border-color:var(--primary); background:var(--surface); outline:none;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent);
}
.csel-trigger:disabled{ opacity:.6; cursor:not-allowed; }
/* .badge/.status-* selects (e.g. the per-row status dropdown) keep their
   pill color instead of the default trigger's gray box — .csel-trigger and
   .badge/.status-* are equal CSS specificity, so without this the trigger's
   own background (declared later in this file) would otherwise win. */
.csel-trigger.badge{ width:auto; border:none; padding:4px 10px; }
.csel-trigger.status-new{ background:var(--warning-100); color:#C05F0F; }
html[data-bs-theme="dark"] .csel-trigger.status-new{ color:var(--warning); }
.csel-trigger.status-contacted{ background:var(--info-100); color:var(--info); }
.csel-trigger.status-qualified{ background:var(--primary-100); color:var(--primary-text); }
.csel-trigger.status-closed{ background:var(--success-100); color:var(--success); }
.csel-trigger.status-rejected{ background:var(--danger-100); color:var(--danger); }
.csel-value{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.csel-chevron{ display:flex; flex-shrink:0; color:var(--muted); transition:transform .2s ease; }
.csel.open .csel-chevron{ transform:rotate(180deg); }
.csel-menu{
  /* Reparented to <body> and positioned with inline top/left/width by
     initCustomSelects() in scripts.js — position:absolute relative to .csel
     would otherwise get clipped by any ancestor with overflow:hidden/auto
     (e.g. a scrollable .modal), so it's a "portal" element like .modal-backdrop.
     z-index is above .modal-backdrop's 200 so it can float over an open modal. */
  /* min-width is set inline by positionMenu() (see scripts.js) — a plain
     CSS min-width:100% here resolves against the *viewport* for a
     position:fixed element, not the trigger, letting the menu balloon out
     past the trigger/card whenever an option's text is wider than it. */
  position:fixed; z-index:250;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  box-shadow:0 16px 40px rgba(0,20,45,.18); padding:6px; max-height:260px; overflow-y:auto;
  list-style:none; margin:0;
  opacity:0; transform:translateY(-6px) scale(.98); pointer-events:none;
  transition:opacity .16s ease, transform .16s ease;
}
.csel-menu.open{ opacity:1; transform:none; pointer-events:auto; }
.csel-option{
  padding:9px 12px; border-radius:8px; font-size:13.3px; color:var(--text); cursor:pointer;
  display:flex; align-items:center; justify-content:space-between; gap:10px;
}
.csel-option:hover, .csel-option.active{ background:var(--surface-2); }
.csel-option.selected{ color:var(--primary-text); font-weight:700; }
.csel-option.selected::after{
  content:""; width:7px; height:12px; flex-shrink:0;
  border-right:2px solid var(--primary); border-bottom:2px solid var(--primary); transform:rotate(45deg) translateY(-1px);
}
.csel-option.disabled{ color:var(--muted); cursor:not-allowed; opacity:.6; }
.field .errors{ color:var(--danger); font-size:12px; font-weight:600; }
.req{ color:var(--danger); }
.form-footer{
  display:flex; justify-content:space-between; align-items:center; margin-top:10px; padding-top:20px;
  border-top:1px solid var(--border); flex-wrap:wrap; gap:10px;
}
.helptext{ color:var(--muted); font-size:12px; margin-top:2px; }
.non-field-errors{
  background:var(--danger-100); color:var(--danger); padding:10px 14px; border-radius:10px;
  font-size:13px; font-weight:600; margin-bottom:16px;
}

/* --------------------------------- chips --------------------------------- */
.chip-select{ display:flex; flex-wrap:wrap; gap:8px; }
.chip-row{ display:flex; flex-wrap:wrap; gap:8px; margin-bottom:18px; }
.chip-opt{
  padding:7px 14px; border-radius:20px; font-size:12.5px; font-weight:700; cursor:pointer;
  border:1.5px solid var(--border); background:var(--surface); color:var(--text); user-select:none;
  transition:all .15s ease; display:inline-flex; align-items:center; gap:6px;
}
.chip-opt.selected{ background:var(--primary); border-color:var(--primary); color:#fff; }
.chip-manage{
  display:inline-flex; align-items:center; gap:8px; padding:7px 8px 7px 13px; border-radius:20px;
  font-size:12.5px; font-weight:600; border:1px solid var(--border); background:var(--surface); color:var(--text);
}
.chip-manage .chip-del{
  width:18px; height:18px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  color:var(--muted); cursor:pointer; flex-shrink:0; border:none; background:transparent;
}
.chip-manage .chip-del:hover{ background:var(--danger-100); color:var(--danger); }
.chip-manage .chip-del svg{ width:11px; height:11px; }

/* --------------------------------- tabs ---------------------------------- */
.status-tabs{ display:flex; gap:6px; padding:5px; background:var(--surface-2); border:1px solid var(--border); border-radius:12px; flex-wrap:wrap; }
.status-tab{
  padding:8px 15px; border-radius:8px; font-size:12.5px; font-weight:700; color:var(--muted); cursor:pointer;
  display:flex; align-items:center; gap:6px; border:none; background:transparent;
}
.status-tab .cnt{ font-size:10.5px; background:var(--border); color:var(--muted); border-radius:20px; padding:1px 6px; }
.status-tab.active{ background:var(--surface); color:var(--text); box-shadow:var(--shadow); }
.status-tab.active .cnt{ background:var(--primary); color:#fff; }

/* -------------------------------- filters --------------------------------- */
.filter-bar{ padding:16px 18px; margin-bottom:16px; }
/* Filter bars: every dropdown and date picker is a pill, matching the
   pill search box and buttons. (.badge status selects aren't in filter
   rows, so they're unaffected.) */
.filter-row .csel-trigger,
.filter-row select,
.filter-row input[type=date]{ border-radius:999px; padding-left:16px; padding-right:14px; }
.filter-row .csel-trigger{ padding-top:10px; padding-bottom:10px; font-weight:600; width:auto; min-width:140px; }
.filter-row input[type=date]{ padding-top:9px; padding-bottom:9px; }
.filter-row{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; }
.filter-row + .filter-row{ margin-top:12px; }
.filter-search{ position:relative; flex:1 1 220px; min-width:0; }
.filter-search svg{ position:absolute; left:16px; top:14px; color:var(--muted); width:14px; height:14px; }
.filter-search input{ width:100%; padding-left:38px !important; padding-right:16px; border-radius:999px; }
.filter-clear{
  display:inline-flex; align-items:center; gap:6px; margin-left:auto;
  padding:7px 14px 7px 10px; border-radius:20px; font-size:12.5px; font-weight:700;
  border:1.5px solid var(--border); background:var(--surface); color:var(--muted);
  cursor:pointer; user-select:none; transition:all .15s ease; white-space:nowrap;
}
.filter-clear svg{ width:13px; height:13px; }
.filter-clear:hover{ background:var(--danger-100); border-color:var(--danger-100); color:var(--danger); }

/* -------------------------------- modal ----------------------------------- */
.modal-backdrop{
  position:fixed; inset:0; background:rgba(6,15,32,.55); backdrop-filter:blur(4px);
  display:none; align-items:center; justify-content:center; z-index:200; padding:16px;
}
.modal-backdrop.show{ display:flex; animation:modalBackdropIn .2s ease both; }
@keyframes modalBackdropIn{ from{ opacity:0; } to{ opacity:1; } }
@media (prefers-reduced-motion:reduce){ .modal-backdrop.show{ animation:none; } }

.modal{
  background:var(--surface); border-radius:20px; width:92vw; max-width:560px; max-height:88vh;
  overflow-y:auto; overflow-x:hidden;
  box-shadow:0 40px 90px rgba(0,10,30,.45), 0 0 0 1px rgba(255,255,255,.06);
}
.modal-backdrop.show .modal{ animation:modalIn .32s cubic-bezier(.22,.85,.32,1) both; }
@keyframes modalIn{
  from{ opacity:0; transform:scale(.92) translateY(18px); }
  to{ opacity:1; transform:scale(1) translateY(0); }
}
@media (prefers-reduced-motion:reduce){ .modal-backdrop.show .modal{ animation:none; } }
.modal.wide{ max-width:560px; }
.modal.narrow{ max-width:440px; }

.modal-head{
  display:flex; align-items:center; justify-content:space-between; gap:12px; padding:22px 26px;
  position:sticky; top:0; z-index:2; border-radius:20px 20px 0 0; overflow:hidden;
  background:linear-gradient(135deg, var(--primary) 0%, var(--primary-600) 100%);
}
.modal-head::before{
  content:""; position:absolute; top:-60px; right:-40px; width:160px; height:160px; border-radius:50%;
  background:radial-gradient(circle, rgba(255,255,255,.16) 0%, transparent 70%); pointer-events:none;
}
.modal-head h3{ font-size:17px; font-weight:800; color:#fff; position:relative; z-index:1; }
.modal-close{
  border:none; background:rgba(255,255,255,.16); border-radius:8px; width:30px; height:30px; cursor:pointer;
  color:#fff; display:flex; align-items:center; justify-content:center; flex-shrink:0; position:relative; z-index:1;
  transition:background .15s ease, transform .2s ease;
}
.modal-close:hover{ background:rgba(255,255,255,.3); transform:rotate(90deg); }
.modal-body{ padding:30px; }

/* -------------------------------- flash messages ---------------------------- */
.messages-stack{ position:fixed; top:16px; right:16px; z-index:400; display:flex; flex-direction:column; gap:10px; max-width:360px; }
.alert{
  display:flex; align-items:flex-start; gap:10px; padding:12px 14px; border-radius:12px;
  font-size:13px; font-weight:600; box-shadow:var(--shadow-lg); background:var(--surface); border:1px solid var(--border);
}
.alert-success{ background:var(--success-100); color:var(--success); border-color:transparent; }
.alert-danger{ background:var(--danger-100); color:var(--danger); border-color:transparent; }
.alert-warning{ background:var(--warning-100); color:#B7860F; border-color:transparent; }
html[data-bs-theme="dark"] .alert-warning{ color:var(--warning); }
.alert-info,.alert-debug{ background:var(--info-100); color:var(--info); border-color:transparent; }
.alert-close{ margin-left:auto; background:none; border:none; cursor:pointer; color:inherit; opacity:.7; flex-shrink:0; }
.alert-close:hover{ opacity:1; }

/* -------------------------------- toast ------------------------------------ */
.toast{
  position:fixed; bottom:24px; right:24px; background:var(--text); color:var(--bg);
  padding:14px 18px; border-radius:10px; font-size:13.5px; font-weight:600;
  box-shadow:var(--shadow-lg); display:flex; align-items:center; gap:10px; z-index:300;
  transform:translateY(30px); opacity:0; transition:all .3s ease; pointer-events:none;
}
.toast.show{ transform:translateY(0); opacity:1; }
.toast svg{ width:16px; height:16px; color:var(--success); flex-shrink:0; }

/* ---------------------------------- back to top ------------------------------ */
.back-to-top{
  position:fixed; right:24px; bottom:90px; z-index:150; width:44px; height:44px; border-radius:50%;
  border:1px solid var(--border); background:var(--surface); color:var(--primary-text);
  display:flex; align-items:center; justify-content:center; cursor:pointer; box-shadow:var(--shadow-lg);
  opacity:0; transform:translateY(10px); pointer-events:none;
  transition:opacity .2s ease, transform .2s ease, background .15s ease;
}
.back-to-top.show{ opacity:1; transform:none; pointer-events:auto; }
.back-to-top:hover{ background:var(--surface-2); }
.back-to-top svg{ width:18px; height:18px; }
@media (prefers-reduced-motion:reduce){ .back-to-top{ transition:opacity .2s ease; } }
@media (max-width:640px){ .back-to-top{ right:16px; bottom:78px; width:40px; height:40px; } }

/* ------------------------------- switch/toggle ------------------------------ */
.switch{ position:relative; width:42px; height:24px; flex-shrink:0; display:inline-block; }
.switch input{ opacity:0; width:0; height:0; }
.slider{ position:absolute; inset:0; background:var(--border); border-radius:20px; cursor:pointer; transition:.2s; }
.slider:before{ content:""; position:absolute; width:18px; height:18px; left:3px; top:3px; background:#fff; border-radius:50%; transition:.2s; box-shadow:0 1px 3px rgba(0,0,0,.25); }
.switch input:checked + .slider{ background:var(--warning); }
.switch input:checked + .slider:before{ transform:translateX(18px); }
.switch input:disabled + .slider{ opacity:.5; cursor:not-allowed; }

/* -------------------------------- empty state -------------------------------- */
.empty-page{ display:flex; flex-direction:column; align-items:center; justify-content:center; padding:70px 20px; text-align:center; color:var(--muted); }
.empty-page svg{ width:52px; height:52px; margin-bottom:16px; opacity:.5; }
.empty-page h3{ color:var(--text); margin:0 0 6px; }
.empty-page p{ margin:0; font-size:13.5px; max-width:340px; }

/* -------------------------------- misc utils -------------------------------- */
.flex{ display:flex; }
.flex-col{ display:flex; flex-direction:column; }
.items-center{ align-items:center; }
.justify-between{ justify-content:space-between; }
.gap-6{ gap:6px; } .gap-8{ gap:8px; } .gap-10{ gap:10px; } .gap-14{ gap:14px; } .gap-18{ gap:18px; }
.mb-0{ margin-bottom:0 !important; }
.text-muted{ color:var(--muted); }
.text-sub{ font-size:12.5px; color:var(--muted); }
.w-full{ width:100%; }
.divider{ height:1px; background:var(--border); margin:14px 0; }

/* -------------------------------- auth page -------------------------------- */
.auth-shell{ min-height:100vh; display:flex; align-items:center; justify-content:center; background:var(--bg); padding:20px; }
.auth-card{ width:420px; max-width:100%; padding:40px 36px; text-align:center; }
.auth-card img.logo-full{ height:40px; width:auto; margin:0 auto 18px; display:block; object-fit:contain; }
html[data-bs-theme="dark"] .auth-card img.logo-full-light{ display:none; }
html:not([data-bs-theme="dark"]) .auth-card img.logo-full-dark{ display:none; }
.auth-card h1{ font-size:20px; margin-bottom:6px; }
.auth-card .sub{ font-size:13.5px; color:var(--muted); margin-bottom:26px; }
.auth-card form{ text-align:left; }
.auth-remember{ display:flex; align-items:center; justify-content:space-between; margin:-6px 0 18px; font-size:12.5px; }
.auth-remember label{ display:flex; align-items:center; gap:7px; color:var(--muted); font-weight:600; cursor:pointer; }

/* Sign in / forgot / reset / verify-otp pages opt into this richer look via
   an extra class — plain .auth-shell stays untouched since public_form.html
   (webform submission page) reuses it too and shouldn't get a dark banner.
   Deliberately light/airy (not the site-footer's dark gradient) so the two
   don't blend into one panel when a footer sits directly below the shell. */
.auth-shell-branded{
  position:relative; overflow:hidden; isolation:isolate;
  background:
    radial-gradient(ellipse 760px 520px at 10% 8%, color-mix(in srgb, var(--primary) 16%, transparent) 0%, transparent 62%),
    radial-gradient(ellipse 640px 520px at 92% 94%, color-mix(in srgb, var(--warning) 16%, transparent) 0%, transparent 62%),
    var(--bg);
}
.auth-shell-branded::before{
  content:""; position:absolute; inset:0; z-index:0; opacity:.7; pointer-events:none;
  background-image:radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--text) 10%, transparent) 1px, transparent 0);
  background-size:26px 26px;
}
.auth-blob{
  position:absolute; border-radius:50%; filter:blur(80px); opacity:.28; z-index:0; pointer-events:none;
  animation:authBlobFloat 16s ease-in-out infinite alternate;
}
.auth-blob.b1{ width:340px; height:340px; background:#5ea3ff; top:-120px; left:-80px; }
.auth-blob.b2{ width:280px; height:280px; background:#ffb648; bottom:-100px; right:-60px; animation-duration:20s; animation-delay:-6s; }
.auth-blob.b3{ width:220px; height:220px; background:#7fe8c0; top:38%; right:6%; animation-duration:14s; animation-delay:-10s; }
@keyframes authBlobFloat{ 0%{ transform:translate(0,0) scale(1); } 100%{ transform:translate(26px,-20px) scale(1.1); } }
@media (prefers-reduced-motion:reduce){ .auth-blob{ animation:none; } }
@keyframes authCardIn{ from{ opacity:0; transform:translateY(20px) scale(.96); } to{ opacity:1; transform:none; } }

/* ------------------- sign-in / forgot-password swap card ------------------ */
/* Both forms live in the DOM at once (accounts/templates/user_auth.html);
   .auth-swap-link clicks just toggle .auth-right-active on #authSwapCard
   (see initAuthSwap in scripts.js) so the accent panel + forms slide across
   without a page reload. Falls back to a normal link navigation (server
   renders the right state via url_name) if JS never runs. */
.auth-card-split{
  position:relative; width:760px; max-width:100%; min-height:480px;
  display:flex; padding:0; overflow:hidden;
  animation:authCardIn .5s cubic-bezier(.22,.85,.32,1) both;
}
.auth-shell-branded .auth-card-split,
.auth-shell-branded .auth-static-split{
  box-shadow:0 30px 70px -10px rgba(20,30,60,.25), 0 8px 24px rgba(20,30,60,.12);
}
.auth-form-panel{
  position:absolute; top:0; left:0; width:50%; height:100%; padding:48px 46px;
  display:flex; flex-direction:column; justify-content:center; text-align:left;
  transition:transform .6s cubic-bezier(.65,0,.35,1), opacity .45s ease;
}
.auth-form-panel img.logo-full{ height:32px; width:auto; margin-bottom:18px; display:block; object-fit:contain; }
html[data-bs-theme="dark"] .auth-form-panel img.logo-full-light{ display:none; }
html:not([data-bs-theme="dark"]) .auth-form-panel img.logo-full-dark{ display:none; }
.auth-form-panel h1{ font-size:20px; margin-bottom:6px; }
.auth-form-panel .sub{ font-size:13.5px; color:var(--muted); margin-bottom:24px; }
.auth-panel-signin{ z-index:2; }
.auth-panel-forgot{ z-index:1; opacity:0; pointer-events:none; }
.auth-card-split.auth-right-active .auth-panel-signin{ transform:translateX(100%); opacity:0; pointer-events:none; z-index:1; }
.auth-card-split.auth-right-active .auth-panel-forgot{ transform:translateX(100%); opacity:1; pointer-events:auto; z-index:5; }

.auth-accent{
  position:absolute; top:0; left:50%; width:50%; height:100%; overflow:hidden; z-index:10;
  background:linear-gradient(150deg, #061428 0%, var(--primary-600) 55%, var(--primary) 100%);
  transition:transform .6s cubic-bezier(.65,0,.35,1);
}
.auth-accent::before{
  content:""; position:absolute; inset:0; z-index:0; opacity:.5; pointer-events:none;
  background-image:radial-gradient(circle at 1px 1px, rgba(255,255,255,.14) 1px, transparent 0);
  background-size:26px 26px;
}
.auth-accent-shape{ position:absolute; border-radius:50%; filter:blur(60px); opacity:.35; z-index:0; pointer-events:none; animation:authBlobFloat 16s ease-in-out infinite alternate; }
.auth-accent-shape.s1{ width:220px; height:220px; background:#5ea3ff; top:-70px; left:15%; }
.auth-accent-shape.s2{ width:180px; height:180px; background:#7fe8c0; bottom:-60px; right:5%; animation-duration:20s; animation-delay:-6s; }
.auth-card-split.auth-right-active .auth-accent{ transform:translateX(-100%); }

.auth-accent-panel{
  position:absolute; inset:0; z-index:1; padding:44px 38px;
  display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center;
  color:#fff; transition:opacity .35s ease, transform .45s ease;
}
.auth-accent-panel svg{ width:38px; height:38px; margin-bottom:16px; opacity:.9; }
.auth-accent-panel h2{ font-family:'Sora', sans-serif; font-size:19px; margin-bottom:10px; color:#fff; }
.auth-accent-panel p{ font-size:13px; line-height:1.6; opacity:.85; margin-bottom:22px; }
.auth-accent-panel .btn{ background:transparent; border:1.5px solid rgba(255,255,255,.7); color:#fff; }
.auth-accent-panel .btn:hover{ background:rgba(255,255,255,.12); border-color:#fff; }
.auth-accent-signin{ opacity:1; transform:none; transition-delay:.2s; }
.auth-accent-forgot{ opacity:0; transform:translateY(10px); pointer-events:none; }
.auth-card-split.auth-right-active .auth-accent-signin{ opacity:0; transform:translateY(-10px); pointer-events:none; transition-delay:0s; }
.auth-card-split.auth-right-active .auth-accent-forgot{ opacity:1; transform:none; pointer-events:auto; transition-delay:.2s; }

@media (prefers-reduced-motion:reduce){
  .auth-card-split{ animation:none; }
  .auth-form-panel, .auth-accent, .auth-accent-panel, .auth-accent-shape{ transition:none; animation:none; }
}
@media (max-width:680px){
  .auth-card-split{ display:block; width:420px; min-height:0; }
  .auth-accent{ display:none; }
  .auth-form-panel{ position:static; width:100%; padding:40px 32px; transform:none !important; opacity:1 !important; pointer-events:auto !important; transition:none; }
  .auth-panel-forgot{ display:none; }
  .auth-card-split.auth-right-active .auth-panel-signin{ display:none; }
  .auth-card-split.auth-right-active .auth-panel-forgot{ display:flex; }
}

/* ------------- static split card (reset-password / verify-otp) ------------ */
/* Same two-tone look as .auth-card-split but no swap partner, so it's a
   plain flex layout instead of the absolutely-positioned sliding version. */
.auth-static-split{
  width:760px; max-width:100%; display:flex; overflow:hidden; padding:0;
  animation:authCardIn .5s cubic-bezier(.22,.85,.32,1) both;
}
.auth-static-form{ width:50%; padding:48px 46px; display:flex; flex-direction:column; justify-content:center; text-align:left; }
.auth-static-form img.logo-full{ height:32px; width:auto; margin-bottom:18px; display:block; object-fit:contain; }
html[data-bs-theme="dark"] .auth-static-form img.logo-full-light{ display:none; }
html:not([data-bs-theme="dark"]) .auth-static-form img.logo-full-dark{ display:none; }
.auth-static-form h1{ font-size:20px; margin-bottom:6px; }
.auth-static-form .sub{ font-size:13.5px; color:var(--muted); margin-bottom:24px; }
.auth-static-accent{
  width:50%; position:relative; overflow:hidden; isolation:isolate;
  background:linear-gradient(150deg, #061428 0%, var(--primary-600) 55%, var(--primary) 100%);
  display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center;
  padding:44px 38px; color:#fff;
}
.auth-static-accent::before{
  content:""; position:absolute; inset:0; z-index:0; opacity:.5; pointer-events:none;
  background-image:radial-gradient(circle at 1px 1px, rgba(255,255,255,.14) 1px, transparent 0);
  background-size:26px 26px;
}
.auth-static-accent > *{ position:relative; z-index:1; }
.auth-static-accent svg{ width:38px; height:38px; margin-bottom:16px; opacity:.9; }
.auth-static-accent h2{ font-family:'Sora', sans-serif; font-size:19px; margin-bottom:10px; color:#fff; }
.auth-static-accent p{ font-size:13px; line-height:1.6; opacity:.85; }

@media (prefers-reduced-motion:reduce){ .auth-static-split{ animation:none; } .auth-static-accent .auth-accent-shape{ animation:none; } }
@media (max-width:680px){
  .auth-static-split{ display:block; width:420px; }
  .auth-static-accent{ display:none; }
  .auth-static-form{ width:100%; }
}

/* --------------------------- public webform hero --------------------------- */
/* Header banner on the public submission page (webforms/public_form.html):
   the form's own image with its name/description overlaid on it, if it has
   one — else the campus color as background with the same name/description
   printed directly on it. When a campus image exists, it appears as a small
   round badge (image case: overlapping the bottom of the banner; text case:
   sitting inline above the title). */
.form-hero{ position:relative; width:100%; min-height:150px; display:flex; align-items:center; justify-content:center; }
.form-hero-img{ width:100%; height:220px; object-fit:cover; display:block; }
.form-hero-overlay{
  position:absolute; left:0; right:0; bottom:0; padding:20px 34px 50px; z-index:1;
  background:linear-gradient(180deg, transparent 0%, rgba(0,0,0,.5) 45%, rgba(0,0,0,.88) 100%);
}
.form-hero-overlay h1{
  font-family:'Sora', sans-serif; font-size:21px; font-weight:800; margin:0 0 6px;
  text-shadow:0 1px 4px rgba(0,0,0,.5);
}
.form-hero-overlay .form-hero-desc{ text-shadow:0 1px 3px rgba(0,0,0,.5); }
.form-hero-badge{
  position:absolute; right:24px; bottom:-40px; width:84px; height:84px; border-radius:18px; overflow:hidden;
  display:flex; align-items:center; justify-content:center; border:3px solid var(--surface); box-shadow:var(--shadow); z-index:2;
}
.form-hero-badge img{ width:100%; height:100%; object-fit:contain; }
.form-hero-body{ padding:32px 34px; }
.form-hero-body.has-badge{ padding-top:60px; }

/* Sits opposite the breadcrumb, above the form card — a second, more
   explicit way back to the campus page beyond just clicking the campus
   name in the breadcrumb itself. */
.form-back-link{
  display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:700;
  color:var(--muted-2); white-space:nowrap; transition:color .15s ease;
}
.form-back-link svg{ transition:transform .2s ease; }
.form-back-link:hover{ color:var(--primary-text); }
.form-back-link:hover svg{ transform:translateX(-3px); }

/* Text-only hero — shown instead of an image when the webform has none: just
   the campus color as background with the webform's own text_color (set in
   the builder) so the title/description stay legible on it, no extra badge. */
.form-hero-text{ flex-direction:column; align-items:flex-start; justify-content:center; padding:30px 34px; }
.form-hero-text h1{ font-family:'Sora', sans-serif; font-size:22px; font-weight:800; margin:0 0 8px; }
.form-hero-desc{ font-size:14px; line-height:1.55; opacity:.9; }
.form-hero-desc p{ margin:0 0 10px; }
.form-hero-desc p:last-child{ margin-bottom:0; }

/* Shared with the homepage hero blobs (static/css/pages/style.css) — defined
   here too so the footer's floating blobs still animate on pages that don't
   load the homepage's stylesheet (login, public webforms, error pages). */
@keyframes heroBlobFloat{
  0%{ transform:translate(0,0) scale(1); }
  100%{ transform:translate(30px,-24px) scale(1.12); }
}

/* ------------------------------- site footer -------------------------------- */
.site-footer{
  position:relative; overflow:hidden; isolation:isolate;
  background:linear-gradient(160deg, #061428 0%, var(--primary-600) 55%, var(--primary) 100%);
  color:rgba(255,255,255,.82);
}
.site-footer::before{
  content:""; position:absolute; inset:0; z-index:0; opacity:.5;
  background-image:radial-gradient(circle at 1px 1px, rgba(255,255,255,.12) 1px, transparent 0);
  background-size:22px 22px;
}
.footer-blob{
  position:absolute; border-radius:50%; filter:blur(80px); opacity:.28; z-index:0;
  animation:heroBlobFloat 18s ease-in-out infinite alternate;
}
.footer-blob.f1{ width:360px; height:360px; background:#5ea3ff; top:-140px; left:-80px; }
.footer-blob.f2{ width:320px; height:320px; background:#7fe8c0; bottom:-160px; right:-60px; animation-duration:22s; animation-delay:-6s; }

.site-footer-inner{
  position:relative; z-index:1; max-width:1180px; margin:0 auto; padding:56px 24px 32px;
  display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:28px;
}
.site-footer-col{ display:flex; flex-direction:column; gap:10px; font-size:13px; }
.site-footer-brand{ font-family:'Sora', sans-serif; font-weight:800; font-size:16px; color:#fff; margin-bottom:2px; }
.site-footer-col p{ margin:0; color:rgba(255,255,255,.62); line-height:1.6; }
.site-footer-head{ font-size:11.5px; font-weight:800; text-transform:uppercase; letter-spacing:.6px; color:rgba(255,255,255,.4); margin-bottom:2px; }
.site-footer-col a{ color:rgba(255,255,255,.82); transition:color .2s ease, transform .2s ease; width:fit-content; }
.site-footer-col a:hover{ color:#7fe8c0; transform:translateX(3px); }
.site-footer-app{ display:flex; align-items:center; gap:8px; }
.site-footer-social{
  position:relative; z-index:1; max-width:1180px; margin:0 auto; padding:4px 24px 28px; display:flex; align-items:center; gap:14px;
  font-size:12.5px; color:rgba(255,255,255,.55); font-weight:600;
}
.site-footer-social a{
  width:36px; height:36px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.16); color:rgba(255,255,255,.75);
  backdrop-filter:blur(6px); transition:all .25s ease;
}
.site-footer-social a:hover{
  color:#fff; border-color:#7fe8c0; background:rgba(127,232,192,.18);
  transform:translateY(-3px); box-shadow:0 8px 20px rgba(127,232,192,.25);
}
.site-footer-bottom{
  position:relative; z-index:1; text-align:center; padding:18px 24px; font-size:11.5px; color:rgba(255,255,255,.5);
  border-top:1px solid rgba(255,255,255,.12);
}
.site-footer-bottom a{ color:rgba(255,255,255,.85); font-weight:700; text-decoration:underline; }
.site-footer-bottom a:hover{ color:#7fe8c0; }
@media (prefers-reduced-motion:reduce){ .footer-blob{ animation:none; } }
@media (max-width:820px){
  .site-footer-inner{ grid-template-columns:1fr 1fr; }
  .public-header-inner{ padding:12px 16px; gap:14px; }
}
@media (max-width:520px){
  .site-footer-inner{ grid-template-columns:1fr; padding:40px 18px 24px; }
  .site-footer-social{ padding:4px 18px 22px; }
  .site-footer-bottom{ padding:16px 18px; }
}

/* =====================================================================
   RESPONSIVE OVERRIDES — kept together at the very end of this file on
   purpose. CSS resolves same-specificity ties by source order, and almost
   every rule below shares its selector's specificity with that selector's
   own base definition earlier in this file (e.g. .grid-stats, .grid-2col,
   .stat-card, .modal, .page-head…). If these overrides lived earlier in
   the file (as an earlier pass of this file mistakenly had them, right
   after the layout section), the later base rule would always win the
   cascade regardless of viewport — silently no-opping the whole block on
   every screen size. Keep ANY new responsive rule added in the future
   down here too, after its target's base rule, or it will suffer the same
   fate.
   ===================================================================== */
@media(max-width:1100px){
  .grid-stats{ grid-template-columns:repeat(2,1fr); }
  .hero-grid{ grid-template-columns:1fr; }
  .grid-2col{ grid-template-columns:1fr; }
  .grid-2col-4-8{ grid-template-columns:1fr; }
  .admin-preview-grid, .admin-preview-grid.cols-2{ grid-template-columns:repeat(2,1fr); }
  .form-grid{ grid-template-columns:1fr; }
  .schedule-row{ grid-template-columns:1fr 1fr; }
  .mini-grid{ grid-template-columns:1fr; }
  .settings-grid-stats-3{ grid-template-columns:repeat(2,1fr) !important; }
  .faq-manager-grid{ grid-template-columns:220px 1fr !important; }
}
@media(max-width:820px){
  .sidebar{ transform:translateX(-100%); }
  .sidebar.open{ transform:translateX(0); }
  .main{ margin-left:0; }
  .sidebar.collapsed ~ .main{ margin-left:0; }
  /* Scoped to a direct .topbar child so this doesn't also hide the search
     form reused inside #mobileSearchModal (same .search-box class, but
     nested in a modal, not the topbar). */
  .topbar > .search-box{ display:none; }
  .sidebar-brand{ justify-content:space-between; }
  .sidebar-brand .drawer-close-btn{ display:flex; }
  .grid-stats{ grid-template-columns:1fr; }
  /* Matches .grid-stats above — the sidebar's already gone off-canvas by
     this width, so admin-preview-grid's 2-up layout (still active from the
     wider @media(max-width:1100px) rule) is just as cramped as the stat
     cards would be; both should go full-width together, not wait for the
     narrower @media(max-width:640px) rule further down. */
  .admin-preview-grid, .admin-preview-grid.cols-2{ grid-template-columns:1fr; }
  /* The count-5 layout's "widen the last card to span 2 columns" trick
     (see the base rule) only makes sense against the desktop 3-column grid
     — in a single-column mobile grid it would just force an unwanted
     implicit 2nd column for that one card, so it's turned off here too. */
  .admin-preview-grid.count-5 > :last-child{ grid-column:auto; }
  .settings-grid-stats-3{ grid-template-columns:1fr !important; }
  .content-shell{ margin:0 12px 12px; border-radius:20px; }
  .content{ padding:22px 18px 44px; }
  /* Row-gap is 0 in the base rule (fine for the desktop side-by-side
     layout, where there's only ever one row) — once this collapses to a
     single column the two panels would otherwise stack with zero space
     between them, so the FAQs panel's header touches the last category
     card right above it. */
  .faq-manager-grid{ grid-template-columns:1fr !important; gap:28px 18px !important; }
  .field-row-detail-grid{ grid-template-columns:1fr 1fr !important; }
}
@media(max-width:640px){
  .admin-preview-grid, .admin-preview-grid.cols-2{ grid-template-columns:1fr; }

  /* Shrink every icon/button in the topbar clusters (dashboard topbar AND
     the public header — logo, hamburger, theme toggle, quick actions, bell,
     avatar) so the whole row comfortably fits a phone screen instead of
     crowding/overflowing it. */
  .topbar{ padding:0 12px; gap:8px; }
  .topbar-right{ gap:3px; }
  .public-header-inner{ padding:10px 12px; gap:0px; }
  .public-brand{ margin-right:10px; }
  .public-header-right{ gap:3px; }
  .icon-btn{ width:32px; height:32px; border-radius:50%; }
  .icon-btn svg{ width:15px; height:15px; }
  .public-brand{ gap:6px; }
  .public-brand img.logo-mark{ width:38px; height:auto; border-radius:8px; }
  .theme-toggle{ padding:3px; gap:2px; }
  .theme-toggle button{ width:26px; height:26px; border-radius:50%; }
  .theme-toggle button svg{ width:13px; height:13px; }
  .avatar{ width:28px; height:28px; border-radius:50%; font-size:11.5px; }
  .topbar-divider{ display:none; }
  .dot{ width:6px; height:6px; top:6px; right:7px; }

  .public-utility-inner{ padding:7px 12px; gap:8px; }
  .utility-links a:not(.utility-admissions-btn) span{ display:none; }
  .utility-links{ gap:12px; }
  .utility-divider{ display:none; }
  .utility-admissions-btn{ padding:6px 12px; }

  .page-head{ flex-direction:column; align-items:stretch; gap:14px; margin-bottom:22px; padding:2px 0 20px 16px; }
  .page-head h1{ font-size:22px; }
  .flex{ flex-wrap:wrap; }

  /* .compact-card marks the templates' one-off "bare" cards that carry their
     own inline padding directly on .card (not the .card-head/.card-body
     structured ones, which would break — the head's border would no longer
     span edge-to-edge if .card itself gained padding too). */
  .compact-card{ padding:14px !important; }
  .form-hero-body{ padding:22px 18px; }
  .form-hero-body.has-badge{ padding-top:48px; }
  .form-hero-overlay{ padding:16px 18px 40px; }
  .form-hero-text{ padding:22px 18px; }
  .card-head{ padding:14px 16px; }
  .card-body{ padding:14px; }
  .stat-card{ padding:16px; }
  .settings-card{ padding:2px 14px !important; }
  .filter-bar{ padding:12px 14px; }
  .empty-page{ padding:44px 16px; }

  .modal{ border-radius:16px; }
  .modal-head{ padding:16px 18px; }
  .modal-body{ padding:16px; }

  .notif-row-outer{ flex-wrap:wrap; }

  .form-grid{ gap:0; }
  .field-row-grid, .field-row-detail-grid{ grid-template-columns:1fr !important; }

  th, td{ padding:10px 12px; }
  .filter-row{ gap:8px; }
  .status-tabs{ width:100%; }
}
@media(max-width:480px){
  .content{ padding:16px 12px 36px; }
  .content-shell{ margin:0 8px 8px; border-radius:16px; }
  .grid-stats{ gap:12px; }
  .stat-value{ font-size:23px; }
  .modal-backdrop{ padding:10px; }
  .modal{ width:100%; }
}

/* .menu-panel (quick actions / notifications / profile dropdowns in the
   topbar) is normally position:absolute, anchored via right:0 to whichever
   icon-btn triggered it (see .menu-wrap). On a narrow phone the trigger
   sits close to the screen's right edge, so a 280–340px-wide panel
   anchored there overflows off the LEFT edge of the viewport and gets
   clipped — hence text like "Quick actions" reading as "k actions". Below
   640px it switches to position:fixed, which re-anchors it to the
   viewport itself instead of the trigger, so it can be pinned to a safe
   margin on both sides regardless of which icon opened it. */
@media(max-width:640px){
  .menu-panel{ position:fixed; top:calc(var(--topbar-height) + 8px); left:12px; right:12px; width:auto; max-width:none; }
  #notificationsMenu{ width:auto; }
}
/* .public-nav .menu-panel (campus dropdowns inside the mobile nav drawer)
   already gets its own static, full-width treatment — more specific than
   the rule above, so it isn't affected by it. */

@media(max-width:640px){
  .detail-header{ flex-direction:column; align-items:stretch; gap:14px; padding:2px 0 20px 16px; margin-bottom:24px; }
  .detail-header > div:first-child{ flex:none; }
  .detail-header-actions{ width:100%; }
}
