/* ==========================================================================
   بلسان قومه — design system (server-rendered). Calm, modern, responsive,
   animated, reduced-motion aware.
   ========================================================================== */
:root {
  --bg:#FBF8F1; --surface:#FFFFFF; --text:#243536; --muted:#5E7173; --border:#E8E2D5;
  --primary:#065053; --primary-hover:#0A6C70; --primary-soft:#E4EEEC;
  --accent:#C9841A; --gold:#C9841A; --gold-hover:#A96D12; --gold-dark:#8A5A12; --gold-soft:#F5E9CF;
  --mist:#92B0B2; --light-gold:#D9A85C;
  --success:#2F8F6F; --warning:#C9841A; --danger:#C0392B;
  --serif:"Cormorant Garamond",Georgia,serif;
  --sans:"Manrope",system-ui,-apple-system,sans-serif;
  --r-card:16px; --r-pill:999px; --r-input:10px;
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-8:32px;
  --sh-sm:0 1px 2px rgba(36,53,54,.06); --sh-md:0 4px 16px rgba(36,53,54,.08); --sh-lg:0 12px 32px rgba(36,53,54,.12);
  --ease:cubic-bezier(.4,0,.2,1); --dur:200ms;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{font-family:var(--sans);background:var(--bg);color:var(--text);
  line-height:1.5;-webkit-font-smoothing:antialiased;overflow-wrap:break-word;}
html{overflow-x:hidden;}
img{max-width:100%;height:auto;}
h1,h2,h3,h4{font-family:var(--serif);}
h1{font-size:1.85rem;font-weight:700;margin:0;letter-spacing:normal;}
h2{font-size:1.35rem;font-weight:700;margin:0 0 var(--sp-4);letter-spacing:normal;}
h3{font-size:1.18rem;font-weight:600;margin:0 0 var(--sp-2);}
h4{font-size:1.05rem;font-weight:600;margin:0 0 var(--sp-3);}
p{margin:0;} a{color:inherit;text-decoration:none;}
.muted{color:var(--muted);} .small{font-size:.82rem;} .danger{color:var(--danger)!important;}
code{background:var(--primary-soft);padding:1px 6px;border-radius:6px;font-size:.85em;}
.inline{display:inline;}

/* ---- Branded icons (public/images/icons/) ---- */
.ico{width:1.25em;height:1.25em;object-fit:contain;vertical-align:-.28em;flex:0 0 auto;}
.ico--xs{width:14px;height:14px;} .ico--sm{width:20px;height:20px;} .ico--md{width:28px;height:28px;}
.ico--lg{width:40px;height:40px;} .ico--xl{width:56px;height:56px;} .ico--2xl{width:72px;height:72px;}
.stat__icon .ico{width:34px;height:34px;vertical-align:middle;}
.empty__icon{opacity:1;} .empty__icon .ico{width:60px;height:60px;}
.feature__icon .ico{width:36px;height:36px;}
.quick>span:first-child .ico{width:30px;height:30px;vertical-align:middle;}

/* ---- Gold 4-point star accent ---- */
.brand-star{display:inline-block;width:14px;height:14px;flex:0 0 auto;
  background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 0 L14.5 9.5 L24 12 L14.5 14.5 L12 24 L9.5 14.5 L0 12 L9.5 9.5 Z' fill='%23C9841A'/></svg>") center/contain no-repeat;}

/* ---- Brand ---- */
.brand{display:inline-flex;align-items:center;gap:10px;}
.brand--center{flex-direction:column;justify-content:center;width:100%;margin-bottom:var(--sp-4);text-align:center;}
.brand__mark{height:40px;width:auto;display:block;}
.brand--center .brand__mark{height:72px;}
.brand__word{font-family:var(--serif);font-weight:600;font-size:1.45rem;line-height:1;color:var(--primary);}
/* legacy (harmless) */
.brand__logo{width:34px;height:34px;border-radius:10px;flex:0 0 auto;
  background:var(--grad-primary,linear-gradient(135deg,#065053,#0A7E82));box-shadow:var(--sh-sm);}
.brand__name{font-family:var(--serif);font-weight:600;font-size:1.5rem;line-height:1;color:var(--primary);}

/* ---- Buttons / inputs ---- */
.btn{font:inherit;font-weight:600;font-size:.92rem;border:1px solid transparent;border-radius:var(--r-pill);
  padding:10px 18px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;white-space:nowrap;
  transition:transform var(--dur) var(--ease),background var(--dur),box-shadow var(--dur),opacity var(--dur);}
.btn:active{transform:scale(.96);}
.btn:disabled{opacity:.55;cursor:not-allowed;}
.btn--primary{background:var(--primary);color:#fff;box-shadow:var(--sh-sm);}
.btn--primary:hover:not(:disabled){background:var(--primary-hover);box-shadow:var(--sh-md);}
.btn--secondary{background:var(--gold-soft);color:var(--gold-dark);}
.btn--secondary:hover:not(:disabled){background:#efdcb6;}
.btn--ghost{background:transparent;color:var(--primary);}
.btn--ghost:hover:not(:disabled){background:var(--primary-soft);color:var(--primary-hover);}
.btn--danger{background:var(--danger);color:#fff;}
.btn--block{width:100%;} .btn--sm{padding:6px 12px;font-size:.82rem;}
.icon-btn{border:1px solid var(--border);background:var(--surface);width:32px;height:32px;border-radius:8px;cursor:pointer;
  color:var(--muted);font-size:.9rem;display:inline-flex;align-items:center;justify-content:center;transition:background var(--dur);}
.icon-btn:hover:not(:disabled){background:var(--primary-soft);color:var(--primary);} .icon-btn:disabled{opacity:.4;cursor:default;}
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:var(--sp-4);}
.field__label{font-size:.82rem;font-weight:600;color:var(--muted);}
input,textarea,select{font:inherit;color:var(--text);background:var(--surface);border:1px solid var(--border);
  border-radius:var(--r-input);padding:11px 13px;width:100%;transition:border var(--dur),box-shadow var(--dur);}
input[type=file]{padding:9px;}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px rgba(6,80,83,.15);}
textarea{resize:vertical;}
/* Radios & checkboxes are small controls — NOT full-width bordered boxes. This
   overrides the blanket input rule above; components that hide the native
   control (.seg, .attend-opt) define their own rules later and still win. */
input[type="radio"],input[type="checkbox"]{width:auto;padding:0;border:0;background:none;box-shadow:none;
  accent-color:var(--primary);cursor:pointer;vertical-align:middle;flex:0 0 auto;}
input[type="radio"]:focus-visible,input[type="checkbox"]:focus-visible{outline:2px solid var(--primary);outline-offset:2px;box-shadow:none;}
.field-error{color:var(--danger);font-size:.85rem;margin:4px 0 var(--sp-3);}
.field-note{color:var(--success);font-size:.85rem;margin-bottom:var(--sp-3);}
.stack>*{margin-bottom:var(--sp-2);}
*:focus-visible{outline:2px solid var(--primary);outline-offset:2px;}

/* ---- Layout ---- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:var(--sp-6);
  box-shadow:var(--sh-sm);margin-bottom:var(--sp-4);}
.container{max-width:960px;margin:0 auto;padding:var(--sp-6) var(--sp-4) 96px;}
.page-head{margin-bottom:var(--sp-6);} .page-head h1+p{margin-top:4px;}
.row-between{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);margin-bottom:var(--sp-4);flex-wrap:wrap;}
.row-end{display:flex;justify-content:flex-end;} .gap{gap:var(--sp-3);}
.section{margin-bottom:var(--sp-8);}
.back-link{color:var(--muted);font-size:.88rem;display:inline-block;margin-bottom:var(--sp-3);}
.back-link:hover{color:var(--primary);}
.subhead{margin:var(--sp-3) 0 var(--sp-2);}
.grid-2{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-4);}
@media(min-width:640px){.grid-2{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}}
.inline-form{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;margin-bottom:var(--sp-4);}
.inline-form input{width:auto;flex:1;min-width:160px;}

/* ---- Auth ---- */
body.auth{min-height:100dvh;}
.auth-wrap{min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:var(--sp-6);gap:var(--sp-4);
  background:radial-gradient(900px 400px at 50% -10%,rgba(6,80,83,.12),transparent 70%),
    radial-gradient(700px 320px at 100% 110%,rgba(201,132,26,.08),transparent 65%),var(--bg);}
.auth-card{width:100%;max-width:400px;box-shadow:var(--sh-lg);text-align:center;}
.auth-sub{margin-bottom:var(--sp-6);} .auth-links{display:flex;justify-content:space-between;margin-top:var(--sp-4);}
.auth-foot{font-size:.8rem;}
.otp{display:flex;gap:var(--sp-2);justify-content:center;margin-bottom:var(--sp-4);}
.otp__box{width:46px;height:56px;text-align:center;font-size:1.4rem;font-weight:700;padding:0;border-radius:12px;}
.otp__box.pop{animation:pop 180ms var(--ease);}
@keyframes pop{0%{transform:scale(1);}45%{transform:scale(1.14);}100%{transform:scale(1);}}
.otp.shake{animation:shake 350ms;}
@keyframes shake{0%,100%{transform:translateX(0);}20%{transform:translateX(-8px);}40%{transform:translateX(7px);}
  60%{transform:translateX(-5px);}80%{transform:translateX(4px);}}

/* ---- Topbar / nav ---- */
.topbar{position:sticky;top:0;z-index:40;border-bottom:1px solid var(--border);
  background:linear-gradient(180deg,rgba(251,248,241,.97),rgba(251,248,241,.9));backdrop-filter:blur(10px);}
.topbar__inner{max-width:1160px;margin:0 auto;padding:8px var(--sp-4);display:flex;align-items:center;gap:var(--sp-3);}
.topbar .brand{flex:0 0 auto;}   /* keep the wordmark whole on desktop — never ellipsize */
.topbar .brand__word{font-size:1.2rem;} .topbar .brand__mark{height:30px;} .topbar .brand__logo{width:28px;height:28px;}
.topnav{display:none;gap:2px;margin-left:var(--sp-2);}
.topnav__link{flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border-radius:var(--r-pill);font-size:.9rem;font-weight:600;color:var(--muted);white-space:nowrap;
  transition:background var(--dur),color var(--dur);}
.topnav__link .ico{width:18px;height:18px;}
.bottomnav__icon .ico{width:23px;height:23px;}
.topnav__link:hover{background:var(--primary-soft);color:var(--primary);}
.topnav__link.is-active{color:var(--gold-dark);background:var(--gold-soft);}
.topbar__right{margin-left:auto;display:flex;align-items:center;gap:var(--sp-3);flex:0 0 auto;}
.bottomnav{position:fixed;bottom:0;left:0;right:0;z-index:40;display:flex;border-top:1px solid var(--border);
  background:rgba(251,248,241,.96);backdrop-filter:blur(10px);padding:6px 4px calc(6px + env(safe-area-inset-bottom));}
.bottomnav__link{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;font-size:.68rem;font-weight:600;
  color:var(--muted);padding:6px 2px;border-radius:10px;}
.bottomnav__link.is-active{color:var(--primary);}
.bottomnav__link.is-active .bottomnav__icon{color:var(--gold);}
.bottomnav__icon{font-size:1.1rem;line-height:1;}

/* ---- Stats / cards ---- */
.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--sp-4);}
.stat{text-align:left;display:flex;flex-direction:column;gap:4px;transition:transform var(--dur),box-shadow var(--dur);}
.stat:hover{transform:translateY(-3px);box-shadow:var(--sh-md);}
.stat__icon{font-size:1.3rem;} .stat__value{font-size:2rem;font-weight:700;letter-spacing:-.02em;}
.quick-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--sp-4);}
.quick{display:flex;flex-direction:column;gap:4px;transition:transform var(--dur),box-shadow var(--dur);}
.quick:hover{transform:translateY(-3px);box-shadow:var(--sh-md);} .quick>span:first-child{font-size:1.4rem;}
.card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--sp-4);}
.course-card{display:flex;flex-direction:column;gap:var(--sp-2);transition:transform var(--dur),box-shadow var(--dur);}
.course-card:hover{transform:translateY(-3px);box-shadow:var(--sh-md);} .course-card.is-archived{opacity:.6;}
.course-card__cover{height:120px;border-radius:10px;background-size:cover;background-position:center;background-color:var(--primary-soft);}
.course-card__head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);}
.course-card__desc{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.6em;}
.course-card__foot{display:flex;gap:4px;flex-wrap:wrap;margin-top:auto;padding-top:var(--sp-2);}
.course-card__ring{display:flex;align-items:center;gap:var(--sp-3);margin-top:auto;}

/* ---- Lists / avatars / pills ---- */
.list{display:flex;flex-direction:column;gap:var(--sp-3);}
.list__row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-4);margin-bottom:0;transition:box-shadow var(--dur);flex-wrap:wrap;}
.list__row:hover{box-shadow:var(--sh-md);}
.list__main{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1;}
.list__main b{overflow:hidden;text-overflow:ellipsis;}
.list__actions{display:flex;gap:4px;flex-wrap:wrap;justify-content:flex-end;align-items:center;}
.avatar{width:40px;height:40px;border-radius:50%;flex:0 0 auto;background:var(--primary-soft);color:var(--primary);
  font-weight:700;font-size:.8rem;display:flex;align-items:center;justify-content:center;}
.pill{font-size:.74rem;font-weight:700;padding:3px 10px;border-radius:var(--r-pill);text-transform:capitalize;white-space:nowrap;}
.pill--ok{background:#D8EFE5;color:#1F6E52;} .pill--warn{background:var(--gold-soft);color:var(--gold-dark);}
.pill--bad{background:#F7DEDA;color:#912A20;} .pill--primary{background:var(--gold-soft);color:var(--gold-dark);}
.pill--muted{background:#EEEAE0;color:var(--muted);}

/* ---- Tabs / phases / chips ---- */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--border);margin-bottom:var(--sp-6);overflow-x:auto;}
.tab{font-weight:600;color:var(--muted);padding:10px 14px;border-bottom:2px solid transparent;white-space:nowrap;
  transition:color var(--dur),border-color var(--dur);}
.tab.is-active{color:var(--primary);border-bottom-color:var(--primary);}
/* A .tab may be an <a> (server-routed tabs) or a <button> (client-side tabs on
   the session page). Reset the button chrome so the two look identical. */
button.tab{background:none;border:none;border-bottom:2px solid transparent;font:inherit;cursor:pointer;}
button.tab:hover{color:var(--primary);}
button.tab:focus-visible{outline:2px solid var(--primary);outline-offset:-2px;border-radius:6px 6px 0 0;}
/* Hidden panels. Explicit, because a display: rule on the element would other-
   wise beat the UA's [hidden]{display:none} (see .cal-filter[hidden]). */
[data-tabpanel][hidden]{display:none;}
/* Cohort-hub: breadcrumb + drill-down cards */
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:7px;font-size:.85rem;margin-bottom:14px;}
.crumbs__link{color:var(--muted);font-weight:600;} .crumbs__link:hover{color:var(--primary);}
.crumbs__sep{color:var(--muted);opacity:.5;} .crumbs__cur{color:var(--text);font-weight:700;}
.tabs--ctx{margin-top:2px;}
.section-title{font-size:1.3rem;margin:0 0 14px;}
.drill-list{display:flex;flex-direction:column;gap:12px;}
.drill-card{display:flex;align-items:center;gap:16px;padding:16px 18px;text-decoration:none;color:inherit;
  transition:box-shadow var(--dur),transform var(--dur);}
.drill-card:hover{box-shadow:var(--sh-md);transform:translateY(-2px);}
.drill-card__num{flex:0 0 auto;width:46px;height:46px;border-radius:12px;background:var(--primary-soft);color:var(--primary);
  display:flex;align-items:center;justify-content:center;font-weight:800;font-size:1.15rem;}
.drill-card__body{flex:1;min-width:0;} .drill-card__body h3{margin:0 0 2px;font-size:1.12rem;}
.drill-card__body p{margin:0;}
.drill-go{flex:0 0 auto;color:var(--gold);font-size:1.3rem;font-weight:700;}
.drill-card--locked{opacity:.75;border-style:dashed;} .drill-card--locked .drill-card__num{background:#EEEAE0;}
a.sess-card{text-decoration:none;color:inherit;}
.phase__head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);flex-wrap:wrap;}
.phase__actions{display:flex;gap:4px;align-items:center;flex-wrap:wrap;}
.phase__sessions{margin-top:var(--sp-4);border-top:1px dashed var(--border);padding-top:var(--sp-3);}
.phase__access{margin-top:var(--sp-4);border-top:1px dashed var(--border);padding-top:var(--sp-3);}
.chip-row{display:flex;flex-wrap:wrap;gap:var(--sp-2);}
.chip{border:1px solid var(--border);background:var(--surface);border-radius:var(--r-pill);padding:6px 12px;font-size:.82rem;
  font-weight:600;color:var(--text);}
.chip--done{background:#D8EFE5;color:#1F6E52;border-color:#B4DECB;} .chip--pending{background:#EEEAE0;color:var(--muted);}
.access-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);border-radius:var(--r-pill);
  padding:4px 8px 4px 12px;font-size:.82rem;background:var(--surface);}
.phase-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--sp-4);}
.phase-card{display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;padding:var(--sp-6) var(--sp-4);}
.phase-card.is-locked{background:#F6F2E8;border-style:dashed;color:var(--muted);} .lock{font-size:1.6rem;}

/* ---- Attendance grid ---- */
.att-details{border:1px solid var(--border);border-radius:12px;padding:var(--sp-3) var(--sp-4);margin-top:var(--sp-3);}
.att-details summary{cursor:pointer;}
.att-grid{display:flex;flex-direction:column;gap:var(--sp-3);margin-top:var(--sp-3);}
.att-row{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);padding:var(--sp-2) 0;flex-wrap:wrap;}
.att-row__name{min-width:0;}
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--r-pill);overflow:hidden;}
.seg__btn{font-size:.8rem;font-weight:600;padding:7px 12px;cursor:pointer;color:var(--muted);background:var(--surface);
  transition:background var(--dur),color var(--dur);margin:0;display:inline-flex;}
.seg__btn:not(:last-child){border-right:1px solid var(--border);}
.seg__btn input{position:absolute;opacity:0;width:0;height:0;}
.seg__btn--present:has(input:checked),.seg__btn--present.is-on{background:var(--success);color:#fff;}
.seg__btn--late:has(input:checked),.seg__btn--late.is-on{background:var(--warning);color:#fff;}
.seg__btn--absent:has(input:checked),.seg__btn--absent.is-on{background:var(--danger);color:#fff;}

.hw__completions{margin-top:var(--sp-3);border-top:1px dashed var(--border);padding-top:var(--sp-3);}
.hw__stat{margin-bottom:var(--sp-2);}

/* ---- Progress ring ---- */
.ring__fg{transition:stroke-dashoffset 900ms var(--ease);} .ring__txt{font:700 .92rem var(--sans);fill:var(--text);}

/* ---- Animated checkmark ---- */
.check-done{display:inline-flex;width:34px;height:34px;border-radius:50%;background:#D8EFE5;align-items:center;justify-content:center;flex:0 0 auto;}
.check-svg{width:20px;height:20px;fill:none;stroke:var(--success);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;}
.check-svg path{stroke-dasharray:30;stroke-dashoffset:30;animation:draw 400ms var(--ease) forwards;}
@keyframes draw{to{stroke-dashoffset:0;}}

/* ---- Notifications ---- */
.notif__dot{width:9px;height:9px;border-radius:50%;background:transparent;flex:0 0 auto;}
.notif.is-unread{background:var(--primary-soft);border-color:#BBD4D0;} .notif.is-unread .notif__dot{background:var(--primary);}

/* ---- Empty ---- */
.empty{text-align:center;padding:var(--sp-8) var(--sp-4);color:var(--muted);}
.empty__icon{font-size:2.4rem;margin-bottom:var(--sp-3);opacity:.7;}
.empty__title{font-weight:700;color:var(--text);margin-bottom:4px;}

/* ---- Skeleton (used by JS if needed) ---- */
.skeleton{height:62px;border-radius:var(--r-card);background:#F1ECE0;position:relative;overflow:hidden;margin-bottom:var(--sp-3);}
.skeleton::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.7),transparent);animation:shimmer 1.4s infinite;}
@keyframes shimmer{100%{transform:translateX(100%);}}

/* ---- Toast ---- */
#toast-root{position:fixed;top:16px;left:0;right:0;z-index:100;display:flex;flex-direction:column;align-items:center;gap:var(--sp-2);pointer-events:none;}
.toast{pointer-events:auto;background:var(--text);color:#fff;padding:11px 18px;border-radius:var(--r-pill);box-shadow:var(--sh-lg);
  font-size:.88rem;font-weight:600;opacity:0;transform:translateY(-14px);transition:opacity var(--dur),transform var(--dur);max-width:90vw;}
.toast.is-in{opacity:1;transform:translateY(0);}
.toast--success{background:#1F6E52;} .toast--error{background:#8A2018;}

/* ---- Modal ---- */
.modal-overlay{position:fixed;inset:0;z-index:90;background:rgba(36,53,54,.45);display:flex;align-items:center;justify-content:center;
  padding:var(--sp-4);opacity:0;transition:opacity var(--dur);}
.modal-overlay:not([hidden]){opacity:1;}
.modal-overlay[hidden]{display:none;}
.modal{background:var(--surface);border-radius:18px;width:100%;max-width:480px;box-shadow:var(--sh-lg);max-height:90vh;overflow:hidden;
  display:flex;flex-direction:column;transform:scale(.94) translateY(8px);transition:transform var(--dur) var(--ease);}
.modal-overlay:not([hidden]) .modal{transform:scale(1) translateY(0);}
.modal__head{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-4) var(--sp-6);border-bottom:1px solid var(--border);}
.modal__title{font-size:1.05rem;margin:0;} .modal__body{padding:var(--sp-6);overflow-y:auto;}

/* ---- View enter ---- */
.view-enter{animation:viewIn 300ms var(--ease);}
@keyframes viewIn{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:translateY(0);}}

/* ---- Desktop ---- */
@media(min-width:720px){.topnav{display:flex;}.bottomnav{display:none;}.container{padding-bottom:var(--sp-8);}h1{font-size:2.15rem;}}

/* ---- Reduced motion ---- */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;}
}

/* ==========================================================================
   GLOBAL POLISH — richer buttons, shadows, backgrounds
   ========================================================================== */
:root{
  --sh-sm:0 1px 2px rgba(36,53,54,.05), 0 1px 3px rgba(36,53,54,.05);
  --sh-md:0 6px 20px rgba(36,53,54,.08);
  --sh-lg:0 20px 48px rgba(36,53,54,.16);
  --grad-primary:linear-gradient(135deg,#065053,#0A7E82);
  --grad-mesh:radial-gradient(1000px 500px at 15% -10%,rgba(6,80,83,.16),transparent 60%),
              radial-gradient(800px 500px at 100% 0%,rgba(201,132,26,.12),transparent 55%),
              radial-gradient(700px 500px at 50% 120%,rgba(146,176,178,.14),transparent 60%);
}
body.app{background:
  radial-gradient(1200px 500px at 100% -5%,rgba(6,80,83,.06),transparent 60%),
  radial-gradient(900px 420px at 0% 100%,rgba(201,132,26,.04),transparent 60%),
  var(--bg);}
.btn--primary{background:var(--grad-primary);box-shadow:0 4px 14px rgba(6,80,83,.32);}
.btn--primary:hover:not(:disabled){background:linear-gradient(135deg,#0A6C70,#0A8B90);box-shadow:0 8px 22px rgba(6,80,83,.42);}
.btn--lg{padding:14px 26px;font-size:1rem;border-radius:var(--r-pill);}
.btn--white{background:#fff;color:var(--primary);box-shadow:0 6px 20px rgba(36,53,54,.18);}
.btn--white:hover{background:#FBF8F1;}
.card{transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease),border-color var(--dur);}
h1{letter-spacing:normal;} h2{letter-spacing:normal;}
.eyebrow{display:inline-block;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--gold-dark);background:var(--gold-soft);padding:5px 12px;border-radius:var(--r-pill);margin-bottom:14px;}
.auth-card{border:1px solid var(--border);box-shadow:0 30px 60px rgba(36,53,54,.14);}

/* ---- Admin landing editor ---- */
.section-edit{border-left:4px solid var(--primary);}
.section-edit.is-hidden{opacity:.7;border-left-color:var(--border);}
.section-edit__head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;}
.section-edit__title{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
.section-edit__actions{display:flex;gap:4px;align-items:center;flex-wrap:wrap;}
.section-edit__form{margin-top:14px;border-top:1px dashed var(--border);padding-top:12px;}
.section-edit__form summary{cursor:pointer;font-weight:600;color:var(--primary);}
.section-edit__image{margin-top:16px;border-top:1px dashed var(--border);padding-top:14px;}
.section-edit__preview img{max-width:240px;border-radius:12px;box-shadow:var(--sh-sm);margin-bottom:8px;display:block;}
.add-section .chip-row{margin-top:6px;}

/* ==========================================================================
   SESSIONS / QUIZZES / PROGRESSION
   ========================================================================== */
/* Admin session rows */
.sess{border:1px solid var(--border);border-radius:12px;padding:12px 14px;margin-bottom:10px;background:#fff;}
.sess.is-done{background:#F1F8F4;border-color:#C4E4D4;}
.sess__top{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;}
.sess__title{display:flex;align-items:center;gap:8px;}
.sess__idx{width:24px;height:24px;border-radius:50%;background:var(--primary-soft);color:var(--primary);
  font-size:.8rem;font-weight:700;display:flex;align-items:center;justify-content:center;flex:0 0 auto;}
.sess__actions{display:flex;gap:4px;align-items:center;flex-wrap:wrap;}
.sess__meta{margin-top:6px;}
.add-sess>summary{list-style:none;cursor:pointer;margin-top:6px;}
.add-sess>summary::-webkit-details-marker{display:none;}

/* Student phase cards */
.phase-locked{display:flex;align-items:center;gap:14px;opacity:.85;border-style:dashed;}
.phase-locked__icon{font-size:1.6rem;}
.phase-open__head{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:16px;flex-wrap:wrap;}
.phase-open__ring{flex:0 0 auto;}
.sess-list{display:flex;flex-direction:column;gap:10px;margin-bottom:8px;}
.sess-card{display:flex;gap:14px;align-items:flex-start;border:1px solid var(--border);border-radius:14px;padding:14px 16px;background:#fff;
  transition:box-shadow var(--dur),transform var(--dur);}
.sess-card.is-active{border-color:var(--primary);box-shadow:0 0 0 3px rgba(6,80,83,.12);}
.sess-card.is-done{background:#F1F8F4;border-color:#C4E4D4;}
.sess-card.is-locked{opacity:.7;border-style:dashed;}
.sess-card__num{flex:0 0 auto;width:34px;height:34px;border-radius:50%;background:var(--primary-soft);color:var(--primary);
  font-weight:700;display:flex;align-items:center;justify-content:center;}
.sess-card.is-done .sess-card__num{background:#D8EFE5;color:#1F6E52;}
.sess-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;}
.sess-card__action{flex:0 0 auto;align-self:center;}
.sess-hw{margin-top:10px;display:flex;flex-direction:column;gap:8px;}
.attended-badge{display:inline-flex;align-items:center;gap:4px;font-weight:700;color:#1F6E52;background:#D8EFE5;
  padding:6px 12px;border-radius:var(--r-pill);font-size:.82rem;}

/* Quiz builder options */
.opt-row{display:flex;align-items:center;gap:10px;margin-bottom:8px;}
.opt-radio{display:inline-flex;align-items:center;gap:5px;font-size:.8rem;color:var(--muted);white-space:nowrap;}
.opt-radio input{width:auto;}
.opt-row input[type=text]{flex:1;}
.opt-list{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:6px;}
.opt-list li{padding:8px 12px;border-radius:10px;background:#F6F2E8;color:var(--muted);}
.opt-list li.is-correct{background:#D8EFE5;color:#1F6E52;font-weight:600;}

/* Quiz taking */
.quiz-choose{display:flex;flex-direction:column;gap:8px;margin-top:12px;}
.quiz-choice{display:flex;align-items:center;gap:10px;border:1px solid var(--border);border-radius:12px;padding:12px 14px;cursor:pointer;
  transition:border var(--dur),background var(--dur);}
.quiz-choice:hover{background:#F6F2E8;}
.quiz-choice input{width:auto;}
.quiz-choice:has(input:checked){border-color:var(--primary);background:var(--primary-soft);}
.quiz-result{display:flex;align-items:center;gap:20px;flex-wrap:wrap;}
.quiz-opts{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:6px;}
.quiz-opts li{display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:10px;background:#F6F2E8;}
.quiz-opts li.is-correct{background:#D8EFE5;color:#1F6E52;font-weight:600;}
.quiz-opts li.is-wrong{background:#F7DEDA;color:#912A20;}
.quiz-opts__mark{width:18px;display:inline-block;font-weight:700;}

/* ==========================================================================
   DASHBOARDS (admin + student)
   ========================================================================== */
.stat-grid--5{grid-template-columns:repeat(auto-fit,minmax(140px,1fr));}
.stat-grid--4{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));}
.stat__label{color:var(--muted);font-size:.82rem;font-weight:600;}
.stat--accent{background:var(--grad-primary);color:#fff;border:none;}
.stat--accent .stat__label{color:rgba(255,255,255,.85);}
.stat--accent .stat__icon{opacity:1;color:var(--light-gold);}
/* `a.stat--link{color:inherit}` further down is (0,1,1) and so beats the
   (0,1,0) rule above, which left the accent tile's number dark-on-dark on every
   dashboard whose tile is a link. Restore white at matching specificity.
   (The profile page had already worked around this locally.) */
a.stat--accent{color:#fff;}
.stat--accent .stat__value{color:#fff;}
.stat__value{font-family:var(--serif);font-weight:700;}

.dash-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:20px;margin-top:24px;}
@media(min-width:900px){.dash-grid{grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);}}
.dash-col{display:flex;flex-direction:column;gap:20px;}
.section-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:22px;box-shadow:var(--sh-sm);}
.section-card h2{margin:0 0 16px;font-size:1.28rem;}

/* mini list rows */
.mini-list{display:flex;flex-direction:column;}
.mini-row{display:flex;align-items:center;gap:14px;padding:12px 8px;border-radius:12px;margin:0 -8px;
  transition:background var(--dur);}
.mini-row:hover{background:#F6F2E8;}
.mini-row + .mini-row{border-top:1px solid var(--border);}
.mini-row__icon{width:40px;height:40px;border-radius:11px;background:var(--primary-soft);color:var(--primary);
  display:flex;align-items:center;justify-content:center;font-size:1.1rem;flex:0 0 auto;}
.mini-row__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;}
.mini-row__main b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.mini-row__meta{flex:0 0 auto;text-align:right;white-space:nowrap;}
.ring-mini{flex:0 0 auto;}

/* date chip */
.date-chip{flex:0 0 auto;width:48px;height:52px;border-radius:12px;background:var(--gold-soft);color:var(--gold-dark);
  display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1;}
.date-chip__d{font-family:var(--serif);font-size:1.4rem;font-weight:700;}
.date-chip__m{font-size:.66rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;}

/* quick actions grid */
.qa-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px;}
.qa{display:flex;flex-direction:column;gap:6px;align-items:flex-start;padding:16px;border:1px solid var(--border);
  border-radius:12px;font-weight:600;font-size:.9rem;transition:transform var(--dur),box-shadow var(--dur),border-color var(--dur);}
.qa:hover{transform:translateY(-2px);box-shadow:var(--sh-md);border-color:var(--mist);}
.qa>span:first-child{font-size:1.3rem;}

/* activity feed */
.feed{display:flex;flex-direction:column;gap:14px;}
.feed__item{display:flex;gap:12px;align-items:flex-start;}
.feed__body{display:flex;flex-direction:column;gap:2px;min-width:0;}
.feed__body span:first-child{line-height:1.4;}
.feed__dot{width:9px;height:9px;border-radius:50%;background:var(--border);margin-top:6px;flex:0 0 auto;}
.feed__dot.is-unread{background:var(--primary);}
.avatar--sm{width:32px;height:32px;font-size:.72rem;}

/* callout */
.callout{margin-top:20px;border-left:4px solid var(--warning);background:var(--gold-soft);}
.callout--warn{border-left-color:var(--warning);}
.callout__head b{font-size:1rem;}

/* ==========================================================================
   CALENDAR
   ========================================================================== */
.cal{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);box-shadow:var(--sh-sm);padding:16px;}
.cal-head{display:flex;align-items:center;gap:10px;margin-bottom:14px;}
.cal-head__label{margin:0;font-size:1.15rem;min-width:180px;text-align:center;}
.cal-head__today{margin-left:auto;}
.cal-scroll{overflow-x:auto;}
.cal-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;min-width:640px;}
.cal-weekday{font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);
  text-align:center;padding:4px 0;}
.cal-cell{min-height:104px;border:1px solid var(--border);border-radius:10px;padding:6px;background:#fff;
  display:flex;flex-direction:column;gap:4px;}
.cal-cell--empty{background:#FBF8F1;border-style:dashed;}
.cal-cell.is-today{border-color:var(--primary);box-shadow:0 0 0 2px rgba(6,80,83,.15) inset;}
.cal-cell__day{font-size:.8rem;font-weight:700;color:var(--muted);}
.cal-cell.is-today .cal-cell__day{color:var(--primary);}
.cal-cell__events{display:flex;flex-direction:column;gap:3px;}
.cal-event{display:flex;align-items:flex-start;gap:5px;width:100%;text-align:left;border:none;cursor:pointer;
  background:color-mix(in srgb, var(--ec) 12%, white);color:var(--text);border-left:3px solid var(--ec);
  border-radius:6px;padding:4px 6px;font-size:.74rem;font-weight:600;font-family:inherit;
  transition:background var(--dur),transform var(--dur);}
.cal-event:hover{background:color-mix(in srgb, var(--ec) 22%, white);transform:translateX(1px);}
.cal-event__dot{width:6px;height:6px;border-radius:50%;background:var(--ec);flex:0 0 auto;margin-top:.35em;}
/* Event titles WRAP — they used to be one clipped line, which on a 7-column
   grid left "Ses…" / the first few letters and nothing else. The cell grows to
   fit instead (min-height is a floor, never a cap).
   `break-word`, NOT `anywhere`: anywhere splits inside a word as soon as the
   line is tight, which turned "Foundation — Session 1" into "Foundatio / n —".
   break-word prefers the spaces and only splits a word that cannot fit a line
   on its own. The width to make that possible comes from .cal-page below. */
.cal-event__txt{flex:1;min-width:0;white-space:normal;overflow-wrap:break-word;line-height:1.3;}
.cal-event__done{color:var(--success);font-weight:800;}
.cal-event.is-active{outline:1.5px solid var(--ec);}
.cal-event.is-attended{opacity:.72;}
@media(max-width:520px){ .cal-cell{min-height:88px;} }

/* ==========================================================================
   CALENDAR — colorful upgrade + class filter
   ========================================================================== */
.cal{position:relative;overflow:hidden;padding:0;border:none;
  background:linear-gradient(180deg,#fff, #FDFBF6);box-shadow:var(--sh-md);}
.cal::before{content:"";position:absolute;top:0;left:0;right:0;height:5px;
  background:linear-gradient(90deg,#065053,#0A7E82,#92B0B2,#D9A85C,#C9841A);}
.cal-head{padding:20px 20px 6px;margin:0;}
.cal-head__label{font-family:var(--serif);font-size:1.6rem;font-weight:700;letter-spacing:normal;
  background:linear-gradient(135deg,#065053,#C9841A);-webkit-background-clip:text;background-clip:text;color:transparent;}
.cal-head .icon-btn{border-radius:10px;}

/* Filter chips */
.cal-filter{display:flex;flex-wrap:wrap;gap:8px;padding:6px 20px 16px;}
.cal-chip{font:inherit;font-weight:700;font-size:.82rem;cursor:pointer;border-radius:var(--r-pill);
  padding:7px 14px;display:inline-flex;align-items:center;gap:7px;
  border:1.5px solid var(--border);background:#fff;color:var(--muted);
  transition:transform var(--dur),box-shadow var(--dur),background var(--dur),color var(--dur),border-color var(--dur);}
.cal-chip:hover{transform:translateY(-2px);box-shadow:var(--sh-sm);color:var(--text);}
.cal-chip__dot{width:11px;height:11px;border-radius:50%;background:var(--ec,#065053);flex:0 0 auto;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--ec,#065053) 22%, transparent);}
.cal-chip.is-active{color:#fff;border-color:transparent;
  background:linear-gradient(135deg, var(--ec,#065053), color-mix(in srgb, var(--ec,#065053) 70%, #000 8%));
  box-shadow:0 6px 16px color-mix(in srgb, var(--ec,#065053) 40%, transparent);}
.cal-chip.is-active .cal-chip__dot{background:#fff;box-shadow:none;}
/* the "All" chip (no --ec) active = brand teal gradient */
.cal-chip[data-course=""].is-active{background:var(--grad-primary);box-shadow:0 6px 16px rgba(6,80,83,.4);}

.cal-scroll{padding:0 20px 20px;}
.cal-grid{gap:8px;}
.cal-weekday{color:var(--primary);}

.cal-cell{min-height:112px;border:1px solid #EFE9DC;border-radius:14px;padding:8px;
  background:#fff;transition:box-shadow var(--dur),transform var(--dur),opacity var(--dur);}
.cal-cell:hover{box-shadow:var(--sh-md);}
.cal-cell.is-dimmed{opacity:.45;}
.cal-cell--empty{background:repeating-linear-gradient(135deg,#FBF8F1,#FBF8F1 8px,#F4EEE1 8px,#F4EEE1 16px);border-style:none;}
.cal-cell__day{font-size:.82rem;font-weight:800;color:var(--muted);width:26px;height:26px;display:flex;align-items:center;justify-content:center;}
.cal-cell.is-today{border-color:transparent;box-shadow:0 0 0 2px var(--primary), var(--sh-md);}
.cal-cell.is-today .cal-cell__day{background:var(--grad-primary);color:#fff;border-radius:50%;}

/* Vibrant event pills */
.cal-event{gap:6px;padding:5px 8px;border-radius:8px;font-size:.75rem;font-weight:700;
  border:none;border-left:3px solid var(--ec);
  background:color-mix(in srgb, var(--ec) 15%, white);
  color:color-mix(in srgb, var(--ec) 62%, #243536);
  box-shadow:0 1px 2px color-mix(in srgb, var(--ec) 25%, transparent);}
.cal-event:hover{background:color-mix(in srgb, var(--ec) 26%, white);transform:translateY(-1px) translateX(1px);
  box-shadow:0 4px 10px color-mix(in srgb, var(--ec) 35%, transparent);}
.cal-event__dot{width:7px;height:7px;box-shadow:0 0 0 2px color-mix(in srgb,var(--ec) 25%, transparent);}
.cal-event.is-active{outline:2px solid var(--ec);outline-offset:1px;}
.cal-event.is-attended{opacity:.6;}

@media(max-width:520px){ .cal-head,.cal-filter,.cal-scroll{padding-left:12px;padding-right:12px;} .cal-cell{min-height:92px;} }

/* THE CALENDAR PAGE IS WIDER THAN THE REST OF THE APP.
   routes/calendar.js sets bodyClass:"cal-page" for this one page. Seven columns
   inside the app's 960px reading column leave a ~125px cell — about 80px of
   actual text once the padding and the dot are taken out, which is narrower
   than the word "Foundation", so titles broke mid-word however they wrapped.
   1440px puts ~145px of text in each cell — enough for "Foundation — Session 1"
   on two comfortable lines — while 96vw keeps a margin on either side so the
   grid never runs into the edge of a wide display. It is a max-width, so
   nothing changes on a narrower screen: below ~1200px the page is simply as
   wide as the viewport allows, and .cal-scroll still scrolls the grid under its
   640px floor. */
body.cal-page .container{max-width:min(1440px, 96vw);}

/* ...AND IT MUST FILL THE SPACE BESIDE THE SIDEBAR, NOT SIT RIGHT OF IT.
   From 720px up the top bar becomes a fixed left sidebar and `body.app
   .container` reserves it with
       margin-left: sidebar + max(0, (100% - sidebar - 960px)/2)
   — a gutter sized to centre a NINE-HUNDRED-AND-SIXTY-pixel block. Widening the
   calendar past 960 therefore did not widen it leftward at all: the gutter
   stayed put and the extra width all went out to the right, which is why the
   grid ended up hard against the right edge with a lake of empty page on its
   left. (`body.sa-page` fixes the same rule the same way, further down.)
   So: the same arithmetic, with the calendar's own width in place of the 960 —
   which centres it in the area beside the sidebar instead of pinning it to one
   end. --cal-w is the only number to change if it wants to be wider or
   narrower; at 1280 a day cell is ~163px, of which ~120px is title, which is
   comfortably more than the ~106px that already fit "Foundation — Session 1"
   on two lines. Specificity (0,3,1) beats the rule above, so this holds
   wherever it sits in the file. */
@media (min-width:720px){
  body.app.cal-page{ --cal-w:1280px; }
  body.app.cal-page .container{
    max-width:var(--cal-w);
    margin-left:calc(var(--sidebar-w) + max(0px,(100% - var(--sidebar-w) - var(--cal-w))/2));
    margin-right:auto;
    padding-inline:var(--sp-6);
  }
}
/* Room bought back from the chrome, since it all goes to the title. */
body.cal-page .cal-event{padding-inline:6px;gap:5px;}

/* ==========================================================================
   CHECK-IN BUTTON + MATERIALS
   ========================================================================== */
.checkin-btn{position:relative;font:inherit;font-weight:800;font-size:.95rem;color:#fff;cursor:pointer;border:none;
  padding:13px 24px;border-radius:var(--r-pill);white-space:nowrap;
  background:linear-gradient(135deg,#065053,#2F8F6F);
  box-shadow:0 8px 22px rgba(6,80,83,.4);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur);overflow:visible;isolation:isolate;}
.checkin-btn:hover{transform:translateY(-2px) scale(1.03);box-shadow:0 12px 30px rgba(6,80,83,.5);}
.checkin-btn:active{transform:scale(.97);}
.checkin-btn__pulse{position:absolute;inset:0;border-radius:var(--r-pill);background:#2F8F6F;z-index:-1;
  animation:checkinPulse 1.8s var(--ease) infinite;}
@keyframes checkinPulse{0%{transform:scale(1);opacity:.55;}70%{transform:scale(1.35);opacity:0;}100%{opacity:0;}}
.checkin-soon{display:inline-block;text-align:center;font-size:.76rem;color:var(--muted);line-height:1.35;
  border:1px dashed var(--border);border-radius:12px;padding:8px 12px;}
.checkin-soon b{color:var(--text);}
.missed-badge{display:inline-flex;align-items:center;font-weight:700;color:#912A20;background:#F7DEDA;
  padding:6px 12px;border-radius:var(--r-pill);font-size:.8rem;}

.mat-icon{width:44px;height:44px;border-radius:12px;background:var(--primary-soft);color:var(--primary);
  display:flex;align-items:center;justify-content:center;font-size:1.3rem;flex:0 0 auto;}
.mat-link{color:var(--primary);word-break:break-all;}
.mat-card{transition:transform var(--dur),box-shadow var(--dur);}
.mat-card:hover{transform:translateY(-2px);box-shadow:var(--sh-md);}

@media(prefers-reduced-motion:reduce){ .checkin-btn__pulse{animation:none;display:none;} }

/* ==========================================================================
   HOME CHECK-IN HERO + PHASE TILES
   ========================================================================== */
.checkin-hero{position:relative;overflow:hidden;border-radius:20px;padding:26px 28px;margin-bottom:22px;color:#fff;
  background:linear-gradient(135deg,#065053 0%,#0A6C70 45%,#2F8F6F 100%);
  box-shadow:0 18px 44px rgba(6,80,83,.4);}
.checkin-hero::before{content:"";position:absolute;top:-40%;right:-10%;width:320px;height:320px;border-radius:50%;
  background:radial-gradient(circle,rgba(255,255,255,.25),transparent 70%);}
.checkin-hero__live{display:inline-flex;align-items:center;gap:8px;font-size:.72rem;font-weight:800;letter-spacing:.08em;
  text-transform:uppercase;background:rgba(255,255,255,.2);padding:5px 12px;border-radius:var(--r-pill);margin-bottom:12px;}
.checkin-hero__dot{width:9px;height:9px;border-radius:50%;background:#fff;box-shadow:0 0 0 0 rgba(255,255,255,.7);
  animation:liveDot 1.6s ease-out infinite;}
@keyframes liveDot{0%{box-shadow:0 0 0 0 rgba(255,255,255,.7);}70%{box-shadow:0 0 0 10px rgba(255,255,255,0);}100%{box-shadow:0 0 0 0 rgba(255,255,255,0);}}
.checkin-hero__title{font-family:var(--serif);font-size:1.9rem;font-weight:700;margin:0 0 4px;color:#fff;position:relative;}
.checkin-hero__meta{color:rgba(255,255,255,.9);margin-bottom:20px;position:relative;}
.checkin-hero__btn{position:relative;font:inherit;font-weight:800;font-size:1.02rem;color:#065053;cursor:pointer;border:none;
  padding:15px 30px;border-radius:var(--r-pill);background:#fff;box-shadow:0 10px 24px rgba(36,53,54,.22);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur);}
.checkin-hero__btn:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 16px 34px rgba(36,53,54,.28);}
.checkin-hero__btn:active{transform:scale(.98);}
.checkin-hero__btn .checkin-btn__pulse{background:#fff;}

.phase-cards{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;}
@media(min-width:640px){.phase-cards{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}}
.phase-tile{display:flex;align-items:center;gap:16px;transition:transform var(--dur),box-shadow var(--dur),border-color var(--dur);}
.phase-tile:not(.is-locked):hover{transform:translateY(-3px);box-shadow:var(--sh-md);border-color:var(--mist);}
.phase-tile.is-locked{opacity:.75;border-style:dashed;}
.phase-tile__lock{width:60px;height:60px;border-radius:50%;background:#F1ECE0;display:flex;align-items:center;justify-content:center;font-size:1.5rem;flex:0 0 auto;}
.phase-tile__ring{flex:0 0 auto;}
.phase-tile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
.phase-tile__go{color:var(--primary);font-weight:700;font-size:.88rem;flex:0 0 auto;}

/* ==========================================================================
   HERO TITLE SPLIT (English line + Arabic line) + COMPACT LIVE CHECK-IN
   ========================================================================== */
.hero__en{display:block;}
.hero__ar{display:block;font-family:"Tajawal",sans-serif;font-weight:800;color:var(--primary);
  line-height:1.15;margin-top:8px;font-size:1.04em;}

/* Compact "happening now" card — the button is a CIRCLE, not a full-width bar */
.checkin-live{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;
  background:linear-gradient(135deg,#F1FAF5,#E7F4EE);border:1px solid #CDE7DB;border-radius:20px;
  padding:18px 22px;margin-bottom:22px;box-shadow:0 6px 20px rgba(47,143,111,.14);}
.checkin-live__info{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1 1 220px;}
.checkin-live__badge{display:inline-flex;align-items:center;gap:8px;font-size:.7rem;font-weight:800;
  letter-spacing:.07em;text-transform:uppercase;color:#2F8F6F;}
.checkin-live__dot{width:9px;height:9px;border-radius:50%;background:#2F8F6F;flex:0 0 auto;
  animation:liveDotG 1.6s ease-out infinite;}
@keyframes liveDotG{0%{box-shadow:0 0 0 0 rgba(47,143,111,.55);}70%{box-shadow:0 0 0 9px rgba(47,143,111,0);}100%{box-shadow:0 0 0 0 rgba(47,143,111,0);}}
.checkin-live__title{font-family:var(--serif);font-weight:700;font-size:1.45rem;color:var(--text);margin:2px 0 0;}
.checkin-live__meta{color:var(--muted);font-size:.9rem;}
.checkin-live__form{flex:0 0 auto;}

.checkin-round{position:relative;isolation:isolate;width:116px;height:116px;border-radius:50%;border:none;cursor:pointer;
  background:linear-gradient(140deg,#065053,#2F8F6F);color:#fff;font-family:inherit;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  box-shadow:0 12px 28px rgba(6,80,83,.4);transition:transform var(--dur) var(--ease),box-shadow var(--dur);}
.checkin-round:hover{transform:scale(1.06);box-shadow:0 16px 36px rgba(6,80,83,.5);}
.checkin-round:active{transform:scale(.96);}
.checkin-round__icon{font-size:1.8rem;line-height:1;}
.checkin-round__label{font-size:.82rem;font-weight:800;letter-spacing:.02em;}
.checkin-round__pulse{position:absolute;inset:0;border-radius:50%;background:#2F8F6F;z-index:-1;
  animation:checkinPulse 1.9s var(--ease) infinite;}
@media(max-width:520px){ .checkin-live{justify-content:center;text-align:center;} .checkin-live__info{flex-basis:100%;align-items:center;} }
@media(prefers-reduced-motion:reduce){ .checkin-round__pulse{animation:none;display:none;} .checkin-live__dot{animation:none;} }

/* Attendance 'excused' segment (new 4-status model) + role pill in topbar */
.seg__btn--excused:has(input:checked),.seg__btn--excused.is-on{background:var(--mist);color:#0F2A2B;}
.topbar__user{display:inline-flex;align-items:center;gap:8px;font-size:.9rem;font-weight:600;color:var(--text);white-space:nowrap;}
.role-pill{font-size:.66rem;font-weight:800;text-transform:uppercase;letter-spacing:.04em;
  background:var(--gold-soft);color:#8A5A12;padding:2px 8px;border-radius:999px;}
@media(max-width:720px){ .topbar__user{display:none;} }

/* Slim role nav — bottom bar spreads its few items evenly on mobile. */
.bottomnav{-webkit-overflow-scrolling:touch;}
.bottomnav__link{padding:8px 6px;font-size:.72rem;}

/* ==========================================================================
   MOBILE POLISH — small-screen refinements (base is mobile-first already)
   ========================================================================== */
@media(max-width:600px){
  h1{font-size:1.62rem;} h2{font-size:1.25rem;}
  .container{padding:20px 14px 92px;}
  .card{padding:18px;} .section-card{padding:18px;}
  .row-between{gap:var(--sp-2);}
  .page-head.row-between{align-items:flex-start;}
  /* Dense list rows: keep name+avatar on line 1, actions drop to a tidy line 2 */
  .list__actions{width:100%;justify-content:flex-start;margin-top:6px;}
  /* Modals hug the screen a little tighter */
  .modal-overlay{padding:12px;}
  .modal{max-height:92dvh;}
  .modal__head{padding:14px 18px;} .modal__body{padding:18px;}
}
@media(max-width:380px){
  .drill-card{gap:12px;padding:14px;}
  .drill-card__num{width:40px;height:40px;font-size:1rem;}
  .stat__value{font-size:1.7rem;}
  .btn{padding:9px 14px;} .btn--lg{padding:11px 16px;}
}

/* ==========================================================================
   LANDING PAGE — editorial, brand-forward. Deep teal + gold on warm ivory.
   Every block here is landing-only (body.landing); nothing below is shared
   with the app shell.
   ========================================================================== */
body.landing{
  --lp-ink:#0C2B2D;                 /* darker than --text, for display type */
  --lp-line:rgba(6,80,83,.10);      /* hairline that reads on ivory */
  --lp-max:1200px;
  --lp-gold-line:linear-gradient(90deg,transparent,var(--gold),transparent);
  --lp-glass:rgba(255,255,255,.72);
  background:var(--bg);color:var(--text);
}
.landing main{overflow-x:clip;}
.landing ::selection{background:var(--gold-soft);color:var(--gold-dark);}

/* Arabic geometric weave — reused by the hero, stats band and CTA. */
body.landing{
  --lp-weave:
    repeating-linear-gradient(45deg,var(--lp-weave-c) 0 1px,transparent 1px 22px),
    repeating-linear-gradient(-45deg,var(--lp-weave-c) 0 1px,transparent 1px 22px);
}

/* ---- Eyebrow + star ---- */
.landing .eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:.7rem;font-weight:800;
  letter-spacing:.14em;text-transform:uppercase;color:var(--gold-dark);
  background:linear-gradient(135deg,#FBF1DC,#F7E7C6);border:1px solid rgba(201,132,26,.22);
  padding:7px 15px 7px 12px;border-radius:var(--r-pill);margin:0 0 20px;
  box-shadow:0 2px 10px rgba(201,132,26,.10);}
.landing .brand-star{width:12px;height:12px;}
.brand-star--lg{width:26px;height:26px;margin-bottom:18px;
  filter:drop-shadow(0 0 14px rgba(217,168,92,.65));}

/* ---- Nav ---- */
.lp-nav{position:fixed;top:0;left:0;right:0;z-index:50;border-bottom:1px solid transparent;
  transition:background var(--dur) var(--ease),box-shadow var(--dur) var(--ease),border-color var(--dur);}
.lp-nav.is-scrolled{background:#FBF8F1;
  border-bottom-color:var(--lp-line);box-shadow:0 10px 30px -14px rgba(6,80,83,.42);}
.lp-nav__inner{max-width:var(--lp-max);margin:0 auto;padding:16px 24px;display:grid;
  grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:24px;}
.lp-nav .brand{grid-column:1;justify-self:start;transition:transform var(--dur) var(--ease);}
.lp-nav .brand:hover{transform:translateY(-1px);}
.lp-nav .brand__word{font-size:1.35rem;letter-spacing:.01em;}
.lp-nav__links{display:none;gap:2px;grid-column:2;justify-self:center;}
.lp-nav__links a{position:relative;padding:8px 14px;border-radius:var(--r-pill);font-size:.9rem;font-weight:600;
  color:var(--muted);transition:color var(--dur);}
.lp-nav__links a::after{content:"";position:absolute;left:14px;right:14px;bottom:2px;height:2px;border-radius:2px;
  background:var(--gold);transform:scaleX(0);transform-origin:left;transition:transform var(--dur) var(--ease);}
.lp-nav__links a:hover,.lp-nav__links a.is-active{color:var(--primary);}
.lp-nav__links a:hover::after,.lp-nav__links a.is-active::after{transform:scaleX(1);}
.lp-nav__cta{grid-column:3;justify-self:end;}

/* ---- Buttons (landing scale) ---- */
.landing .btn--lg{padding:15px 30px;font-size:1rem;font-weight:700;}
.landing .btn--primary{position:relative;overflow:hidden;
  background:linear-gradient(135deg,#065053,#0A7E82 55%,#0F9A93);
  box-shadow:0 10px 26px -8px rgba(6,80,83,.55),inset 0 1px 0 rgba(255,255,255,.18);}
.landing .btn--primary::after{content:"";position:absolute;inset:0;
  background:linear-gradient(105deg,transparent 35%,rgba(255,255,255,.32) 50%,transparent 65%);
  transform:translateX(-120%);transition:transform .7s var(--ease);}
.landing .btn--primary:hover::after{transform:translateX(120%);}
.landing .btn--primary:hover:not(:disabled){transform:translateY(-2px);
  box-shadow:0 18px 38px -12px rgba(6,80,83,.6),inset 0 1px 0 rgba(255,255,255,.22);}
.landing .btn--ghost{border-color:rgba(6,80,83,.18);background:rgba(255,255,255,.6);color:var(--primary);}
.landing .btn--ghost:hover:not(:disabled){background:#fff;border-color:var(--primary);transform:translateY(-2px);
  box-shadow:0 12px 26px -14px rgba(6,80,83,.5);}

/* ==========================  HERO  ========================================= */
.hero{position:relative;padding:84px 24px 96px;overflow:hidden;isolation:isolate;}
.hero__bg{position:absolute;inset:0;z-index:-2;
  background:
    radial-gradient(1100px 620px at 12% -12%,rgba(6,80,83,.16),transparent 62%),
    radial-gradient(900px 520px at 96% 4%,rgba(201,132,26,.16),transparent 58%),
    radial-gradient(900px 620px at 50% 118%,rgba(146,176,178,.20),transparent 62%),
    linear-gradient(180deg,#FDFBF6,var(--bg));}
.hero__bg::after{content:"";position:absolute;inset:0;--lp-weave-c:rgba(6,80,83,.055);
  background:var(--lp-weave);
  -webkit-mask-image:radial-gradient(80% 70% at 50% 35%,#000,transparent 78%);
          mask-image:radial-gradient(80% 70% at 50% 35%,#000,transparent 78%);}
/* Soft drifting orbs behind the content */
.hero__orbs{position:absolute;inset:0;z-index:-1;pointer-events:none;}
.hero__orbs i{position:absolute;border-radius:50%;filter:blur(60px);opacity:.5;
  animation:lpFloat 16s var(--ease) infinite alternate;}
.hero__orbs i:nth-child(1){width:340px;height:340px;top:-60px;left:4%;background:rgba(10,126,130,.35);}
.hero__orbs i:nth-child(2){width:300px;height:300px;bottom:-40px;left:38%;background:rgba(201,132,26,.26);animation-delay:-5s;}
.hero__orbs i:nth-child(3){width:380px;height:380px;top:10%;right:-60px;background:rgba(146,176,178,.42);animation-delay:-9s;}
@keyframes lpFloat{from{transform:translate3d(0,0,0) scale(1);}to{transform:translate3d(0,-28px,0) scale(1.08);}}

.hero__inner{position:relative;z-index:1;max-width:var(--lp-max);margin:0 auto;display:grid;
  grid-template-columns:minmax(0,1fr);gap:48px;align-items:center;}
.hero__title{font-family:var(--serif);font-size:clamp(2.9rem,6.4vw,4.6rem);font-weight:700;line-height:1.02;
  color:var(--lp-ink);margin:0 0 20px;}
.hero__en{display:block;}
.hero__ar{display:block;font-family:"Tajawal",sans-serif;font-weight:800;line-height:1.25;margin-top:6px;
  font-size:1.02em;background:linear-gradient(100deg,#C9841A 0%,#0A8B90 38%,#065053 78%);
  -webkit-background-clip:text;background-clip:text;color:transparent;}
.hero__lead{font-size:1.16rem;line-height:1.7;color:var(--muted);max-width:44ch;margin-bottom:32px;}
.hero__actions{display:flex;flex-wrap:wrap;gap:14px;}

/* Fact strip under the CTAs — pulled from the stats block. */
.hero__facts{list-style:none;display:flex;flex-wrap:wrap;gap:32px;margin:40px 0 0;padding:28px 0 0;
  border-top:1px solid var(--lp-line);}
.hero__facts li{display:flex;flex-direction:column;gap:2px;}
.hero__facts b{font-family:var(--serif);font-size:1.9rem;font-weight:700;line-height:1;color:var(--primary);}
.hero__facts span{font-size:.72rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);}

/* Logo medallion */
.hero__art{display:flex;justify-content:center;}
.medallion{position:relative;display:grid;place-items:center;width:min(440px,88vw);aspect-ratio:1;}
.medallion__halo{position:absolute;inset:6%;border-radius:50%;
  background:radial-gradient(circle at 50% 42%,rgba(255,255,255,.95),rgba(255,255,255,.6) 42%,transparent 68%);
  box-shadow:0 40px 80px -40px rgba(6,80,83,.5);}
.medallion__ring{position:absolute;border-radius:50%;}
.medallion__ring--outer{inset:0;border:1px solid rgba(6,80,83,.16);
  background:conic-gradient(from 0deg,transparent 0 82%,rgba(201,132,26,.55) 88%,transparent 96%);
  -webkit-mask:radial-gradient(circle,transparent 0 calc(50% - 2px),#000 calc(50% - 2px));
          mask:radial-gradient(circle,transparent 0 calc(50% - 2px),#000 calc(50% - 2px));
  animation:lpSpin 22s linear infinite;}
.medallion__ring--inner{inset:11%;border:1px dashed rgba(201,132,26,.35);animation:lpSpin 40s linear infinite reverse;}
@keyframes lpSpin{to{transform:rotate(360deg);}}
.hero__img{position:relative;width:72%;filter:drop-shadow(0 24px 40px rgba(6,80,83,.28));
  animation:lpBob 7s var(--ease) infinite alternate;}
@keyframes lpBob{from{transform:translateY(-6px);}to{transform:translateY(10px);}}

/* Scroll cue */
.hero__scroll{position:absolute;left:50%;bottom:22px;transform:translateX(-50%);z-index:2;
  display:none;place-items:center;width:26px;height:42px;}
.hero__scroll-mouse{width:24px;height:38px;border:1.5px solid rgba(6,80,83,.3);border-radius:14px;position:relative;
  transition:border-color var(--dur);}
.hero__scroll:hover .hero__scroll-mouse{border-color:var(--primary);}
.hero__scroll-mouse::after{content:"";position:absolute;left:50%;top:8px;width:3px;height:7px;border-radius:2px;
  background:var(--gold);transform:translateX(-50%);animation:lpWheel 1.8s var(--ease) infinite;}
@keyframes lpWheel{0%{opacity:0;transform:translate(-50%,-4px);}40%{opacity:1;}100%{opacity:0;transform:translate(-50%,12px);}}

/* ==========================  SECTIONS  ===================================== */
.lp-section{position:relative;padding:96px 24px;}
.lp-section:nth-of-type(even){background:linear-gradient(180deg,#FDFAF3,#F6F1E4);}
.lp-section:nth-of-type(even)::before,.lp-section:nth-of-type(even)::after{content:"";position:absolute;
  left:0;right:0;height:1px;background:var(--lp-gold-line);opacity:.35;}
.lp-section:nth-of-type(even)::before{top:0;} .lp-section:nth-of-type(even)::after{bottom:0;}
.lp-wrap{max-width:var(--lp-max);margin:0 auto;}
.lp-wrap--narrow{max-width:820px;}
.lp-head{text-align:center;max-width:660px;margin:0 auto 56px;display:flex;flex-direction:column;align-items:center;}
.lp-title{font-family:var(--serif);font-size:clamp(2.1rem,4vw,3.1rem);font-weight:700;line-height:1.12;
  color:var(--lp-ink);margin:0 0 14px;}
.lp-head .lp-title::after{content:"";display:block;width:56px;height:3px;border-radius:2px;margin:18px auto 0;
  background:linear-gradient(90deg,var(--primary),var(--gold));}
.lp-sub,.lp-body{color:var(--muted);font-size:1.08rem;line-height:1.75;}
.lp-body{max-width:52ch;margin-top:8px;}

/* ---- Split (text + image) ---- */
.lp-split{max-width:var(--lp-max);margin:0 auto;display:grid;grid-template-columns:minmax(0,1fr);gap:44px;align-items:center;}
.lp-split__text .lp-title{text-align:left;}
.lp-frame{position:relative;border-radius:24px;padding:10px;background:var(--lp-glass);
  border:1px solid rgba(255,255,255,.9);backdrop-filter:blur(6px);
  box-shadow:0 40px 80px -40px rgba(6,80,83,.45),0 2px 8px rgba(36,53,54,.06);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);}
.lp-frame::before{content:"";position:absolute;inset:-14px;z-index:-1;border-radius:32px;
  background:radial-gradient(60% 60% at 30% 20%,rgba(10,126,130,.25),transparent 70%),
             radial-gradient(60% 60% at 80% 90%,rgba(201,132,26,.25),transparent 70%);
  filter:blur(22px);}
.lp-frame::after{content:"";position:absolute;inset:0;border-radius:24px;padding:1px;pointer-events:none;
  background:linear-gradient(140deg,rgba(201,132,26,.65),rgba(255,255,255,0) 42%,rgba(255,255,255,0) 58%,rgba(6,80,83,.45));
  -webkit-mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
          mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
  -webkit-mask-composite:xor;mask-composite:exclude;}
.lp-frame:hover{transform:translateY(-6px);box-shadow:0 56px 100px -46px rgba(6,80,83,.55);}
.lp-frame img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:16px;display:block;}
.lp-split__placeholder{aspect-ratio:4/3;border-radius:16px;background:var(--grad-primary);position:relative;overflow:hidden;
  display:flex;align-items:center;justify-content:center;}
.lp-split__placeholder::before{content:"";position:absolute;inset:0;
  background:radial-gradient(circle at 30% 20%,rgba(255,255,255,.35),transparent 45%);}
.lp-split__placeholder span{font-family:var(--serif);font-weight:700;font-size:2.4rem;color:rgba(255,255,255,.92);}

/* ---- Feature grid ---- */
.feature-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:24px;}
.feature{position:relative;overflow:hidden;background:var(--surface);border:1px solid var(--border);
  border-radius:20px;padding:32px 28px;box-shadow:0 2px 4px rgba(36,53,54,.04);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease);}
.feature::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;
  background:linear-gradient(90deg,var(--primary),var(--gold));transform:scaleX(0);transform-origin:left;
  transition:transform .4s var(--ease);}
.feature::after{content:"";position:absolute;right:-40px;top:-40px;width:140px;height:140px;border-radius:50%;
  background:radial-gradient(circle,rgba(201,132,26,.14),transparent 70%);opacity:0;transition:opacity .4s var(--ease);}
.feature:hover{transform:translateY(-8px);border-color:rgba(6,80,83,.18);
  box-shadow:0 30px 60px -30px rgba(6,80,83,.4);}
.feature:hover::before{transform:scaleX(1);}
.feature:hover::after{opacity:1;}
.feature__icon{position:relative;display:inline-flex;align-items:center;justify-content:center;width:58px;height:58px;
  border-radius:18px;font-size:1.95rem;line-height:1;margin-bottom:20px;
  background:linear-gradient(140deg,var(--primary-soft),#F5E9CF);
  box-shadow:inset 0 1px 0 #fff,0 8px 20px -12px rgba(6,80,83,.55);
  transition:transform .35s var(--ease);}
.feature:hover .feature__icon{transform:rotate(-6deg) scale(1.06);}
.feature h3{font-size:1.15rem;color:var(--lp-ink);margin-bottom:8px;}
.feature p{line-height:1.65;}
.reveal.is-visible .feature{animation:lpRise .6s var(--ease) both;animation-delay:var(--d,0ms);}
@keyframes lpRise{from{opacity:0;transform:translateY(18px);}to{opacity:1;transform:none;}}

/* ---- Steps (timeline) ---- */
.steps{list-style:none;margin:0 auto;padding:0;max-width:760px;position:relative;
  display:flex;flex-direction:column;gap:16px;}
.steps::before{content:"";position:absolute;left:30px;top:34px;bottom:34px;width:2px;
  background:linear-gradient(180deg,var(--primary),rgba(201,132,26,.5));opacity:.35;}
.step{position:relative;display:flex;gap:22px;padding:24px 26px;background:var(--surface);
  border:1px solid var(--border);border-radius:20px;box-shadow:0 2px 4px rgba(36,53,54,.04);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease);}
.step:hover{transform:translateX(6px);border-color:rgba(6,80,83,.18);
  box-shadow:0 26px 50px -30px rgba(6,80,83,.4);}
.step__num{flex:0 0 auto;width:44px;height:44px;border-radius:50%;background:var(--grad-primary);color:#fff;
  font-family:var(--serif);font-weight:700;font-size:1.3rem;display:flex;align-items:center;justify-content:center;
  box-shadow:0 10px 22px -10px rgba(6,80,83,.8),0 0 0 5px var(--bg),0 0 0 6px rgba(6,80,83,.12);}
.lp-section:nth-of-type(even) .step__num{box-shadow:0 10px 22px -10px rgba(6,80,83,.8),0 0 0 5px #F9F4E9,0 0 0 6px rgba(6,80,83,.12);}
.step__body h3{display:flex;align-items:center;gap:9px;font-size:1.15rem;color:var(--lp-ink);margin-bottom:6px;}
.step__icon{font-size:1.05rem;line-height:1;}
.step__body p{line-height:1.65;}

/* ---- Bullet list ---- */
.bullets{list-style:none;margin:0 auto;padding:0;display:flex;flex-direction:column;gap:14px;}
.bullet{display:flex;gap:18px;align-items:flex-start;padding:22px 24px;background:var(--surface);
  border:1px solid var(--border);border-radius:18px;box-shadow:0 2px 4px rgba(36,53,54,.04);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease);}
.bullet:hover{transform:translateY(-4px);border-color:rgba(201,132,26,.35);
  box-shadow:0 26px 50px -30px rgba(201,132,26,.5);}
.bullet__mark{flex:0 0 auto;width:40px;height:40px;border-radius:14px;font-size:1.15rem;
  background:linear-gradient(140deg,#F7E7C6,#F1D8A6);color:var(--gold-dark);
  display:flex;align-items:center;justify-content:center;font-weight:700;
  box-shadow:inset 0 1px 0 #fff,0 8px 18px -12px rgba(201,132,26,.9);}
.bullet__body b{display:block;font-size:1.06rem;color:var(--lp-ink);}
.bullet__body p{margin-top:4px;line-height:1.6;}

/* ==========================  STATS BAND  =================================== */
.lp-stats{position:relative;overflow:hidden;padding:64px 24px;color:#fff;
  background:linear-gradient(135deg,#04383A,#065053 40%,#0A7E82);}
.lp-stats__pattern{position:absolute;inset:0;--lp-weave-c:rgba(255,255,255,.07);background:var(--lp-weave);
  -webkit-mask-image:radial-gradient(90% 80% at 50% 0%,#000,transparent 85%);
          mask-image:radial-gradient(90% 80% at 50% 0%,#000,transparent 85%);}
.lp-stats::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(700px 300px at 85% -30%,rgba(217,168,92,.22),transparent 60%),
             radial-gradient(600px 260px at 10% 120%,rgba(255,255,255,.10),transparent 60%);}
.lp-stats .lp-wrap{position:relative;z-index:1;}
.lp-stats__title{display:flex;align-items:center;justify-content:center;gap:10px;text-align:center;color:#fff;
  font-family:var(--serif);font-weight:700;font-size:1.6rem;margin:0 0 36px;}
.lp-stats__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:28px 20px;max-width:960px;margin:0 auto;}
.lp-stat{position:relative;text-align:center;display:flex;flex-direction:column;gap:8px;padding:0 12px;}
.lp-stat + .lp-stat::before{content:"";position:absolute;left:0;top:12%;bottom:12%;width:1px;
  background:linear-gradient(180deg,transparent,rgba(255,255,255,.25),transparent);}
.lp-stat__num{font-family:var(--serif);font-weight:700;font-size:clamp(2.4rem,4vw,3.2rem);line-height:1;
  background:linear-gradient(160deg,#fff 40%,#EBC98A);-webkit-background-clip:text;background-clip:text;color:transparent;}
.lp-stat__label{font-size:.74rem;font-weight:700;color:rgba(255,255,255,.72);text-transform:uppercase;letter-spacing:.12em;}

/* ==========================  CTA BAND  ===================================== */
.cta-band{position:relative;overflow:hidden;padding:104px 24px;
  background:linear-gradient(135deg,#04383A,#065053 45%,#0A7E82);}
.cta-band__pattern{position:absolute;inset:0;--lp-weave-c:rgba(255,255,255,.07);background:var(--lp-weave);
  -webkit-mask-image:radial-gradient(70% 70% at 50% 50%,#000,transparent 80%);
          mask-image:radial-gradient(70% 70% at 50% 50%,#000,transparent 80%);}
.cta-band::before{content:"";position:absolute;inset:0;
  background:radial-gradient(700px 340px at 50% -20%,rgba(217,168,92,.3),transparent 62%),
             radial-gradient(600px 320px at 50% 120%,rgba(255,255,255,.12),transparent 60%);}
.cta-band__inner{position:relative;z-index:1;max-width:760px;margin:0 auto;text-align:center;color:#fff;
  display:flex;flex-direction:column;align-items:center;}
.cta-band__inner h2{font-family:var(--serif);font-size:clamp(2.2rem,4.6vw,3.3rem);font-weight:700;line-height:1.12;
  margin:0 0 16px;color:#fff;}
.cta-band__inner p{font-size:1.12rem;line-height:1.7;color:rgba(255,255,255,.86);margin:0 0 32px;max-width:52ch;}
.landing .cta-band .btn--white{background:#fff;color:var(--primary);font-weight:700;
  box-shadow:0 18px 40px -16px rgba(0,0,0,.55);transition:transform var(--dur) var(--ease),box-shadow var(--dur);}
.landing .cta-band .btn--white:hover{transform:translateY(-2px);background:#fff;
  box-shadow:0 26px 54px -18px rgba(0,0,0,.6);}

/* ==========================  FOOTER  ======================================= */
.lp-footer{background:linear-gradient(180deg,#FDFBF6,#F6F1E4);border-top:1px solid var(--lp-line);}
.lp-footer__inner{max-width:var(--lp-max);margin:0 auto;padding:52px 24px 36px;display:flex;align-items:flex-start;
  justify-content:space-between;gap:32px;flex-wrap:wrap;}
.lp-footer__brand{display:flex;flex-direction:column;gap:10px;max-width:34ch;}
.lp-footer__brand p{font-size:.9rem;line-height:1.6;}
.lp-footer .brand__word{font-size:1.25rem;}
.lp-footer__links{display:flex;flex-direction:column;gap:10px;}
.lp-footer__links a{font-size:.9rem;font-weight:600;color:var(--muted);transition:color var(--dur);}
.lp-footer__links a:hover{color:var(--primary);}
.lp-footer__bar{border-top:1px solid var(--lp-line);}
.lp-footer__bar p{max-width:var(--lp-max);margin:0 auto;padding:18px 24px;text-align:center;}

/* ---- Reveal on scroll ---- */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .7s var(--ease),transform .7s var(--ease);}
.reveal.is-visible{opacity:1;transform:none;}
.hero__art.reveal{transform:translateY(26px) scale(.96);}
.hero__art.reveal.is-visible{transform:none;}

/* ---- Landing: tablet / desktop ---- */
@media(min-width:860px){
  .lp-nav__links{display:flex;}
  .hero{padding:96px 24px 56px;min-height:100svh;display:flex;align-items:center;}
  .hero__inner{grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);gap:56px;width:100%;}
  .hero__scroll{display:grid;}
  .lp-split{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:64px;}
  .lp-split--img-right .lp-split__media{order:2;}
  .lp-stats__grid{grid-template-columns:repeat(4,minmax(0,1fr));}
}

/* ---- Landing: mobile ---- */
@media(max-width:600px){
  .hero{padding:92px 20px 64px;} /* extra top clears the fixed overlay nav */
  .hero__inner{gap:36px;}
  .hero__art{order:-1;}
  .medallion{width:min(300px,72vw);}
  .hero__lead{font-size:1.05rem;margin-bottom:26px;}
  .hero__actions .btn--lg{flex:1 1 100%;}
  .hero__facts{gap:20px 28px;margin-top:32px;padding-top:22px;}
  .hero__facts b{font-size:1.6rem;}
  .lp-section{padding:64px 20px;}
  .lp-head{margin-bottom:38px;}
  .cta-band{padding:72px 20px;}
  .lp-stats{padding:48px 20px;}
  .lp-stats__grid{gap:26px 12px;}
  .lp-stat + .lp-stat:nth-child(odd)::before{display:none;}
  .feature{padding:26px 22px;}
  .step{padding:20px;gap:16px;}
  .steps::before{left:26px;}
  .bullet{padding:18px;gap:14px;}
  .lp-footer__inner{flex-direction:column;gap:24px;padding:40px 20px 28px;}
}

/* ---- Reduced motion: drop the ambient animation, keep the design ---- */
@media(prefers-reduced-motion:reduce){
  .hero__orbs i,.medallion__ring,.hero__img,.hero__scroll-mouse::after{animation:none;}
  .reveal.is-visible .feature{animation:none;}
}

/* ==========================================================================
   STUDENT DASHBOARD — mini month calendar (read-only widget)
   Lives in the narrow right-hand .dash-col, so unlike .cal-grid it has NO
   min-width: cells shrink instead of forcing a horizontal scrollbar. Events
   are reduced to one coloured dot per type; full text lives in the cell's
   title= / the dot's aria-label, and in the "What's next" list underneath.
   ========================================================================== */
.cal--mini{--mini-cell:34px;}

.mini-cal__month{font-family:var(--serif);font-size:1.05rem;font-weight:700;color:var(--primary);
  text-align:center;margin:0 0 var(--sp-3);}

.mini-cal__grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:var(--sp-1);}

.mini-cal__weekday{font-size:.64rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);text-align:center;padding-bottom:var(--sp-1);}
.mini-cal__weekday abbr{text-decoration:none;border:none;cursor:help;}

.mini-cal__cell{min-height:var(--mini-cell);border:1px solid var(--border);border-radius:8px;
  background:var(--surface);padding:3px 2px 4px;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:2px;}
.mini-cal__cell--empty{border:none;background:transparent;}
.mini-cal__cell.has-events{border-color:var(--mist);}
.mini-cal__cell.is-today{border-color:transparent;box-shadow:0 0 0 2px var(--primary);}

.mini-cal__day{font-size:.72rem;font-weight:700;color:var(--muted);line-height:1.25;
  width:19px;height:19px;display:flex;align-items:center;justify-content:center;}
.mini-cal__cell.has-events .mini-cal__day{color:var(--text);}
.mini-cal__cell.is-today .mini-cal__day{background:var(--primary);color:#fff;border-radius:50%;}

.mini-cal__dots{display:flex;align-items:center;justify-content:center;gap:2px;min-height:6px;}

/* The one shared dot. Colour comes from --ec set inline (matches the .cal-event
   convention already used by the full calendar). */
.mini-cal__dot{width:6px;height:6px;border-radius:50%;background:var(--ec,var(--primary));flex:0 0 auto;}

.mini-cal__legend{list-style:none;margin:var(--sp-3) 0 0;padding:0;
  display:flex;flex-wrap:wrap;gap:var(--sp-1) var(--sp-3);justify-content:center;}
.mini-cal__legend li{display:inline-flex;align-items:center;gap:5px;
  font-size:.72rem;font-weight:600;color:var(--muted);}

.mini-cal__next{margin-top:var(--sp-4);padding-top:var(--sp-3);border-top:1px solid var(--border);}
.mini-cal__next-title{font-size:.95rem;margin:0 0 var(--sp-2);}

.mini-cal__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-2);}
.mini-cal__item{display:flex;align-items:flex-start;gap:var(--sp-2);
  border-left:3px solid var(--ec,var(--primary));border-radius:8px;padding:6px var(--sp-2);
  background:color-mix(in srgb, var(--ec,var(--primary)) 9%, var(--surface));}
.mini-cal__item .mini-cal__dot{width:7px;height:7px;margin-top:6px;}
.mini-cal__item-main{display:flex;flex-direction:column;gap:1px;min-width:0;}
.mini-cal__item-title{font-size:.84rem;font-weight:700;line-height:1.35;
  overflow-wrap:anywhere;}
.mini-cal__item-when{font-size:.74rem;color:var(--muted);}

/* Below 900px .dash-grid collapses to one column, so the widget gets the full
   page width — give the cells a little more room to breathe. */
@media(max-width:900px){ .cal--mini{--mini-cell:40px;} }
@media(max-width:380px){
  .cal--mini{--mini-cell:32px;}
  .mini-cal__grid{gap:3px;}
  .mini-cal__legend{gap:var(--sp-1) var(--sp-2);}
}

/* ===========================================================================
   Session page + the one-week session lock.
   Namespaces: .session-*  (the session detail page)
               .lock-*     (locked state, shared by the phase list + lock page)
   Plus two modifiers that hang off the existing .sess-card block:
               .sess-card--locked, and .sess-card__num.lock-num
   Nothing else is touched.
   =========================================================================== */

/* ---- Session page head ---------------------------------------------------- */
.session-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.session-head__title {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
}
/* The session number, as a small numbered chip beside the title. */
.session-head__num {
  display: inline-grid;
  place-items: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 .5rem;
  border-radius: 999px;
  background: var(--brand-soft, rgba(6, 80, 83, .10));
  color: var(--brand, #065053);
  font-size: .95rem;
  font-weight: 700;
  line-height: 1;
}
.session-head__when {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.session-when {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  white-space: nowrap;
}

/* ---- Section jump nav (anchors, not tabs — the page is one page) ---------- */
.session-jump {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin: 4px 0 16px;
}

/* ---- Sections ------------------------------------------------------------- */
.session-section {
  margin-bottom: 18px;
  scroll-margin-top: 84px;   /* so an anchor jump clears the sticky topbar */
}
.session-section__title {
  margin: 0 0 10px;
  font-size: 1.05rem;
}
.session-objective {
  margin: 0;
  white-space: pre-wrap;
}
.session-attend {
  margin-top: 10px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.session-attend p { margin: 0; }

/* A one-off inline error (e.g. "attendance isn't open right now"). */
.session-alert {
  margin-bottom: 14px;
  padding: 12px 14px;
  border-left: 3px solid var(--warn, #b8860b);
  font-weight: 600;
}

/* ---- The lock ------------------------------------------------------------- */
/* Locked session in the phase list: same shape as .sess-card, but flat, dimmed
   and inert — it is a <div>, not an <a>, so it cannot be clicked at all. */
.sess-card--locked {
  cursor: not-allowed;
  opacity: .72;
  background: var(--surface-2, rgba(0, 0, 0, .02));
  box-shadow: none;
}
.sess-card--locked:hover { transform: none; }
.sess-card--locked b { color: var(--muted, #6b7280); }

.sess-card__num.lock-num {
  background: transparent;
  display: inline-grid;
  place-items: center;
}
.sess-card__num.lock-num .ico {
  width: 1.15rem;
  height: 1.15rem;
  opacity: .75;
}

/* "Unlocks in 3 days" */
.lock-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--surface-2, rgba(0, 0, 0, .05));
  color: var(--muted, #6b7280);
  font-size: .78rem;
  font-weight: 700;
  white-space: nowrap;
}

/* The full-page 403 a student gets if they type a locked session's URL. */
.lock-panel {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 22px;
  max-width: 640px;
}
.lock-panel__icon { flex: 0 0 auto; }
.lock-panel__icon .ico { opacity: .8; }
.lock-panel__body { flex: 1 1 auto; }
.lock-panel__title {
  display: block;
  margin-bottom: 4px;
  font-size: 1.05rem;
}
.lock-panel p { margin: 0 0 4px; }

@media (max-width: 640px) {
  .session-head__when { width: 100%; }
  .lock-badge { font-size: .72rem; }
}

/* ==========================================================================
   CALENDAR — additions for the full month view (cohort colours, filters, +)
   Merge into public/css/styles.css. Everything here is NEW; the existing
   .cal / .cal-chip / .cal-cell / .cal-event rules are reused as-is.
   ========================================================================== */

/* --- The "+ Schedule" button ---------------------------------------------- */
.cal-add{display:inline-flex;align-items:center;gap:8px;}
.cal-add__plus{display:inline-flex;align-items:center;justify-content:center;
  width:20px;height:20px;border-radius:50%;background:rgba(255,255,255,.22);
  font-weight:800;font-size:1rem;line-height:1;}

/* --- Success / error banner (this app has no flash middleware; the state
       comes back on the query string after a POST-redirect-GET) ------------- */
.cal-flash{display:flex;align-items:center;gap:8px;font-size:.9rem;font-weight:600;
  padding:12px 16px;border-radius:12px;margin:0 0 var(--sp-4);border:1px solid transparent;}
.cal-flash--ok{color:#0B5F44;background:#E8F5EF;border-color:#BFE3D4;}
.cal-flash--err{color:#8A2B20;background:#FBEBE8;border-color:#F0C9C2;}

/* --- Month-nav row -------------------------------------------------------- */
.cal-head.row-between{margin-bottom:0;}
.cal-head .back-link{margin-bottom:0;}
.cal-today-row{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:center;margin:.25rem 0 .25rem;}

/* --- Filter bars ---------------------------------------------------------- */
.cal-filter{align-items:center;}
.cal-filter__label{font-size:.7rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);margin-right:2px;}
.cal-filter--phase{margin:0 20px;padding:12px 0 16px;border-top:1px dashed var(--border);}
.cal-chip[hidden]{display:none;}
/* .cal-filter sets display:flex, which beats the UA's [hidden]{display:none} —
   without this the JS's `phaseBar.hidden = true` does nothing and every cohort's
   phases stay on screen at once. */
.cal-filter[hidden]{display:none;}
.cal-chip:focus-visible{outline:2px solid var(--primary);outline-offset:2px;}

/* --- Type indicator: colour is the COHORT, so the dot's SHAPE is the TYPE --- */
.cal-event--session  .cal-event__dot{border-radius:50%;}                 /* circle   */
.cal-event--homework .cal-event__dot{border-radius:2px;}                 /* square   */
.cal-event--quiz     .cal-event__dot{border-radius:1px;transform:rotate(45deg);} /* diamond */
.cal-event--exam     .cal-event__dot{border-radius:2px;
  clip-path:polygon(50% 0,100% 100%,0 100%);box-shadow:none;}            /* triangle */
.cal-event--exam{border-left-width:4px;font-weight:800;}

/* --- Legend --------------------------------------------------------------- */
.cal-legend{display:flex;flex-wrap:wrap;gap:20px;margin-top:16px;padding:14px 16px;
  border-radius:14px;background:#FBF8F1;border:1px solid var(--border);}
.cal-legend__group{display:flex;flex-wrap:wrap;align-items:center;gap:10px;}
.cal-legend__title{font-size:.7rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);}
.cal-legend__item{display:inline-flex;align-items:center;gap:7px;font-size:.78rem;font-weight:700;color:var(--text);}
.cal-legend__swatch{width:12px;height:12px;border-radius:4px;background:var(--ec,#065053);flex:0 0 auto;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--ec,#065053) 20%, transparent);}
/* the type legend reuses .cal-event, but must not look clickable */
.cal-legend .cal-event{width:auto;cursor:default;}
.cal-legend .cal-event:hover{transform:none;box-shadow:none;
  background:color-mix(in srgb, var(--ec) 15%, white);}

/* --- Schedule modal ------------------------------------------------------- */
.grid-3{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-4);}
@media(min-width:560px){ .grid-3{grid-template-columns:repeat(3,minmax(0,1fr));} }
.modal__foot{display:flex;justify-content:flex-end;gap:10px;margin-top:var(--sp-2);}
.sch-form [hidden]{display:none;}
.sch-form__note{font-size:.8rem;margin:-6px 0 var(--sp-4);}

/* ---- Field hint: the small explainer under an input ---- */
.field__hint{display:block;margin-top:5px;font-size:.76rem;color:var(--muted);line-height:1.45;}

/* ---- Phase gate rules (director settings) ---- */
.gate-conds{margin:8px 0 0;padding-left:18px;color:var(--text);font-size:.9rem;line-height:1.6;}
.gate-conds li{margin:2px 0;}
.gate-actions{min-width:230px;}

/* ==========================================================================
   staff.css — scratch styles for the staff change-requests batch.
   Merge into public/css/styles.css. Every rule here is additive; it only
   restyles elements that BECAME links/read-only in this batch.
   ========================================================================== */

/* §1.1 — calendar event chips are now <a> deep-links. Keep them looking exactly
   like the old <div> chip (no underline, inherit text colour) but feel tappable. */
a.cal-event {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}
a.cal-event:hover,
a.cal-event:focus-visible {
  filter: brightness(1.06);
  outline: none;
}
a.cal-event:focus-visible {
  box-shadow: 0 0 0 2px var(--ec, #065053);
}

/* §3.5 — dashboard stat cards are now <a>. The .stat/.card look already applies;
   these rules just neutralise anchor defaults and add a hover affordance. */
a.stat--link {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
a.stat--link:hover,
a.stat--link:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.10);
  outline: none;
}

/* §1.2 — read-only attendance note shown to roles that may not mark. */
.att-readonly {
  margin-bottom: var(--sp-3, 12px);
}

/* ==========================================================================
   §1.7 — "What the program covers" strip (hardcoded branded icons).
   Landing-only. Mirrors the existing .feature card design language
   (ivory surface, teal + gold, 20px radius, lift-on-hover) but gives the
   real branded .webp icons a clean circular badge to sit in.
   ========================================================================== */
.lp-covers{position:relative;padding:96px 24px;}
.covers-grid{list-style:none;margin:0 auto;padding:0;max-width:var(--lp-max);
  display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:24px;}
.cover{position:relative;overflow:hidden;text-align:center;background:var(--surface);
  border:1px solid var(--border);border-radius:20px;padding:34px 26px;
  box-shadow:0 2px 4px rgba(36,53,54,.04);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease);}
.cover::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;
  background:linear-gradient(90deg,var(--primary),var(--gold));transform:scaleX(0);transform-origin:left;
  transition:transform .4s var(--ease);}
.cover:hover{transform:translateY(-8px);border-color:rgba(6,80,83,.18);
  box-shadow:0 30px 60px -30px rgba(6,80,83,.4);}
.cover:hover::before{transform:scaleX(1);}

.cover__icon{display:inline-flex;align-items:center;justify-content:center;
  width:84px;height:84px;margin:0 auto 20px;border-radius:50%;
  background:radial-gradient(circle at 50% 40%,#FFFFFF,var(--primary-soft));
  border:1px solid rgba(6,80,83,.10);
  box-shadow:inset 0 1px 0 #fff,0 10px 22px -14px rgba(6,80,83,.55);
  transition:transform .35s var(--ease);}
.cover:hover .cover__icon{transform:translateY(-2px) scale(1.05);}
.cover__icon img{width:46px;height:46px;object-fit:contain;display:block;}
.cover h3{font-size:1.15rem;color:var(--lp-ink);margin-bottom:8px;}
.cover p{line-height:1.65;}

/* Reuse the landing reveal-rise for the cards. */
.reveal.is-visible .cover{animation:lpRise .6s var(--ease) both;animation-delay:var(--d,0ms);}

@media (max-width:720px){
  .lp-covers{padding:64px 20px;}
  .cover{padding:28px 22px;}
  .reveal.is-visible .cover{animation:none;}
}
@media (prefers-reduced-motion:reduce){
  .reveal.is-visible .cover{animation:none;}
  .cover,.cover__icon{transition:none;}
}

/* ==========================================================================
   §2.1 — Enlarge the footer logo (landing footer only; nav logo untouched).
   ========================================================================== */
.lp-footer__brand .brand__mark--footer{
  height:68px;width:auto;   /* was 40px — noticeably larger, aspect preserved */
}
@media (max-width:720px){
  .lp-footer__brand .brand__mark--footer{height:56px;}
}

/* ===========================================================================
   Cohort Hub — scratch CSS (merged into styles.css by the integrator).
   New classes introduced by the Cohort Hub change-requests only; existing
   classes (session-*, list__*, card, pill, btn, field…) are reused as-is.
   =========================================================================== */

/* §1.5 Phase description shown under the phase title. */
.phase-desc {
  margin: 6px 0 0;
  max-width: 68ch;
  white-space: pre-line;
  color: var(--text, #222);
}

/* §3.4 Inline staff attendance sheet on the session page. */
.attend-sheet { display: flex; flex-direction: column; gap: 8px; }

.attend-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 12px;
}
.attend-row__name { display: flex; align-items: center; gap: 8px; }

.attend-row__opts { display: inline-flex; gap: 6px; flex-wrap: wrap; }

/* Segmented present / absent / excused control. Radios are visually hidden and
   their label acts as the button; the checked one is highlighted. */
.attend-opt { position: relative; }
.attend-opt input {
  position: absolute;
  opacity: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.attend-opt span {
  display: inline-block;
  padding: 5px 12px;
  border: 1px solid var(--border, #d5d5d5);
  border-radius: 999px;
  font-size: 13px;
  line-height: 1.2;
  background: var(--surface, #fff);
  color: var(--muted, #666);
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.attend-opt input:hover + span { border-color: var(--accent, #2f6f4f); }
.attend-opt input:focus-visible + span {
  outline: 2px solid var(--accent, #2f6f4f);
  outline-offset: 1px;
}
.attend-opt input:checked + span {
  background: var(--accent, #2f6f4f);
  border-color: var(--accent, #2f6f4f);
  color: #fff;
  font-weight: 600;
}

/* §2.6 Small inline select used in the challenge marking grid. */
.select-sm {
  padding: 5px 8px;
  border: 1px solid var(--border, #d5d5d5);
  border-radius: 6px;
  font-size: 13px;
  background: var(--surface, #fff);
  color: inherit;
}

/* ==========================================================================
   STUDENT AREA — scratch CSS (merge into public/css/styles.css).
   Owner: student-area agent. §2.8 My Project tabs, §2.9 Profile.
   Reuses existing tokens (--sp-*, --border, --muted, --radius, colours) and
   existing components (.tabs/.tab/.is-active, .section-card, .pill, .ring,
   .stat-grid, .grid-2, .field, .list, .empty). Only NEW classes below.
   ========================================================================== */

/* ---- My Project: locked-tab pill in the tab bar ---- */
.proj-lock-pill { margin-left: 6px; font-size: .7em; vertical-align: middle; }

/* ---- Locked-tab panel (§2.8) ---- */
.tab-locked { display: flex; align-items: center; gap: var(--sp-3, 16px); }
.tab-locked__icon { flex: 0 0 auto; opacity: .7; }
.tab-locked p { margin: 4px 0 0; }

/* ---- Makamak placeholder (§2.8 — blocked) ---- */
.makamak-placeholder { text-align: center; }
.makamak-placeholder__icon { opacity: .6; margin-bottom: var(--sp-2, 8px); }
.makamak-placeholder h2 { margin: 0 0 6px; }
.makamak-placeholder .btn[disabled] { margin-top: var(--sp-3, 16px); opacity: .55; cursor: not-allowed; }

/* ---- Comment thread ----
   The base rule is NOT scoped to .pthread: the shared project partial
   (views/project/_form-thread.ejs) renders comments outside that wrapper, and
   with no base border the `border-style: dashed` below fell back to the
   initial `medium currentColor` — a thick black box round every reply. */
.pcomment { padding: var(--sp-3, 16px); border: 1px solid var(--border, #e2e2e2);
  border-radius: var(--r-card, 12px); margin-top: var(--sp-3, 16px); }
.pcomment--reply { margin-top: var(--sp-2, 8px); margin-inline-start: var(--sp-4, 24px);
  border: 1px dashed var(--border, #e2e2e2); background: var(--mq-tint, rgba(0,0,0,.02)); }
.pcomment__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.pcomment__author { font-weight: 600; }
.pcomment__role { text-transform: capitalize; }
.pcomment__meta { color: var(--muted, #667); font-size: .8rem; margin-left: auto; }
.pcomment__body { margin: 0; white-space: pre-wrap; }
.pcomment__reply-form { margin-top: var(--sp-2, 8px); }

/* ---- Profile: attendance per phase (§2.9) ---- */
.attn-phases { display: grid; gap: var(--sp-3, 16px);
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); margin-top: var(--sp-3, 16px); }
.attn-phase { border: 1px solid var(--border, #e2e2e2); border-radius: var(--radius, 12px);
  padding: var(--sp-3, 16px); text-align: center; }
.attn-phase__ring { display: flex; justify-content: center; }
.attn-phase__title { font-weight: 600; margin-top: var(--sp-2, 8px); }
.attn-phase__count { margin-top: 2px; }
ul.attn-phase__missed { list-style: none; margin: var(--sp-2, 8px) 0 0; padding: 0;
  text-align: left; display: grid; gap: 4px; }
ul.attn-phase__missed li { display: flex; align-items: center; gap: 6px; justify-content: space-between; }
.attn-item__title { font-size: .85rem; }
.attn-item__status { text-transform: capitalize; flex: 0 0 auto; font-size: .7rem; }

/* ---- Staff project management (student report §2.8) ---- */
.proj-lock-row{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0 8px;}
.proj-thread{border-top:1px solid var(--border);margin-top:16px;padding-top:14px;}
.proj-thread__title{font-size:1rem;margin:0 0 10px;}
.proj-comment-form{display:flex;flex-direction:column;gap:8px;margin-top:10px;align-items:flex-start;}
.proj-comment-form textarea{width:100%;}

/* ---- Quiz builder (session page modal) ---- */
.modal--wide{max-width:640px;width:min(640px,94vw);}
/* Quiz BUILDER (staff "Add quiz") — one card per question, full width. */
.quiz-build{display:flex;flex-direction:column;gap:14px;margin:14px 0;}
.quiz-q{padding:16px 18px;display:flex;flex-direction:column;gap:12px;text-align:left;}
.quiz-q__type{max-width:220px;}
.qb-choices-head{font-weight:600;font-size:.9rem;margin:2px 0 2px;}
.qb-choice{display:flex;align-items:center;gap:12px;}
.qb-choice__radio{flex:0 0 auto;width:20px;height:20px;cursor:pointer;}
.qb-choice__text{flex:1;min-width:0;}
.quiz-q__tf{display:flex;gap:24px;padding:2px 0;}
.qb-tf{display:inline-flex;align-items:center;gap:8px;font-weight:600;}

/* ---- Student report: per-phase groups ---- */
.rep-phase{margin-top:14px;}
.rep-phase:first-of-type{margin-top:8px;}
.rep-phase__head{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:8px 2px;border-bottom:1px solid var(--border);margin-bottom:6px;}
.rep-phase__head strong{font-size:.98rem;}

/* ---- People, grouped by cohort ---- */
.ppl-group{margin-top:26px;}
.ppl-group:first-of-type{margin-top:8px;}
.ppl-group__title{display:flex;align-items:center;gap:10px;font-size:1.15rem;margin:0 0 12px;}
.ppl-group__title .ico{width:22px;height:22px;}

/* ---- People: a student row opens their report ----
   Stretched-link pattern. The <a> wraps only the name so it announces sensibly,
   while its ::after covers the whole card so the entire row is a click target.
   .list__actions is raised above that overlay, otherwise the Edit button would
   sit underneath it and open the report instead of the modal. */
.ppl-row--link{position:relative;cursor:pointer;
  transition:box-shadow var(--dur) var(--ease),transform var(--dur) var(--ease),
             border-color var(--dur) var(--ease);}
.ppl-row--link:hover{box-shadow:var(--sh-md);transform:translateY(-1px);border-color:var(--primary-soft);}
.ppl-row__link{color:inherit;text-decoration:none;}
.ppl-row__link::after{content:"";position:absolute;inset:0;border-radius:inherit;}
.ppl-row--link:hover .ppl-row__link{color:var(--primary);text-decoration:underline;
  text-underline-offset:2px;}
/* Keyboard focus rings the whole row, since the link's target IS the whole row.
   Drawn on the stretched ::after rather than via :focus-within, so tabbing to
   the Edit button rings the button and not the entire card. */
.ppl-row__link:focus-visible{outline:none;}
.ppl-row__link:focus-visible::after{outline:2px solid var(--primary);outline-offset:2px;}
.ppl-row--link .list__actions{position:relative;z-index:1;}
/* Chevron hint — slides in on hover, hidden from assistive tech. */
.ppl-row__go{color:var(--muted);opacity:0;transform:translateX(-4px);
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease);}
.ppl-row--link:hover .ppl-row__go{opacity:1;transform:none;}
[dir="rtl"] .ppl-row__go{transform:scaleX(-1) translateX(-4px);}
[dir="rtl"] .ppl-row--link:hover .ppl-row__go{transform:scaleX(-1);}
@media (hover:none){.ppl-row__go{opacity:1;transform:none;}}
@media (prefers-reduced-motion:reduce){
  .ppl-row--link,.ppl-row__go{transition:none;}
  .ppl-row--link:hover{transform:none;}
}

/* ---- Calendar: locked event (student view) ---- */
.cal-event--locked{opacity:.62;cursor:default;filter:grayscale(.25);}
.cal-event--locked .cal-event__txt{text-decoration:none;}
.cal-event__lock{margin-left:auto;font-size:.72em;line-height:1;flex:0 0 auto;}

/* ---- Quiz take/review popup (student, on the session page) — sq-* so it never
        collides with the staff quiz BUILDER (.quiz-*) ---- */
.sq-modal .modal__body{max-width:520px;margin:0 auto;}
.sq-grade{text-align:center;font-size:1.05rem;margin:0 0 16px;}
.sq-q{margin:0 auto 18px;padding:16px;border:1px solid var(--border);border-radius:12px;
  background:var(--surface);max-width:460px;text-align:center;}
.sq-q__prompt{font-weight:700;margin-bottom:12px;line-height:1.45;}
.sq-q__answer{text-align:left;white-space:pre-wrap;}
.sq-choices{display:flex;flex-direction:column;gap:10px;align-items:stretch;max-width:380px;margin:0 auto;}
.sq-choice{display:flex;align-items:center;gap:12px;text-align:left;
  border:1px solid var(--border);border-radius:12px;padding:13px 16px;cursor:pointer;
  transition:border-color var(--dur),background var(--dur);}
.sq-choice:hover{border-color:var(--primary);}
.sq-choice:has(input:checked){border-color:var(--primary);background:var(--primary-soft);}
.sq-choice span{flex:1;}
.sq-q textarea{width:100%;text-align:left;}
.sq-opts{list-style:none;margin:0 auto;padding:0;max-width:360px;text-align:left;}
.sq-opts li{padding:8px 12px;border:1px solid var(--border);border-radius:10px;margin:6px 0;}
.sq-opts li.is-picked{border-color:var(--primary);background:var(--primary-soft);font-weight:600;}
.sq-form__note{text-align:center;}
.sq-form__submit{display:flex;justify-content:center;margin-top:8px;}
.quiz-done{margin-top:6px;font-size:.9rem;}

/* ---- Attendance: compact present/not-present summary on the session page ---- */
.att-compact{margin-top:10px;font-size:.8rem;line-height:1.6;color:var(--text);}
.att-compact__line{margin:2px 0;}
.att-compact__tag{display:inline-block;font-weight:700;font-size:.68rem;text-transform:uppercase;letter-spacing:.04em;
  padding:1px 7px;border-radius:6px;margin-right:6px;background:var(--mist);color:var(--muted);}
.att-compact__tag--ok{background:#D8EFE5;color:#1F6E52;}

/* ---- Quiz/homework detail: polished question cards ---- */
.q-list{display:flex;flex-direction:column;gap:16px;margin-bottom:22px;}
.q-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);
  padding:20px 22px;box-shadow:var(--sh-sm);}
.q-card__head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin-bottom:14px;}
.q-card__title{display:flex;align-items:flex-start;gap:10px;font-weight:700;font-size:1.02rem;line-height:1.5;}
.q-card__num{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex:0 0 auto;
  border-radius:50%;background:var(--primary);color:#fff;font-size:.85rem;font-weight:700;}
.q-card__meta{display:flex;align-items:center;gap:8px;flex:0 0 auto;}
.q-card__note{margin:0;padding:12px 14px;border:1px dashed var(--border);border-radius:12px;}
.q-choices{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;}
.q-choice{display:flex;align-items:center;gap:12px;padding:12px 16px;border:1px solid var(--border);
  border-radius:12px;background:var(--surface);}
.q-choice--correct{border-color:#2E7D5B;background:#E8F5EE;}
.q-choice__mark{width:22px;height:22px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  border-radius:50%;font-size:.78rem;font-weight:700;color:transparent;border:1.5px solid var(--border);}
.q-choice--correct .q-choice__mark{background:#2E7D5B;border-color:#2E7D5B;color:#fff;}
.q-choice__text{flex:1;font-weight:500;}
.q-choice__badge{flex:0 0 auto;font-size:.7rem;font-weight:800;color:#1F6E52;text-transform:uppercase;letter-spacing:.05em;}

/* ---- Homework detail: header actions align neatly on one row ---- */
.hw-actions{display:flex;align-items:center;flex-wrap:wrap;justify-content:flex-end;gap:8px;}

/* ---- Clear section division across the app ---- */
/* Stacked sections get breathing room; the two dashboards use gap instead. */
.section-card + .section-card{margin-top:18px;}
.dash-col > .section-card + .section-card{margin-top:0;}
/* A titled section header with an underline, so each section reads as its own block. */
.sec-title{font-family:var(--serif);font-size:1.18rem;font-weight:600;color:var(--primary);
  margin:0 0 14px;padding-bottom:10px;border-bottom:1px solid var(--border);}
.sec-title-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap;
  margin:0 0 14px;padding-bottom:10px;border-bottom:1px solid var(--border);}

/* Quiz detail: add-question block + locked note, clearly set apart. */
.q-add{margin-top:20px;padding-top:18px;border-top:2px dashed var(--border);}
.q-add__title{font-size:1.02rem;margin:0 0 12px;}
.q-locked{display:flex;gap:14px;align-items:flex-start;margin-top:20px;padding:16px 18px;
  border-radius:12px;background:var(--primary-soft);}
.q-locked__icon .ico{width:26px;height:26px;}

/* ---- Edit-quiz modal: section headers + reset ---- */
.edit-sec{font-size:.95rem;font-weight:700;color:var(--primary);margin:18px 0 10px;
  padding-bottom:6px;border-bottom:1px solid var(--border);}
.edit-sec:first-of-type{margin-top:0;}
.q-reset{margin-top:14px;padding-top:14px;border-top:1px dashed var(--border);text-align:center;}

/* ---- Styled confirmation dialog (replaces native confirm) ---- */
.confirm-modal{max-width:400px;width:min(400px,92vw);text-align:center;padding:26px 24px;}
.confirm-modal__icon{width:52px;height:52px;margin:0 auto 14px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-size:1.6rem;font-weight:800;
  background:var(--primary-soft);color:var(--primary);}
.confirm-modal__msg{font-size:1.02rem;line-height:1.5;margin:0 0 22px;color:var(--text);}
.confirm-modal__actions{display:flex;gap:10px;justify-content:center;}
.confirm-modal__actions .btn{min-width:110px;}

/* ===========================================================================
   AUDIT-PASS ADDITIONS (merged from per-area scratch files). Appended last so
   the landing footer-logo override below supersedes the earlier value.
   =========================================================================== */

/* ---- Staff report: section sub-heading (e.g. "Tab locks") ---- */
.stf-subhead{font-size:.95rem;font-weight:600;margin:14px 0 6px;color:var(--text-muted,#64748b);}

/* ---- Calendar: locked-status legend key + empty-month note ---- */
.cal2-lock-key{display:inline-flex;align-items:center;gap:8px;font-size:.78rem;font-weight:700;color:var(--muted,#6B7F81);}
.cal2-lock-key__chip{display:inline-flex;align-items:center;gap:5px;padding:3px 8px;border-radius:8px;
  background:color-mix(in srgb,#6B7F81 12%,white);opacity:.62;filter:grayscale(.25);}
.cal2-lock-key__chip .cal-event__dot{width:7px;height:7px;border-radius:50%;background:#6B7F81;flex:0 0 auto;}
.cal2-lock-key__ico{font-size:.72em;line-height:1;}
.cal2-empty{display:flex;align-items:center;justify-content:center;gap:8px;margin:0;padding:26px 20px 30px;
  color:var(--muted,#6B7F81);font-size:.92rem;font-weight:600;}
.cal2-empty__ico{font-size:1.15rem;opacity:.8;}
@media (max-width:520px){.cal2-empty{padding:18px 12px 22px;font-size:.86rem;}}

/* ---- Homework material download link (staff-attached PDF/worksheet) ---- */
.hw-material{display:inline-flex;align-items:center;gap:6px;font-size:.86rem;font-weight:600;
  color:var(--primary);text-decoration:none;}
.hw-material:hover{text-decoration:underline;}
.hw-material .ico,.hw-material svg{width:16px;height:16px;flex:0 0 auto;}

/* ---- Inline checkbox field (label beside a checkbox, not full-width) ---- */
.field--check{display:flex;flex-direction:row;align-items:center;gap:8px;}
.field--check input[type="checkbox"]{width:auto;margin:0;}
.field--check span{font-size:.9rem;color:var(--text);}

/* ---- Landing footer logo — enlarged (supersedes earlier value) ---- */
.lp-footer__brand .brand.brand--footer{gap:14px;}
.lp-footer__brand .brand__mark--footer{height:80px;width:auto;margin-bottom:2px;
  filter:drop-shadow(0 8px 18px rgba(6,80,83,.16));}
@media (max-width:720px){.lp-footer__brand .brand__mark--footer{height:64px;}}
@media (max-width:420px){
  .lp-covers{padding:56px 18px;}
  .covers-grid{gap:18px;}
  .cover{padding:26px 20px;}
  .cover__icon{width:76px;height:76px;margin-bottom:16px;}
}


/* ###########################################################################
   MOBILE POLISH PASS — merged from 8 area audits (2026-07). Appended last so
   these responsive refinements win on equal specificity. Ordered: primitives →
   chrome → modals/forms → landing/auth → cohort → student → staff → calendar.
   ########################################################################### */


/* ========================= primitives.css ========================= */
/* ==========================================================================
   MOBILE PRIMITIVES — proposed additive CSS for the shared layout building
   blocks. Owner: layout-primitives agent.

   HOW TO READ THIS FILE
   - Everything here is ADDITIVE and meant to be merged into
     public/css/styles.css (do NOT replace the file).
   - The base styles.css is already mobile-first (auto-fit/auto-fill grids
     collapse on their own; html{overflow-x:hidden} is a backstop). These rules
     REFINE that base: bigger tap targets, tidy button clusters, safe wrapping,
     table safety, and small spacing/typography tuning for 360–430px phones.
   - For every EXISTING selector I override, a comment names it + the reason so
     the integrator can reconcile against the current rules in styles.css.
   - Breakpoints follow the existing convention: 720 (desktop) / 600 / 520 /
     420 / 380. I add nothing narrower and fight nothing already there.
   ========================================================================== */


/* ==========================================================================
   1. GLOBAL OVERFLOW SAFETY (backbone — other agents rely on this)
   Existing: html{overflow-x:hidden} (styles.css ~L22) is the only guard.
   Reason: make the guard belt-and-suspenders and make sure long unbroken
   strings (URLs, emails, Arabic ligatures) never push the body sideways. The
   base already sets overflow-wrap:break-word on body; reinforce for headings
   and generic containers so a single long word can't create a scrollbar.
   ========================================================================== */
html, body { max-width: 100%; }
h1, h2, h3, h4 { overflow-wrap: break-word; }        /* serif headings can be long */
.mat-link, .pcomment__body, .session-objective { overflow-wrap: anywhere; }


/* ==========================================================================
   2. PAGE CONTAINER / WRAPPER
   Existing: .container{max-width:960px;margin:0 auto;padding:24px 16px 96px}
             @media(max-width:600px){.container{padding:20px 14px 92px}}
   Reason: 14px side padding holds up at 360px, but on very small phones
   (<=380px) trim a touch more so cards get maximum inner width, and make sure
   the bottom padding always clears the fixed .bottomnav.
   ========================================================================== */
@media (max-width: 380px) {
  .container { padding-left: 12px; padding-right: 12px; }
}


/* ==========================================================================
   3. PAGE HEAD / ROW-BETWEEN (title + actions clusters)
   Existing: .row-between{display:flex;...;flex-wrap:wrap} — already wraps. Good.
             @media(max-width:600px){.page-head.row-between{align-items:flex-start}}
   Problem: the action cluster is usually a bare `.inline` (display:inline,
   styles.css ~L32) holding 2 buttons — so the buttons have only a stray HTML
   whitespace between them (no real gap) and wrap unpredictably. On phones they
   should become a proper wrapping flex row with real gap, and grow to share the
   width so they read as comfortable, evenly-arranged tap targets.
   NOTE for other agents / integrator: I scope this to `.page-head .inline`
   ONLY. Do NOT globally flex `.inline` — many views use `<form class="inline">`
   as a single-button wrapper inside other flex rows (.sess__actions,
   .section-edit__actions, …); flexing it globally would break those rows.
   ========================================================================== */
@media (max-width: 600px) {
  .page-head .inline {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    width: 100%;
    margin-top: var(--sp-3);   /* separate from the title block it wraps under */
  }
  .page-head .inline > .btn,
  .page-head .inline > form > .btn {
    flex: 1 1 auto;            /* share the row; full-width when alone */
    min-height: 44px;         /* comfortable tap target even for .btn--sm */
  }
}

/* Tidy alignment: an <h2> inside a row-between carries a 16px bottom margin
   (h2{margin:0 0 16px} / .section-card h2) which offsets it from the trailing
   button. Zero it only in this flex context. */
.row-between > h2 { margin-bottom: 0; }


/* ==========================================================================
   4. TYPOGRAPHY SCALE (phones)
   Existing: h1 1.85 (720:2.15, 600:1.62). h2 1.35 (600:1.25). h3 1.18.
   Reason: base is already sensible. Only nudge the leading tighter on phones so
   multi-line serif headings feel intentional, not loose.
   ========================================================================== */
@media (max-width: 600px) {
  h1 { line-height: 1.15; }
  h2 { line-height: 1.2; }
}


/* ==========================================================================
   5. BUTTONS & TAP TARGETS (backbone — other agents defer here)
   Existing: .btn{padding:10px 18px;font-size:.92rem} → ~40px tall.
             .btn--sm{padding:6px 12px} → ~30px tall.
             .icon-btn{width:32px;height:32px}.
             @media(max-width:380px){.btn{padding:9px 14px}}
   Problem: none of these hit the 44px minimum touch target on phones.
   Fix: enforce min-heights on phones. Keep the visual padding, just guarantee
   the hit area. icon-btn grows to a square 44. .btn--sm stays visually small
   but becomes reliably tappable.
   ========================================================================== */
@media (max-width: 600px) {
  .btn      { min-height: 44px; }
  .btn--sm  { min-height: 40px; }          /* still compact, still tappable */
  .icon-btn { width: 44px; height: 44px; } /* was 32×32 */

  /* Segmented attendance control + chips are interactive too — give them room. */
  .seg__btn      { padding-top: 10px; padding-bottom: 10px; }   /* was 7px 12px */
  .attend-opt span { padding-top: 8px; padding-bottom: 8px; }   /* was 5px 12px */
  .chip          { padding-top: 8px; padding-bottom: 8px; }
  .cal-chip      { padding-top: 9px; padding-bottom: 9px; }
}

/* Very-narrow safety for the 380 rule already in styles.css: keep the enforced
   min-height even though padding shrinks (min-height wins, so nothing to do —
   documented here so the integrator doesn't reintroduce a shrink that beats it). */


/* ==========================================================================
   6. STAT GRID
   Existing: .stat-grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
             .stat-grid--4/--5 use minmax(150/140).
             @media(max-width:380px){.stat__value{font-size:1.7rem}}
   Behaviour: at 360px (≈332px inner) this already yields a tidy 2 columns — the
   desired "collapse to 1–2 columns". No structural change needed. Only make the
   big serif value never overflow its cell and keep 2-up gaps calm on phones.
   ========================================================================== */
@media (max-width: 600px) {
  .stat-grid, .stat-grid--4, .stat-grid--5 { gap: var(--sp-3); }
  .stat__value { line-height: 1.05; overflow-wrap: anywhere; }
}
/* Ultra-narrow: a single clean column reads better than two cramped ones. */
@media (max-width: 340px) {
  .stat-grid, .stat-grid--4, .stat-grid--5 { grid-template-columns: minmax(0, 1fr); }
}


/* ==========================================================================
   7. GRID-2 / GRID-3 (generic content grids)
   Existing: .grid-2 → 1fr, 2 cols @640.  .grid-3 → 1fr, 3 cols @560.
   Behaviour: both are already single-column on phones (correct). .grid-3 jumps
   1→3 at 560px which is fine inside the schedule modal it serves. No override
   needed; documented so the integrator knows it was reviewed and left as-is.
   ========================================================================== */
/* (intentionally no rules — base behaviour is already correct on phones) */


/* ==========================================================================
   8. LIST / LIST__ROW / LIST__MAIN / LIST__ACTIONS
   Existing: .list__row{display:flex;...;flex-wrap:wrap}
             .list__main{flex:1;min-width:0}
             .list__actions{display:flex;gap:4px;flex-wrap:wrap;justify-content:flex-end}
             @media(max-width:600px){.list__actions{width:100%;justify-content:flex-start;margin-top:6px}}
   Behaviour: rows already keep name+avatar on line 1 and drop actions to a full
   line 2 on phones — exactly right. Refinements: (a) give the wrapped actions a
   little more breathing room, (b) let the action buttons in that full-width row
   grow so the 44px targets space out evenly instead of hugging the left edge.
   ========================================================================== */
@media (max-width: 600px) {
  .list__actions { gap: var(--sp-2); margin-top: var(--sp-2); }
  /* Text buttons in the wrapped action row share the width evenly; icon-only
     buttons keep their fixed 44px square (they are not .btn). */
  .list__actions > .btn,
  .list__actions > form > .btn { flex: 1 1 auto; }
}


/* ==========================================================================
   9. CARD / SECTION-CARD (padding tuned for phones)
   Existing: .card{padding:24px}. .section-card{padding:22px}.
             @media(max-width:600px){.card,.section-card{padding:18px}}
   Reason: 18px is good at 375px; at <=380px shave to 16px so content width is
   maximised and nested grids/lists don't feel boxed in.
   ========================================================================== */
@media (max-width: 380px) {
  .card, .section-card { padding: 16px; }
}


/* ==========================================================================
   10. INLINE-FORM (search / filter rows)
   Existing: .inline-form{display:flex;gap:8px;flex-wrap:wrap}
             .inline-form input{width:auto;flex:1;min-width:160px}
   Problem: the 160px min-width can force an overflow on the very narrowest
   phones before the wrap kicks in. Let the input shrink fully on tiny screens.
   ========================================================================== */
@media (max-width: 420px) {
  .inline-form input { min-width: 0; }
  .inline-form .btn  { flex: 1 1 auto; }   /* full-width submit under the input */
}


/* ==========================================================================
   11. TABLES  (required safety net)
   Existing: there are currently NO <table> elements in any view, and no
   overflow wrapper class exists. This block is DEFENSIVE so that the body can
   never scroll horizontally the moment anyone adds a table.
   NOTE for other agents: if you introduce a <table>, wrap it in
   <div class="table-scroll">…</div>. The wrapper — not the page — takes the
   horizontal scroll.
   ========================================================================== */
table { width: 100%; max-width: 100%; border-collapse: collapse; }
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}
.table-scroll > table { min-width: 480px; }   /* let columns keep their width inside the scroller */


/* ==========================================================================
   12. PILL / MISC INLINE TOKENS
   Existing: .pill{...;white-space:nowrap}. Already safe (won't wrap-break).
   Reason: only ensure a run of pills/meta separated by "·" wraps as a group
   rather than pushing width. mini-row__meta is nowrap by design; allow it to
   wrap on phones so long "status · facilitator · N students" strings don't
   force horizontal scroll.
   Existing: .mini-row__meta{white-space:nowrap;text-align:right}
   ========================================================================== */
@media (max-width: 600px) {
  .mini-row__meta { white-space: normal; }
}


/* ==========================================================================
   13. MODAL FOOTERS / ACTION ROWS IN STACKS
   Existing: .modal__foot{justify-content:flex-end}. .row-end{justify-content:flex-end}.
   Reason: in a narrow modal a right-aligned Cancel/Save pair can crowd; on
   phones let them span the row so each is a full, evenly-sized target. Modal
   chrome itself is owned by another agent — this only touches the shared
   .row-end / .modal__foot action primitives.
   ========================================================================== */
@media (max-width: 480px) {
  .row-end, .modal__foot { flex-wrap: wrap; }
  .modal__foot { gap: var(--sp-2); }
  .modal__foot > .btn { flex: 1 1 auto; min-height: 44px; }
}


/* ========================= nav.css ========================= */
/* =====================================================================
   MOBILE CHROME POLISH  —  nav / header / footer / crumbs
   Scope: views/partials/{header,nav,crumbs,footer}.ejs
   Target: phones 360–430px first, then tablet (768px).
   CSS-only — NO JavaScript (the existing bottom tab bar means no hamburger
   toggle is needed). Reuses the design tokens from styles.css :root.

   >>> INTEGRATOR: include this file AFTER public/css/styles.css so the
   media-query overrides below (equal specificity) win the cascade. <<<
   ===================================================================== */


/* ---------------------------------------------------------------------
   1. TOPBAR — restore the missing horizontal padding + honor notches
   OVERRIDE of styles.css line 129:
       .topbar__inner{ ... padding:12px var(--sp-5); ... }
   WHY: `--sp-5` is NOT defined in :root (the scale is sp-1,2,3,4,6,8 — it
   skips 5). `var(--sp-5)` therefore resolves invalid-at-computed-value and
   the whole `padding` declaration collapses to its initial 0. Result: the
   brand and the Sign-out button sit flush against the screen edges on every
   width. Replace with a real value and add safe-area insets (the header
   uses viewport-fit=cover, so content can slide under a notch in landscape).
   --------------------------------------------------------------------- */
.topbar__inner{
  padding:12px 16px;                                  /* was 12px var(--sp-5) */
  padding-left:max(16px, env(safe-area-inset-left));
  padding-right:max(16px, env(safe-area-inset-right));
}
/* Sticky bar clears the status-bar / notch when viewport-fit=cover. */
.topbar{ padding-top:env(safe-area-inset-top); }

/* ---------------------------------------------------------------------
   2. BRAND — never wrap. On desktop the brand keeps its full width
   (.topbar .brand{flex:0 0 auto}), so the wordmark is never clipped. Only on
   phones (≤720px) do we let it ellipsize as a last resort next to Sign out.
   --------------------------------------------------------------------- */
@media(max-width:720px){
  .brand{ min-width:0; }
  .topbar .brand{ flex:1 1 auto; min-width:0; }
  .topbar .brand__word{
    min-width:0;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
}


/* =====================================================================
   3. PHONES  (max-width:720px)  — the mobile chrome regime:
   top brand bar + fixed bottom tab bar (topnav is already hidden here).
   ===================================================================== */
@media(max-width:720px){

  /* --- Role badge + Sign out ------------------------------------------
     OVERRIDE of styles.css line 631:
         @media(max-width:720px){ .topbar__user{display:none;} }
     WHY: that rule hides the ENTIRE user block, so the role badge
     (DIRECTOR / STUDENT / …) disappears on mobile and the top-right loses
     all hierarchy. Instead we keep a compact role pill and hide only the
     long name/email (which does not fit next to Sign out on a phone).
     NOTE: relies on the name being wrapped in `.topbar__user__name`
     (structural edit made in views/partials/header.ejs).                 */
  .topbar__user{ display:inline-flex; align-items:center; gap:0; }
  .topbar__user__name{ display:none; }
  .role-pill{ font-size:.62rem; padding:3px 9px; }

  .topbar__right{ gap:10px; }

  /* Sign-out: comfortable ≥44px thumb target.
     OVERRIDE (scoped to the topbar) of styles.css line 71:
         .btn--sm{ padding:6px 12px; ... }  → too short to tap on a phone. */
  .topbar__right .btn--sm{
    min-height:44px;
    display:inline-flex;
    align-items:center;
    padding:8px 15px;
  }

  /* --- Bottom tab bar = the primary mobile nav (CSS-only, no JS) -------
     Generous tap targets; labels stay on one line and truncate rather
     than wrap/overflow when a role has several items (director = 5).      */
  .bottomnav__link{
    min-height:52px;
    justify-content:center;
    gap:3px;
  }
  .bottomnav__link > span:last-child{
    max-width:100%;
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
  }
  .bottomnav__link:active{ background:var(--primary-soft); } /* press feedback */

  /* A director now has eight links. An equal share of a 390px phone is ~44px,
     at which every label collapses to an ellipsis and the icons crowd. Give
     each item a readable floor and let the BAR scroll instead — with few items
     `flex:1 0 auto` still spreads them across the full width, so nothing
     changes for a student (3) or a facilitator (2). */
  .bottomnav{ overflow-x:auto; scrollbar-width:none; }
  .bottomnav::-webkit-scrollbar{ display:none; }
  .bottomnav__link{ flex:1 0 auto; min-width:66px; }

  /* --- Breadcrumb: roomier line + taller tap targets ------------------ */
  .crumbs{ gap:6px 8px; font-size:.82rem; margin-bottom:12px; }
  .crumbs__link{ padding:4px 0; }
}


/* =====================================================================
   4. NARROW PHONES  (max-width:420px)  — reclaim a little width.
   ===================================================================== */
@media(max-width:420px){
  .topbar .brand__word{ font-size:1.18rem; }           /* was 1.35rem @topbar */
  .topbar .brand__mark{ height:32px; }                 /* was 36px */

  .bottomnav__link{ font-size:.66rem; padding:8px 4px; }
  .bottomnav__icon .ico{ width:22px; height:22px; }    /* was 23px */
}


/* =====================================================================
   5. TABLET  (720–899px)  — keep the comfortable mobile chrome.
   OVERRIDE of styles.css line 287:
       @media(min-width:720px){ .topnav{display:flex} .bottomnav{display:none} ... }
   WHY: at 720px the desktop top-nav takes over, but the full text nav
   (up to 5 role links) + brand + username + Sign out do NOT fit on one
   row until ~1000px+, causing horizontal overflow on ~768–834px tablets
   (iPad mini/Air/Pro portrait). Keep the top brand bar + bottom tab bar
   through 899px; the top-right now has room to show the full username too.
   >>> See findings: the top text-nav still risks overflow in the 900–1000px
   band — that is the pre-existing desktop layout, left untouched here. <<<
   --------------------------------------------------------------------- */
@media(min-width:720px) and (max-width:899px){
  .topnav{ display:none; }
  .bottomnav{ display:flex; }
  .container{ padding-bottom:92px; }   /* re-clear the fixed bottom bar */
}


/* ---------------------------------------------------------------------
   5b. DESKTOP HEADER — the 900–1030px overflow the note above flagged.
   Measured on /director at 900px: .topbar__inner needs 1026px, so
   `document.documentElement.scrollWidth` = 1026 vs a 900px viewport and
   .topbar__right (with the Sign-out button, 947→1026) sits entirely OFF
   screen — elementFromPoint over Sign out returns null. Same unreachable
   Sign-out failure as B3, just in the 900–1030 band (iPad landscape,
   1024x768 laptops). Cause: EVERY child of the flex row refuses to shrink
   — .topbar .brand{flex:0 0 auto} (:130), .topnav__link{flex:0 0 auto}
   (:133), .topbar__right{flex:0 0 auto} (:139) — so the row simply
   overflows instead of adapting.
   Fix: make the link row the flexible element (it is the only one that can
   degrade gracefully) and let it scroll horizontally inside the bar, and
   drop the long name in the tight band — the role pill stays. Sign out is
   then always on screen. Nothing changes at >=1040px, where it already fit.
   --------------------------------------------------------------------- */
@media(min-width:900px){
  .topnav{ flex:0 1 auto; min-width:0; overflow-x:auto; scrollbar-width:none; }
  .topnav::-webkit-scrollbar{ display:none; }
}
@media(min-width:900px) and (max-width:1039px){
  .topbar__user__name{ display:none; }   /* role pill is enough at this width */
}


/* ========================= modals-forms.css ========================= */
/* =============================================================================
   MOBILE POLISH — MODALS & FORM CONTROLS  (additive, CSS-only)
   Scope: .modal* / .confirm-* / .field / inputs / quiz BUILDER (.quiz-*,.qb-*)
          / student quiz popup (.sq-*). No .ejs edits, no JS.
   Design system: reuses :root tokens from styles.css (--sp-*, --primary,
   --border, --r-input, --danger, etc). Breakpoints follow the app: 720 / 520 /
   420. Load this AFTER styles.css so these rules win on equal specificity.

   Each block notes the base rule in styles.css it overrides and why.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   1. TOUCH-COMFORT BASELINE FOR FORM CONTROLS (applies on phones & tablets)
   Base: styles.css:77-78 sets inputs to padding:11px 13px (~40px tall) and
   font:inherit. On touch we want >=44px targets and 16px text so iOS Safari
   does NOT auto-zoom when a field is focused.
--------------------------------------------------------------------------------*/
@media (max-width:720px){
  .modal input:not([type="radio"]):not([type="checkbox"]),
  .modal select,
  .modal textarea,
  .field input:not([type="radio"]):not([type="checkbox"]),
  .field select,
  .field textarea{
    font-size:16px;              /* prevent iOS focus-zoom (base was font:inherit) */
    min-height:44px;             /* comfortable tap target (base was ~40px) */
    padding-top:12px;padding-bottom:12px;
  }
  .field textarea, .modal textarea{ min-height:96px; }   /* multi-line breathing room */
  .field select, .modal select{
    /* keep native chevron reachable; leave room so text never sits under it */
    padding-right:36px;
  }
  /* Full-bleed labels stack tightly; give the label a touch more contrast size */
  .field__label{ font-size:.85rem; }
}

/* -----------------------------------------------------------------------------
   2. MODAL SHELL — never exceed viewport, cap height, body scrolls internally
   Base: styles.css:276 .modal{width:100%;max-width:480px;max-height:90vh}
         styles.css:640-651 (max-width:600px) already tightens overlay padding
         to 12px and max-height:92dvh. We make the width explicit as
         min(cap,92vw) and centre it, and harden the internal scroll so the
         head stays put while only the body scrolls.
--------------------------------------------------------------------------------*/
@media (max-width:520px){
  .modal-overlay{ padding:12px; }                    /* reinforce 600px rule at phone tier */
  .modal{
    width:min(480px,92vw);                           /* explicit, centred, no h-scroll */
    max-width:92vw;
    max-height:92dvh;
    margin:0 auto;
  }
  .modal--wide{ width:min(640px,92vw); max-width:92vw; }   /* base 1497: width:min(640px,94vw) */

  /* Head is fixed; body is the only scroll region. Base: styles.css:280 already
     sets body overflow-y:auto — we make the head non-shrinking so long bodies
     don't squeeze the title, and add momentum scrolling. */
  .modal__head{ flex:0 0 auto; padding:14px 16px; }
  .modal__body{
    flex:1 1 auto;
    padding:16px;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;                     /* trap scroll inside the modal */
  }
  .modal__title{ font-size:1rem; line-height:1.3; }
}

/* -----------------------------------------------------------------------------
   3. MODAL ACTION ROWS — foot buttons stack full-width on phones
   Base: styles.css:1259 .modal__foot{display:flex;justify-content:flex-end}
         Also the "submit" rows use the generic .row-end (styles.css:99) inside
         the modal body (e.g. session Add-homework / Add-quiz). Both must become
         full-width, comfortably tall, primary-on-top stacks on phones.
   NOTE: .row-end is a shared class, so we ONLY touch it inside .modal to avoid
         disturbing other pages owned by other agents.
--------------------------------------------------------------------------------*/
@media (max-width:520px){
  .modal__foot,
  .modal__body .row-end,
  .modal__body .row-end.gap{
    display:flex;
    flex-direction:column-reverse;   /* primary (last in DOM) sits on top */
    align-items:stretch;
    gap:10px;
    margin-top:16px;
  }
  .modal__foot > .btn,
  .modal__body .row-end > .btn{
    width:100%;
    min-height:46px;
    padding:12px 16px;               /* base .btn is 9-10px vertical on phone */
    justify-content:center;
  }
}

/* -----------------------------------------------------------------------------
   4. CONFIRM DIALOG — comfortable, no cramped side-by-side on tiny screens
   Base: styles.css:1604 .confirm-modal{max-width:400px;width:min(400px,92vw)}
         styles.css:1609 actions=flex center; :1610 .btn{min-width:110px}
--------------------------------------------------------------------------------*/
@media (max-width:520px){
  .confirm-modal{ width:min(400px,92vw); padding:24px 20px; }
  .confirm-modal__msg{ font-size:1rem; }
  .confirm-modal__actions{ gap:10px; }
  .confirm-modal__actions .btn{
    flex:1 1 0;
    min-width:0;                     /* base 110px min could overflow at 320px */
    min-height:46px;
    padding:12px 14px;
  }
}
@media (max-width:420px){
  /* At the tight tier, stack so each action is a full-width, unmissable target */
  .confirm-modal__actions{ flex-direction:column-reverse; }
  .confirm-modal__actions .btn{ width:100%; }
}

/* -----------------------------------------------------------------------------
   5. QUIZ BUILDER (staff) — dense card must breathe on a 360px phone
   Base: styles.css:1499-1507 (.quiz-build/.quiz-q/.quiz-q__type/.qb-choice/
         .quiz-q__tf/.qb-tf). The header row (type-select + Remove) can crowd,
         and the fixed 220px select + 24px TF gap waste width on a phone.
--------------------------------------------------------------------------------*/
@media (max-width:520px){
  .quiz-build{ gap:12px; margin:12px 0; }
  .quiz-q{ padding:14px; gap:10px; }

  /* Header: let the type-select take the row and Remove wrap tidily beside it */
  .quiz-q > .row-between{ flex-wrap:wrap; gap:8px; }
  .quiz-q__type{
    max-width:none;                  /* base 1501 caps at 220px */
    flex:1 1 160px;
    min-width:0;
  }
  .quiz-q > .row-between > .btn{ flex:0 0 auto; }

  /* Choice rows: radio + text input; keep radio a solid 44px-tall tap zone
     while the text field stays flexible and never overflows. */
  .qb-choice{ gap:10px; align-items:center; }
  .qb-choice__radio{
    width:22px;height:22px;          /* base 20px — a touch bigger for touch */
    margin:0;
  }
  .qb-choice__text{ flex:1 1 auto; min-width:0; }   /* min-width:0 stops overflow */
  .qb-choices-head{ line-height:1.4; }
  .qb-choices-head .small{ display:inline-block; }  /* helper text wraps under label cleanly */

  /* True/False: comfortable spacing + big tap labels, wrap if needed */
  .quiz-q__tf{ gap:16px; flex-wrap:wrap; }           /* base 24px */
  .qb-tf{ min-height:44px; padding:4px 2px; }        /* whole label is tappable */
}
@media (max-width:420px){
  .quiz-q{ padding:12px; }
  .quiz-q__tf{ gap:12px; }
}

/* -----------------------------------------------------------------------------
   6. STUDENT QUIZ-TAKING POPUP (.sq-*) — centred, generous, full-width choices
   Base: styles.css:1529-1548. Widths are capped (380/460/520) for desktop; on
         phones those caps are fine but we want choices to fill the width and be
         tall enough to tap, and the submit button to be a full-width target.
--------------------------------------------------------------------------------*/
@media (max-width:520px){
  .sq-modal .modal__body{ max-width:100%; }          /* base 520px cap → fill phone */
  .sq-q{ max-width:100%; padding:14px; }             /* base 460px cap */
  .sq-choices{ max-width:100%; gap:10px; }           /* base 380px cap */
  .sq-choice{
    padding:14px 16px;
    min-height:48px;                                 /* big, easy answer target */
    align-items:center;
  }
  .sq-choice input[type="radio"]{ width:22px;height:22px; }
  .sq-opts{ max-width:100%; }                         /* review list fills width */
  .sq-q textarea{ min-height:120px; }                /* short-answer writing room */
  .sq-form__submit{ margin-top:14px; }
  .sq-form__submit .btn{ width:100%; min-height:48px; }
}

/* -----------------------------------------------------------------------------
   7. TABLET TOUCH-UP (768px) — modals get a sensible cap, not edge-to-edge
   Base: styles.css:276 max-width:480px / :1497 wide 640px already apply; here
         we just keep comfortable overlay gutters and internal scroll on tablets.
--------------------------------------------------------------------------------*/
@media (min-width:521px) and (max-width:768px){
  .modal-overlay{ padding:24px; }
  .modal{ max-height:88dvh; }
  .modal__body{ -webkit-overflow-scrolling:touch; overscroll-behavior:contain; }
  /* Foot stays inline on tablet but guarantee a decent tap height */
  .modal__foot > .btn{ min-height:44px; }
  .confirm-modal__actions .btn{ min-height:44px; }
}


/* ========================= landing-auth.css ========================= */
/* ==========================================================================
   MOBILE POLISH — public landing + auth pages
   Scope: body.landing (landing.ejs) and body.auth (login/signup/forgot/
   reset/error). Additive only. Intended to load AFTER public/css/styles.css
   (append to that file, or <link> it after styles.css) so source-order wins
   where specificity ties.

   Follows the existing breakpoint convention: max-width 720 / 520 / 420.
   The one exception is the hero-centering block at max-width:860px — that is
   the exact width at which the hero collapses from two columns to one
   (styles.css line 949 `@media(min-width:860px)`), so centering must begin
   there, not at 720, to avoid a left-aligned single-column gap on landscape
   tablets.

   OVERRIDES OF EXISTING SELECTORS ARE NOTED INLINE (— overrides: …).
   New classes are namespaced `m-` (none required so far — all fixes reuse
   existing hooks, so no .ejs structural edits were necessary).
   ========================================================================== */


/* ==========================================================================
   1) HERO — center + tighten on phones/tablets (single-column range)
   Problem: in the single-column layout the hero text (eyebrow, title, lead,
   CTAs, facts) is left-aligned and the CTAs don't stretch, so the most
   important content reads ragged and the buttons look undersized on a phone.
   ========================================================================== */
@media (max-width:860px){
  /* Turn the text block into a centered column so every child lines up. */
  .hero__text{                       /* — no prior text-align rule; additive */
    text-align:center;
    display:flex;
    flex-direction:column;
    align-items:center;
  }
  .hero__lead{                       /* — overrides margins + width on styles.css:756 */
    margin-left:auto;
    margin-right:auto;
    max-width:min(44ch,100%);        /* 44ch overflows a phone; cap to container */
  }
  /* CTAs: centered and comfortably full-width (capped so they don't sprawl
     on wide tablets). Pairs with styles.css:966 (.btn--lg{flex:1 1 100%}). */
  .hero__actions{                    /* — adds width/justify to styles.css:756 */
    justify-content:center;
    width:100%;
    max-width:360px;
  }
  /* Proof facts: centered row that wraps tidily under the CTAs. */
  .hero__facts{                      /* — adds justify to styles.css:759 */
    justify-content:center;
    text-align:center;
  }
  .hero__facts li{align-items:center;}
}

/* Large phones: slightly calmer medallion + snug rhythm. */
@media (max-width:520px){
  .medallion{width:min(272px,68vw);} /* — overrides styles.css:964 (300/72vw) */
  .hero__facts{gap:16px 26px;}       /* — overrides styles.css:967 */
}

/* Small phones (≤420): guarantee the display title never overflows and trim
   the visual so the value proposition sits higher. */
@media (max-width:420px){
  .hero{padding:80px 18px 56px;}     /* — overrides styles.css:961; top clears fixed nav */
  .hero__title{font-size:clamp(2.35rem,9vw,2.9rem);} /* — overrides clamp on styles.css:749 */
  .hero__lead{font-size:1rem;margin-bottom:22px;}    /* — overrides styles.css:755/965 */
  .medallion{width:min(232px,62vw);}
  .hero__facts b{font-size:1.5rem;}  /* — overrides styles.css:968 */
}


/* ==========================================================================
   2) NAV — keep brand + Sign-in comfortable on narrow phones
   Problem: at ~360px the serif brand word + logo + button can crowd the bar.
   ========================================================================== */
@media (max-width:420px){
  .lp-nav__inner{padding:12px 16px;gap:12px;}      /* — overrides styles.css:698 */
  .lp-nav .brand__word{font-size:1.12rem;}         /* — overrides styles.css:701 */
  .lp-nav .brand__mark{height:34px;}
  .lp-nav__cta .btn--sm{padding:8px 14px;}         /* bump tap target ~34→38px */
}


/* ==========================================================================
   3) "WHAT THE PROGRAM COVERS" — icon cards
   Already stacks to 1 col (phone) / 2 col (tablet) via auto-fit minmax(250).
   Just calm the icon badge a touch on phones so it doesn't dominate the card.
   ========================================================================== */
@media (max-width:520px){
  .cover{padding:26px 22px;}                        /* — overrides styles.css:1349 */
  .cover__icon{width:72px;height:72px;margin-bottom:16px;} /* — overrides styles.css:1651 */
  .cover__icon img{width:42px;height:42px;}         /* — overrides styles.css:1340 */
}


/* ==========================================================================
   4) FOOTER — center for a calm, balanced close on phones
   Problem: stacked footer (styles.css:979) stays left-aligned and the brand
   column is capped at 34ch, so it looks lopsided on a narrow screen.
   ========================================================================== */
@media (max-width:520px){
  .lp-footer__inner{align-items:center;text-align:center;} /* — adds to styles.css:979 */
  .lp-footer__brand{                                 /* — overrides styles.css:933 */
    align-items:center;
    max-width:none;
  }
  .lp-footer__links{align-items:center;}             /* — overrides styles.css:936 */
}


/* ==========================================================================
   5) AUTH CARD — login / signup / forgot / reset / error
   Problems on mobile:
   - The card is text-align:center (styles.css:115), so form LABELS render
     centered above left-aligned inputs — reads inconsistent.
   - The submit button sits a hair under the 44px tap-target minimum.
   Fix: left-align form internals only (brand, sub-heading and post-form
   links stay centered), and make the primary submit prominent + ≥48px.
   ========================================================================== */

/* Left-align everything inside the <form>; the brand block, .auth-sub and the
   footer links live OUTSIDE the form, so they stay centered. */
body.auth .auth-card form{text-align:left;} /* — counters .auth-card text-align:center (styles.css:115) */
body.auth .auth-card .field__label{text-align:left;}
body.auth .auth-card .field-error,
body.auth .auth-card .field-note{text-align:left;}

/* Prominent, thumb-friendly submit on every auth screen. */
body.auth .auth-card .btn--block{      /* — augments .btn base (styles.css:59) */
  min-height:50px;
  font-size:1rem;
  margin-top:6px;
}

/* Comfortable field rhythm + full-width inputs (inputs are already 100%). */
@media (max-width:520px){
  body.auth .auth-card{padding:22px 20px;} /* — overrides .card padding (styles.css:643) for auth only */
  body.auth .field{margin-bottom:18px;}    /* — overrides styles.css:75 */
}

/* Very small phones: give the card a little more width. */
@media (max-width:420px){
  body.auth .auth-wrap{padding:18px 14px;} /* — overrides styles.css:111 (24px) */
}


/* ========================= cohort.css ========================= */
/* ==========================================================================
   COHORT HUB — MOBILE POLISH (proposal, additive)
   Scope: views/cohort/*.ejs + partials/cohort-tabs.ejs only.
   Design system: reuses tokens from public/css/styles.css :root.
   Breakpoints follow the existing app: 720 / 520 / 420 (phones first).

   Every rule below is either (a) a safe, size-agnostic wrap/overflow fix on a
   COHORT-ONLY selector, or (b) a phone-scoped layout refinement. Each block
   notes the existing selector it overrides and why. No base primitive
   (.container / .stat-grid / .list / .btn / tables) is restyled globally, and
   nothing inside a modal / form / quiz-builder is touched.
   ========================================================================== */

/* ==========================================================================
   1) CONTEXTUAL TAB BAR  (.tabs--ctx — cohort-tabs partial, phase.ejs ptabs)
   ------------------------------------------------------------------------
   Base .tabs already does `overflow-x:auto; white-space:nowrap`, so it
   scrolls — but on a phone it (a) shows a chunky native scrollbar that fights
   the 1px underline, (b) sits inset inside the container padding so the last
   tab looks arbitrarily clipped, and (c) has smallish 10px/14px tap padding.
   Fixes: momentum scroll, hidden scrollbar, edge-to-edge bleed so the strip
   reads as "swipe me", scroll-snap, and bigger touch targets.
   NOTE: .tabs--ctx is the shared cohort-hub tab component; student/project.ejs
   reuses it and gets the identical (beneficial) treatment.
   ========================================================================== */
.tabs--ctx {
  -webkit-overflow-scrolling: touch;   /* momentum scroll on iOS */
  scroll-snap-type: x proximity;
  scrollbar-width: none;               /* Firefox: hide scrollbar */
  /* Base .tabs sets gap:4px. With multi-word labels sitting next to each other
     ("My Project" / "Self-Assessment") 4px reads as one run-on strip — the eye
     can't tell where one tab stops. Widen it here only, so the tighter gap
     still applies to any other .tabs bar. */
  gap: var(--sp-3);
}
.tabs--ctx::-webkit-scrollbar { display: none; }  /* WebKit: hide scrollbar */
.tabs--ctx .tab { scroll-snap-align: start; }

@media (max-width: 520px) {
  /* Edge-to-edge bleed: cancel the container's 14px side padding, then re-pad
     inside so the first/last tab keep a comfortable inset while the strip runs
     to both screen edges (standard mobile scroller affordance). The 1px
     bottom border now spans the full width. */
  .tabs--ctx {
    margin-left: -14px;
    margin-right: -14px;
    padding-left: 14px;
    padding-right: 14px;
    scroll-padding-left: 14px;
  }
  /* Bigger, comfier tap targets (was 10px 14px). */
  .tabs--ctx .tab { padding: 12px 16px; }
}

/* ==========================================================================
   2) OVERVIEW STAT CARDS  (.cohort-stats — NEW class added to the
      overview.ejs .stat-grid; requires the 1-line EJS edit noted in report)
   ------------------------------------------------------------------------
   The shared .stat-grid uses minmax(150px,1fr), so the 3 cohort stats
   (Students / Phases / Sessions) fall to 2-up on a phone, leaving one card
   stretched full-width and lopsided. Scoped to .cohort-stats only (NOT the
   global .stat-grid primitive), lay the three small numbers as a clean,
   centered 3-across row — premium and balanced.
   ========================================================================== */
@media (max-width: 520px) {
  .cohort-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .cohort-stats .stat {
    align-items: center;
    text-align: center;
    padding: 14px 8px;      /* tighter than the base .card 18px so 3 fit calmly */
    gap: 2px;
  }
  .cohort-stats .stat__value { font-size: 1.55rem; }
  .cohort-stats .stat .muted { font-size: .74rem; }
}
@media (max-width: 360px) {
  .cohort-stats { gap: 8px; }
  .cohort-stats .stat { padding: 12px 6px; }
  .cohort-stats .stat__value { font-size: 1.35rem; }
}

/* ==========================================================================
   3) PHASE DRILL ROWS  (.drill-wrap — overview.ejs staff view)
   ------------------------------------------------------------------------
   Each phase is a .drill-card (num + title + → arrow) with an Edit/Delete
   .row-end tucked underneath. On a narrow screen keep that action pair from
   crowding: guarantee wrap + a real gap. (The drill-card itself already has a
   @380 rule in the base sheet; nothing there to change.)
   ========================================================================== */
@media (max-width: 520px) {
  .drill-wrap .row-end { flex-wrap: wrap; gap: 8px; }
  /* Give the two small buttons a fair share so they read as proper targets. */
  .drill-wrap .row-end .btn--sm { flex: 1 1 auto; min-width: 96px; }
  .drill-wrap .row-end form { flex: 1 1 auto; display: flex; }
  .drill-wrap .row-end form .btn { width: 100%; }
}

/* ==========================================================================
   4) HOMEWORK MATERIAL LINK  (.hw-material — session.ejs, phase.ejs,
      homework-detail.ejs)  — SIZE-AGNOSTIC OVERFLOW FIX
   ------------------------------------------------------------------------
   .hw-material is display:inline-flex with an icon + a raw filename text node.
   A long, unbroken filename (e.g. Chapter_3_Reading_Final_v2.pdf) can't break
   and pushes past the card edge -> content clipped by html{overflow-x:hidden}.
   Let the label wrap under the icon and break long tokens. Applied at all
   sizes because the overflow is a correctness bug, not a phone-only nicety.
   ========================================================================== */
.hw-material {
  max-width: 100%;
  flex-wrap: wrap;                 /* label can drop below the icon if needed */
  align-items: center;
  overflow-wrap: anywhere;         /* break very long filenames */
  word-break: break-word;
}

/* ==========================================================================
   5) SESSION PAGE SECTIONS  (session.ejs — cohort-only selectors)
   ------------------------------------------------------------------------
   Header, objective, and the compact read-only attendance summary. (The
   take-attendance sheet lives in a modal and is the forms agent's; untouched.)
   ========================================================================== */
@media (max-width: 520px) {
  /* Title chip + text should wrap gracefully and not collide with the date. */
  .session-head { gap: 10px; }
  .session-head__title { gap: 8px; }
  /* Compact attendance summary: a touch larger for legibility on a phone, and
     let each status line's tag sit cleanly above its wrapped list of names. */
  .att-compact { font-size: .82rem; line-height: 1.7; }
  .att-compact__tag { margin-right: 4px; }
  /* Section headings sit a little tighter to their meta line. */
  .session-section__title { font-size: 1.02rem; }
}

/* ==========================================================================
   6) HOMEWORK-DETAIL QUESTION CARDS  (.q-card — homework-detail.ejs)
   ------------------------------------------------------------------------
   .q-card__head is a non-wrapping flex row (prompt + type pill). A long prompt
   squeezes the pill; allow the head to wrap and the title to shrink/wrap.
   ========================================================================== */
@media (max-width: 520px) {
  .q-card__head { flex-wrap: wrap; gap: 8px; }
  .q-card__title { min-width: 0; }
  .q-choice { padding: 10px 12px; }   /* base is 12px 16px — reclaim width */
}

/* ==========================================================================
   7) HOMEWORK-DETAIL HEADER ACTIONS  (.hw-actions — homework-detail.ejs)
   ------------------------------------------------------------------------
   Back / Edit / Delete cluster. Base right-aligns it; on a phone it wraps
   under the title, so left-align it to line up with the title for a tidy edge.
   ========================================================================== */
@media (max-width: 520px) {
  .hw-actions { justify-content: flex-start; width: 100%; }
}

/* ==========================================================================
   8) PHASE — INLINE CHALLENGE MARKING GRID  (phase.ejs, view=challenge)
   ------------------------------------------------------------------------
   Each roster row carries a <select> + Save button in an .inline form. Ensure
   the select can shrink and the control pair wraps instead of overflowing the
   card on a narrow screen. Cohort-scoped via .select-sm's row context.
   ========================================================================== */
@media (max-width: 520px) {
  .list__row form.inline { flex-wrap: wrap; gap: 6px; }
  .select-sm { flex: 1 1 auto; min-width: 0; }
}


/* ========================= student.css ========================= */
/* =====================================================================
   STUDENT AREA — MOBILE POLISH
   Scope: views/student/*.ejs + views/partials/ring.ejs
   Additive only — intended to load AFTER public/css/styles.css.
   Breakpoints follow the app's existing set: 720 (tablet) / 520 (phone)
   / 420 (small phone).  Everything here targets STUDENT-UNIQUE classes
   (.attn-*, .mini-cal/.cal--mini, .pthread/.pcomment, .makamak-*,
   .tab-locked) or explicitly-added hook classes (.stud-quiz,
   .quiz-actions), plus the contextual .tabs--ctx used by the project
   tab bar.  Shared base primitives (.stat-grid, .list, .grid-2, .btn,
   .field, .section-card, tables) are NOT restyled — descendant rules
   like `.pthread textarea` only reach elements inside student widgets.
   ===================================================================== */


/* =====================================================================
   1. DASHBOARD — mini calendar widget  (dashboard.ejs)
   The 7-col grid is repeat(7,1fr) so it can never overflow horizontally;
   these rules just make the cells a comfortable tap size and keep the
   legend / "what's next" list calm and centered on a phone.
   Overrides existing:
     - `@media(max-width:900px){.cal--mini{--mini-cell:40px}}`  (line ~1044)
     - `@media(max-width:380px){.cal--mini{--mini-cell:32px}...}` (line ~1045)
   We load later, so our 520/420 values win in the 381–520 band and below.
   ===================================================================== */
@media (max-width: 520px) {
  .cal--mini { --mini-cell: 40px; }
  .mini-cal__grid { gap: 5px; }
  .mini-cal__cell { min-height: var(--mini-cell); }
  .mini-cal__day { font-size: .82rem; }
  .mini-cal__weekday { font-size: .62rem; }
  /* legend reads as a centered caption strip rather than a left-aligned row */
  .mini-cal__legend { justify-content: center; gap: var(--sp-1) var(--sp-4); }
  .mini-cal__item-title { font-size: .9rem; }
  .mini-cal__item-when  { font-size: .78rem; }
}
@media (max-width: 420px) {
  .cal--mini { --mini-cell: 36px; }
  .mini-cal__grid { gap: 4px; }
  .mini-cal__day { font-size: .78rem; }
}


/* =====================================================================
   2. PROFILE — attendance rings per phase  (profile.ejs)
   Goal: the rings "wrap and center nicely" and the missed-session lists
   stay readable.  On phones we show a compact 2-up grid (collapsing to a
   single column on the smallest screens), keep every card centered, and
   let a long missed-session title wrap instead of colliding with its pill.
   Overrides existing:
     - `.attn-phases{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}` (line ~1477)
   ===================================================================== */
@media (max-width: 720px) {
  .attn-phases {
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: var(--sp-3);
  }
}
@media (max-width: 520px) {
  .attn-phases { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
  .attn-phase  { padding: var(--sp-4); }
  .attn-phase__title { margin-top: var(--sp-3); }
}
@media (max-width: 420px) {
  /* one clean column so narrow cards never crush the missed-session list */
  .attn-phases { grid-template-columns: minmax(0, 1fr); }
  .attn-phase  { padding: var(--sp-4) var(--sp-6); }
}
/* Missed-session rows: allow the title to wrap; the pill trails on its
   own line if there isn't room, instead of overflowing the card. */
ul.attn-phase__missed li { flex-wrap: wrap; gap: 4px 6px; }
.attn-item__title { word-break: break-word; line-height: 1.3; }
.attn-item__status { margin-left: auto; }


/* =====================================================================
   3. PROJECT — 3-tab bar  (project.ejs, .tabs--ctx)
   The bar already scrolls horizontally (overflow-x on .tabs); on a phone
   we give each tab a bigger tap target and hide the scrollbar so the
   swipe feels native.  Scoped to .tabs--ctx (project / staff context).
   ===================================================================== */
@media (max-width: 520px) {
  .tabs--ctx { gap: 2px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .tabs--ctx::-webkit-scrollbar { display: none; }
  .tabs--ctx .tab { padding: 13px 14px; font-size: .96rem; }
  .proj-lock-pill { font-size: .62em; }
}


/* =====================================================================
   4. PROJECT — comment thread & student reply form  (_comments.ejs)
   "Reply forms comfortable to type in": bigger textareas, 16px text so
   iOS doesn't zoom on focus, a shallower reply indent so the conversation
   keeps its width, and a full-width Reply button that's easy to hit.
   Scoped to .pthread / .pcomment (student-only widget).
   Overrides existing:
     - `.pcomment--reply{margin-left:var(--sp-4)}` (line ~1466)
   ===================================================================== */
.pthread textarea { min-height: 88px; }
.pcomment__reply-form textarea { min-height: 92px; }

@media (max-width: 520px) {
  .pthread textarea { font-size: 16px; line-height: 1.45; }
  .pcomment--reply { margin-left: var(--sp-3); }
  .pcomment__body  { line-height: 1.5; }
  /* full-width, thumb-friendly Reply button */
  .pcomment__reply-form .row-end { justify-content: stretch; }
  .pcomment__reply-form .btn { width: 100%; padding-top: 11px; padding-bottom: 11px; }
}
@media (max-width: 420px) {
  /* drop the indent entirely so replies use the whole card width */
  .pcomment--reply { margin-left: 0; }
}


/* =====================================================================
   5. ASSIGNMENT — quiz take page  (assignment.ejs, hook: .stud-quiz)
   Scoped to .stud-quiz so the shared .quiz-* styles used by exams/take
   and exams/grade are untouched.  Bigger answer rows, larger radios,
   16px short-answer textareas, and the two action buttons stack full
   width instead of crowding on one line.
   ===================================================================== */
.quiz-actions { display: flex; gap: var(--sp-3); }

@media (max-width: 520px) {
  .stud-quiz .quiz-choice { padding: 14px 16px; min-height: 52px; }
  .stud-quiz .quiz-choice input { width: 22px; height: 22px; flex: 0 0 auto; }
  .stud-quiz textarea { font-size: 16px; min-height: 120px; line-height: 1.45; }
  /* stack "Save progress" / "Submit answers" — no side-by-side squeeze */
  .quiz-actions { flex-direction: column; gap: var(--sp-2); }
  .quiz-actions .btn { width: 100%; }
  /* review-mode option list: a touch more breathing room */
  .quiz-opts li { padding: 11px 14px; }
  .quiz-result { gap: var(--sp-3); }
}


/* =====================================================================
   6. ASSIGNMENT — PDF upload  (assignment-upload.ejs)
   Keep the file input from forcing horizontal overflow and give the
   view/download action buttons a comfortable, full-width stack.
   Scoped via .stack (used only on this page's upload/submission blocks).
   ===================================================================== */
@media (max-width: 520px) {
  .stack input[type="file"] { max-width: 100%; box-sizing: border-box; }
  .stack .row-end { flex-wrap: wrap; gap: var(--sp-2); }
  .stack .row-end .btn { flex: 1 1 auto; text-align: center; }
}


/* =====================================================================
   7. PROJECT — locked-tab & Makamak placeholder  (_tab-locked.ejs,
   project.ejs .makamak-placeholder)
   Small centering / stacking tidy-ups so these panels feel intentional
   on a narrow screen.
   ===================================================================== */
@media (max-width: 520px) {
  .tab-locked { flex-direction: column; text-align: center; gap: var(--sp-2); }
  .tab-locked__icon { margin: 0 auto; }
  .makamak-placeholder .btn[disabled] { width: 100%; }
}


/* ========================= staff.css ========================= */
/* ==========================================================================
   staff.css — MOBILE polish for the Staff areas ONLY.
   Scope: views/director/{dashboard,users,cohorts,cohort,settings}.ejs
          views/facilitator/{dashboard,attendance,student-report,
                             assignment,assignments,roster,sessions}.ejs
   (NOT director/landing.ejs.)

   Rules are ADDITIVE and reuse the existing design tokens (--sp-*, --border,
   --primary, --success, --danger, --r-pill …). Everything is namespaced to
   staff-only classes (.att-*, .seg INSIDE .att-row, .ppl-group, .rep-phase)
   or to two tiny marker classes I added to my own EJS (.act-group, .att-save,
   .mcq-choices) — so nothing here can leak into the primitives / modals /
   student / landing areas owned by other agents.

   Breakpoints follow the existing file: 720 (tablet) / 600 & 520 (phone).

   The app already has an `html{overflow-x:hidden}` guard, but the goal here is
   to remove the CAUSES of overflow (a nowrap pill strip, a rigid inline
   segmented control) rather than clip them, and to grow tap targets to ~44px.
   ========================================================================== */


/* ==========================================================================
   1. ATTENDANCE MARKING SHEET  (facilitator/attendance.ejs)
   The densest staff screen. Base rules:
     .att-grid { flex column; gap:--sp-3 }
     .att-row  { flex; justify-content:space-between; flex-wrap:wrap }
     .seg      { inline-flex; border-radius pill; overflow hidden }
     .seg__btn { padding:7px 12px }  ← ~32px tall, too small to tap on a phone
   On a 360px phone the three-button segmented control + the name do not sit
   comfortably on one line, so it wraps to a cramped, left-aligned strip.
   Fix: below 600px the name takes its own line and the segmented control
   becomes a full-width, equal-thirds control with big tap targets.
   All .seg overrides are scoped to `.att-row` so the primitive .seg is
   untouched elsewhere.
   ========================================================================== */

/* Clear separation between students, comfortable rhythm (all widths). */
.att-grid { gap: var(--sp-2); }
.att-grid .att-row { padding: var(--sp-3) 0; }
.att-grid .att-row + .att-row { border-top: 1px solid var(--border); }
.att-row__name strong { display: block; line-height: 1.35; }

@media (max-width: 600px) {
  /* Stack: name on line 1 (full width), control on line 2 (full width). */
  .att-row { align-items: stretch; gap: var(--sp-2); }
  .att-row__name { flex: 1 1 100%; }

  .att-row .seg {
    display: flex;
    width: 100%;
    border-radius: 14px;   /* softer than the pill when it spans the row */
  }
  .att-row .seg__btn {
    flex: 1 1 0;
    justify-content: center;
    min-height: 46px;      /* generous tap target */
    padding: 12px 6px;
    font-size: .9rem;
  }

  /* The read-only variant (canMark=false): name left, status pill right,
     kept on one line since the pill is short. */
  .att-row .pill { align-self: center; flex: 0 0 auto; }

  /* Save action becomes a full-width primary button, easy to reach. */
  .att-save { display: block; margin-top: var(--sp-4); }
  .att-save .btn { width: 100%; padding: 14px 18px; font-size: 1rem; }
}

/* Tablet: control can breathe a little wider but still clearly tappable. */
@media (min-width: 601px) and (max-width: 900px) {
  .att-row .seg__btn { padding: 9px 16px; }
}


/* ==========================================================================
   2. PEOPLE — role/cohort groups  (director/users.ejs)
   Base: .ppl-group{margin-top:26px}
         .ppl-group__title{display:flex;align-items:center;gap:10px} ← no wrap
   The group title is  [icon] [cohort name] [count]  and does not wrap, so a
   long cohort name + count can push past the viewport. The people rows use the
   shared .list primitive (owned by another agent) — only the group chrome and
   the tap size of the per-row Edit button are staff-specific and handled here.
   ========================================================================== */
.ppl-group__title { flex-wrap: wrap; row-gap: 4px; }
.ppl-group__title .muted { margin-left: auto; }   /* count sits at the far end */

@media (max-width: 600px) {
  .ppl-group { margin-top: 20px; }
  .ppl-group__title { font-size: 1.08rem; }
  .ppl-group__title .muted { margin-left: 0; }     /* wrap under, don't stretch */
  /* Comfortable Edit target on the dense people rows. */
  .ppl-group .list__actions .btn--sm { min-height: 40px; padding: 8px 16px; }
}


/* ==========================================================================
   3. STUDENT REPORT — tabbed mini-lists  (facilitator/student-report.ejs)
   Base: .rep-phase__head{display:flex;justify-content:space-between;gap:10px}
         (no flex-wrap → the "x/y attended" pill can crowd a long phase name)
   .mini-row / .mini-list are shared, but these tweaks are scoped to .rep-phase
   so only the report's per-phase groups are affected.
   ========================================================================== */
.rep-phase__head { flex-wrap: wrap; }
.rep-phase__head strong { min-width: 0; overflow-wrap: anywhere; }
.rep-phase__head .pill { flex: 0 0 auto; }

@media (max-width: 600px) {
  /* Give the per-session rows a touch more vertical room; let the right-hand
     meta (date · status pill) wrap under the title instead of being squeezed. */
  .rep-phase .mini-row { gap: 8px; padding: 12px 6px; }
  .rep-phase .mini-row__meta { white-space: normal; }
}


/* ==========================================================================
   4. TABS — cohort-manage (director/cohort.ejs) & report (student-report.ejs)
   Base .tab padding is 10px 14px (~40px tall). The .tabs bar already scrolls
   horizontally; here we (a) grow the touch height a little on phones and
   (b) let the row scroll edge-to-edge so the active underline is never clipped
   against the container padding. Kept intentionally light — .tab/.tabs are a
   semi-shared component; see report note.
   ========================================================================== */
@media (max-width: 520px) {
  /* The bleed must cancel EXACTLY the container's side padding, which is 14px
     at <=600px (styles.css:649) — `--sp-4` (16px) overshot it by 2px a side and,
     because this block is later in the file than the identical-specificity
     `.tabs--ctx` block above, it clobbered that one's correct -14px too. That
     2px was the whole L34 horizontal overflow at 390 (and 4px at 320, where the
     container drops to 12px — see the <=380px block right below). */
  .tabs { margin-left: -14px; margin-right: -14px;
          padding: 0 14px; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs .tab { padding: 12px 14px; }
}
/* Very small phones: .container drops to 12px side padding (styles.css:1746),
   so the bleed has to follow it or the strip pokes 2px past the viewport.
   Covers BOTH tab variants (.tabs and .tabs--ctx). */
@media (max-width: 380px) {
  .tabs, .tabs--ctx {
    margin-left: -12px; margin-right: -12px;
    padding-left: 12px; padding-right: 12px;
    scroll-padding-left: 12px;
  }
}


/* ==========================================================================
   5. ASSIGNMENT DETAIL — MCQ question rows  (facilitator/assignment.ejs)
   Each question is a column .mini-row whose choices live in .mini-row__meta.
   But .mini-row__meta is `white-space:nowrap; text-align:right` — with several
   choice pills that forces a horizontal overflow on a phone. The .mcq-choices
   marker (added to that span) lets the pills wrap into a tidy left-aligned
   cluster. Scoped entirely to the marker class.
   ========================================================================== */
.mcq-choices {
  white-space: normal;
  text-align: left;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  width: 100%;
  margin-top: 6px;
}


/* ==========================================================================
   6. PAGE-HEAD / SECTION ACTION CLUSTERS  (.act-group marker)
   The header CTA clusters use the generic `.inline` (display:inline), which
   gives buttons no gap control and no sane wrapping on a phone. I added an
   `.act-group` marker beside `.inline` on the staff clusters that hold 2+
   controls (director dashboard, cohort phase header, facilitator assignments).
   ========================================================================== */
.act-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }

@media (max-width: 520px) {
  /* On the narrowest phones the two header CTAs go full-width, side by side,
     so they read as primary actions rather than a cramped inline pair. */
  .act-group { width: 100%; }
  .act-group > .btn,
  .act-group > form > .btn { flex: 1 1 140px; }
  .act-group .field { flex: 1 1 100%; margin-bottom: 0; }  /* cohort-switch select */
}


/* ==========================================================================
   7. GENERAL STAFF COMFORT
   The page-head on staff pages is `.page-head row-between`; the base already
   flips it to align-items:flex-start under 600px. Just add a little gap so the
   title block and the action cluster don't touch when they stack.
   ========================================================================== */
@media (max-width: 600px) {
  .page-head.row-between { row-gap: var(--sp-3); }
}


/* ========================= calendar.css ========================= */
/* ==========================================================================
   CALENDAR — MOBILE POLISH (additive)
   Scope: views/calendar/month.ejs only.
   Author: mobile/responsive UI pass.

   HOW THIS IS ORGANISED
   ---------------------
   Everything here is additive and lives inside media queries, so it changes
   NOTHING on desktop. It reuses the existing design tokens in
   public/css/styles.css :root (--primary, --muted, --border, --sp-*, --r-*,
   --grad-primary, shadows). Existing breakpoints in the app are 720 / 600 /
   520 / 420 / 380 — this file uses 720 (tablet), 520 (phone, the primary
   target for 360–430px devices) and 420 (small-phone refinements).

   THE KEY PROBLEM IT SOLVES
   -------------------------
   `.cal-grid{min-width:640px}` (styles.css:456) forces a 7-column month grid
   that is ~640px wide. On a 360–430px phone that spills into the horizontal
   scroller `.cal-scroll{overflow-x:auto}` (styles.css:455/506), so a phone
   user must swipe sideways inside the card to see the back half of every week,
   and each day cell is far too narrow to read its event chips.

   THE FIX (phones ≤520px): reflow the month grid into a premium AGENDA LIST.
   Only days that actually have events are shown, each as a full-width row with
   a legible date rail (weekday + day number) and comfortably sized, tappable
   event chips. No horizontal scroll, big tap targets, everything centred in
   the card. Tablets (≤720px) keep the grid but get tighter, comfier spacing.
   ========================================================================== */


/* ==========================================================================
   TABLET (≤720px) — keep the 7-col grid, just make it breathe
   ========================================================================== */
@media (max-width: 720px) {
  /* Month title (gradient serif) can be large; keep it from crowding the
     prev/next links. Overrides .cal-head__label min-width:180px (styles.css:453). */
  .cal-head__label { min-width: 0; font-size: 1.4rem; }

  /* Bigger, easier prev/next hit areas. .back-link base is a plain inline
     link (styles.css:101) — give it real padding without changing colour. */
  .cal-head .back-link {
    padding: 8px 10px;
    border-radius: 10px;
    font-weight: 700;
  }

  /* Tap-friendly filter chips. Base .cal-chip is 7px 14px (styles.css:492). */
  .cal-chip { min-height: 40px; }
}


/* ==========================================================================
   PHONE (≤520px) — reflow the month grid into an AGENDA LIST
   ========================================================================== */
@media (max-width: 520px) {

  /* ---- 1. Kill the horizontal scroller + the 640px grid floor ------------ */
  /* Override .cal-scroll{overflow-x:auto} (styles.css:455/506): on a phone we
     no longer need to scroll sideways because the grid is now a vertical list. */
  .cal-scroll { overflow-x: visible; padding: 0 12px 16px; }

  /* Override .cal-grid (styles.css:456): drop grid + min-width:640, stack. */
  .cal-grid {
    display: block;
    min-width: 0;
    gap: 0;
  }

  /* Weekday header strip (Sun…Sat) is meaningless in a list — the weekday now
     rides on each row's date rail instead. Hides .cal-weekday (styles.css:457). */
  .cal-weekday { display: none; }

  /* Leading/trailing padding days carry no information in a list. Hides
     .cal-cell--empty (styles.css:461/514). */
  .cal-cell--empty { display: none; }

  /* Days with no events would just be empty rows — hide them so the agenda is
     a tight list of only the days that matter. Relies on :has() (already used
     in this codebase, e.g. .sq-choice:has(input:checked)). When a month has
     zero events every row is hidden and the existing .cal2-empty note shows. */
  .cal-cell:not(.cal-cell--empty):not(:has(.cal-event)) { display: none; }

  /* ---- 2. Turn each remaining day cell into an agenda ROW ---------------- */
  /* Base .cal-cell is a flex column card (styles.css:459/510). Re-lay it out as
     a 2-track grid: a fixed date rail on the left, events filling the right.
     grid areas:  [ wd  | events ]
                  [ day | events ] */
  .cal-cell {
    display: grid;
    grid-template-columns: 46px minmax(0, 1fr);
    grid-template-areas: "wd events" "day events";
    align-items: start;
    gap: 2px 12px;
    min-height: 0;                 /* override min-height:112px (styles.css:510) */
    margin: 0 0 10px;
    padding: 12px 14px;
    border-radius: 14px;
  }
  .cal-cell:last-child { margin-bottom: 0; }

  /* Weekday label — pulled from data-wd on the cell (set in month.ejs). It is
     ONLY rendered here, so desktop never shows it. */
  .cal-cell::before {
    content: attr(data-wd);
    grid-area: wd;
    font-size: .62rem;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
    text-align: center;
  }

  /* Day number — was a tiny 26px muted badge (styles.css:515); make it the
     prominent numeral of the date rail. */
  .cal-cell__day {
    grid-area: day;
    width: 100%;
    height: auto;
    font-size: 1.35rem;
    font-weight: 800;
    color: var(--text);
    line-height: 1.1;
    justify-content: center;
  }

  /* Keep the "today" treatment premium: circular gradient chip around the
     number (reuses .cal-cell.is-today .cal-cell__day, styles.css:517), and a
     soft ring on the whole row. */
  .cal-cell.is-today {
    box-shadow: 0 0 0 2px var(--primary), var(--sh-sm);
  }
  .cal-cell.is-today .cal-cell__day {
    width: 34px;
    height: 34px;
    margin: 0 auto;
    border-radius: 50%;
    font-size: 1.05rem;
    align-items: center;
  }

  /* ---- 3. Legible, tappable event chips --------------------------------- */
  /* Base chips are dense .74–.75rem pills (styles.css:466/520). In the agenda
     they get room to show their full title and a proper 44px tap target. */
  .cal-cell__events {
    grid-area: events;
    gap: 6px;
    overflow: visible;            /* override overflow:hidden (styles.css:465) */
  }
  .cal-event {
    min-height: 44px;
    padding: 9px 12px;
    font-size: .9rem;
    border-radius: 10px;
    border-left-width: 4px;
    gap: 8px;
  }
  /* Titles were clipped to one line for the grid; in a full-width row they can
     wrap to two. Overrides .cal-event__txt{white-space:nowrap} (styles.css:472). */
  .cal-event__txt {
    white-space: normal;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .cal-event__dot { width: 9px; height: 9px; }
  /* Locked (student) chips keep their lock icon pinned to the right. */
  .cal-event__lock { font-size: .85em; }

  /* ---- 4. Header, nav row, filters — centred + comfortable -------------- */
  /* "+ Schedule" becomes a full-width, centred, thumb-friendly button under
     the title instead of squeezing beside it. .page-head.row-between already
     wraps (styles.css:98) and goes align-items:flex-start on mobile
     (styles.css:645), so width:100% drops it onto its own line. */
  .page-head.row-between .cal-add {
    width: 100%;
    justify-content: center;
    min-height: 46px;
    margin-top: 4px;
  }

  /* Month prev / label / next row: keep all three on one line, centred label. */
  .cal-head { padding: 18px 14px 4px; gap: 6px; }
  .cal-head__label { font-size: 1.35rem; flex: 1; text-align: center; }
  .cal-head .back-link {
    padding: 8px 8px;
    font-size: .82rem;
    white-space: nowrap;
  }

  /* Today / "show all" pills: bigger tap targets, already centred
     (.cal-today-row justify-content:center, styles.css:1221). Base .pill is
     tiny 3px 10px (styles.css:173). */
  .cal-today-row .pill {
    padding: 9px 16px;
    font-size: .82rem;
    min-height: 40px;
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--border);
    background: #fff;
    color: var(--primary);
    border-radius: var(--r-pill);
  }

  /* Filter bars: centre the chips and let the label sit above them. */
  .cal-filter { justify-content: center; padding: 6px 12px 14px; }
  .cal-filter--phase { margin: 0 12px; padding: 12px 0 14px; }
  .cal-filter__label { width: 100%; text-align: center; margin: 0 0 2px; }
  .cal-chip { min-height: 42px; padding: 8px 16px; }

  /* ---- 5. Legend — stacked, centred, easy to scan ----------------------- */
  /* Base .cal-legend is a wrap row with 20px gaps (styles.css:1244). On a phone
     stack the groups vertically and centre each so it reads as a tidy key. */
  .cal-legend {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
    margin-top: 14px;
    padding: 14px;
  }
  .cal-legend__group {
    justify-content: center;
    gap: 8px 12px;
    padding-bottom: 12px;
    border-bottom: 1px dashed var(--border);
  }
  .cal-legend__group:last-child { padding-bottom: 0; border-bottom: none; }
  .cal-legend__title { width: 100%; text-align: center; margin-bottom: 2px; }

  /* Empty-month note already has a ≤520 rule (styles.css:1629); nudge spacing. */
  .cal2-empty { padding: 22px 12px; }
}


/* ==========================================================================
   SMALL PHONE (≤420px) — final squeeze for 360px-class devices
   ========================================================================== */
@media (max-width: 420px) {
  .cal-scroll { padding: 0 10px 14px; }
  .cal-head__label { font-size: 1.2rem; }
  .cal-head .back-link { font-size: .78rem; padding: 8px 6px; }

  /* Slightly narrower date rail so long titles get maximum width. */
  .cal-cell { grid-template-columns: 40px minmax(0, 1fr); padding: 11px 12px; gap: 2px 10px; }
  .cal-cell__day { font-size: 1.25rem; }
  .cal-event { font-size: .88rem; padding: 9px 11px; }

  /* Keep the "+ Schedule" plus badge from feeling cramped. */
  .cal-add__plus { width: 22px; height: 22px; }
}


/* ===========================================================================
   Follow-up tweaks (2026-07): clickable-link helper, quiz-answer review modal,
   static (non-scrolling) modal variant, and a link-styled button.
   =========================================================================== */

/* Links inside user-entered copy (homework/exam/phase descriptions). */
.hw-desc, .phase-desc{ overflow-wrap:anywhere; }
.hw-desc a{ color:var(--primary); font-weight:600; text-decoration:underline; }
.hw-desc a:hover{ color:var(--gold-dark); }

/* ---- Staff formatting in prose (lib/richtext.js renders these) ------------
   The four marks a staff member can apply to a homework description or a
   session objective. These class names are the RENDERER's output contract —
   .rt-c--KEY must exist for every key in COLORS there, or a colour silently
   renders as plain text. The hex values are that file's, kept in step by hand;
   they are the brand palette darkened enough to stay readable as body text on
   an ivory background.
   Deliberately NOT scoped to .hw-desc: the same marks render in a session
   objective, and will render anywhere else the field is shown next. */
.rt-u{ text-decoration:underline; text-underline-offset:2px; }
.rt-c{ font-weight:600; }              /* colour alone is never the only signal */
.rt-c--teal{ color:#065053; }
.rt-c--gold{ color:#9A6510; }
.rt-c--red{ color:#A62D3B; }
.rt-c--green{ color:#2F6B45; }

/* A <button> that looks and sits like a link (e.g. a clickable student name). */
.linklike{ background:none; border:0; padding:0; margin:0; font:inherit; color:var(--primary);
  cursor:pointer; text-align:left; }
.linklike:hover{ text-decoration:underline; }

/* Static modal — grows with its content instead of scrolling internally. Used
   by the Edit-exam form so the whole form is visible at once. The overlay still
   scrolls the page if the viewport is very short. */
.modal--static{ max-height:none; }
.modal--static .modal__body{ overflow:visible; max-height:none; }

/* Quiz answer-review modal (staff clicking a student's name in results). */
.ans-q{ padding:12px 0; border-top:1px solid var(--border); }
.ans-q:first-of-type{ border-top:0; }
.ans-q__prompt{ font-weight:600; margin-bottom:8px; }
.ans-q__text{ background:var(--primary-soft); border-radius:10px; padding:10px 12px; white-space:pre-wrap; }
.ans-opts{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px; }
.ans-opts li{ display:flex; align-items:center; gap:8px; padding:8px 12px; border:1px solid var(--border);
  border-radius:10px; }
.ans-opts li.is-correct{ border-color:var(--success); background:color-mix(in srgb,var(--success) 10%,white); }
.ans-opts li.is-wrong{ border-color:var(--danger); background:color-mix(in srgb,var(--danger) 8%,white); }
.ans-opts__mark{ width:1.2em; font-weight:800; }
.ans-opts li.is-correct .ans-opts__mark{ color:var(--success); }
.ans-opts li.is-wrong .ans-opts__mark{ color:var(--danger); }


/* ###########################################################################
   EXAM/HOMEWORK/PHASE/CALENDAR reorg pass (2026-07). Merged from 4 view audits.
   ########################################################################### */


/* ========================= css3/calendar.css ========================= */
/* ===========================================================================
   Calendar "Schedule something" modal — layout polish
   ---------------------------------------------------------------------------
   Proposed additions for public/css/styles.css. Everything is scoped to
   .sch-modal (the modal now carries `modal modal--wide sch-modal`) so it never
   touches the shared .modal / .modal--wide session quiz modal.

   Reuses existing tokens only: --sp-*, --border, --surface. Respects the
   existing 640 / 560 grid breakpoints (grid-2 → 2col @640, grid-3 → 3col @560).

   Fixes:
     • Width no longer jumps LEFT/RIGHT when switching What (Session/Homework/
       Exam). The jump was the body's overflow-y:auto scrollbar appearing for
       tall types and vanishing for short ones. scrollbar-gutter:stable reserves
       that track permanently, so the content column stays put.
     • modal--wide gives a stable, comfortable width; hidden [data-kinds] groups
       are display:none so they contribute nothing to width or height.
     • Tighter field rhythm + Cohort|Phase paired into a grid-2 row (in the EJS)
       keep every type short enough to avoid an internal scrollbar on a normal
       laptop; genuinely tall content (homework + quiz) scrolls INSIDE the body
       only — never the page, never horizontally.
   =========================================================================== */

/* Stable content width: reserve the scrollbar gutter so showing/hiding groups
   (which changes body height, hence whether a scrollbar is needed) never
   nudges the column sideways. */
.sch-modal .modal__body{
  scrollbar-gutter: stable;
}

/* The form is the fixed-width inner column; hidden groups can't widen it. */
.sch-modal .sch-form{
  width: 100%;
  max-width: 100%;
  margin: 0;
}

/* Tighten vertical rhythm a touch so common types fit without scrolling.
   Base .field margin-bottom is --sp-4 (16px); --sp-3 (12px) here. */
.sch-modal .field{
  margin-bottom: var(--sp-3);
}

/* Grids inside the modal already collapse to 1 column below their breakpoints;
   keep their gap consistent with the tightened field rhythm. The Cohort|Phase
   pairing row uses the shared .grid-2, so no bespoke columns needed. */
.sch-modal .sch-row,
.sch-modal .grid-2,
.sch-modal .grid-3{
  margin-bottom: var(--sp-3);
}

/* Quiz builder block sits flush under Completion; give it a hairline separator
   so the revealed questions read as their own section without adding height. */
.sch-modal [data-sch-quiz]:not([hidden]){
  margin-top: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}


/* ========================= css3/exam.css ========================= */
/* =========================================================================
   Cohort › Exam-detail — PROPOSED styles (namespace: exd-)
   Built to sit ON TOP of existing styles.css primitives
   (.section-card .pill .btn .field .grid-2 .modal .mini-list .list).
   Integrator: paste into styles.css. Nothing here overrides base classes.
   ========================================================================= */

/* ---- Header ------------------------------------------------------------- */
.exd-head__main { min-width: 0; }

.exd-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-top: 8px;
}
.exd-meta__stat {
  font-size: .85rem;
  color: var(--muted);
}
.exd-meta__stat strong { color: var(--text); }

/* section headers (Questions / Attempts) keep their own margins */
.exd-section-head { margin: 28px 0 14px; }

/* ---- Availability controls --------------------------------------------- */
.exd-controls { margin-top: 16px; }
.exd-controls__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.exd-controls__text { min-width: 0; flex: 1 1 320px; }
.exd-controls__state { display: block; margin-bottom: 4px; font-size: 1rem; }
.exd-controls__hint { margin: 0; max-width: 62ch; }
.exd-controls__actions { flex: 0 0 auto; }

/* ---- Questions ---------------------------------------------------------- */
.exd-q__head { align-items: flex-start; margin-bottom: 0; }
.exd-q__prompt {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-weight: 600;
  line-height: 1.4;
}
.exd-q__num { color: var(--primary); font-weight: 700; }
.exd-q__choices { margin-top: 10px; }
.exd-q__rubric {
  margin-top: 10px;
  padding: 10px 12px;
  background: var(--bg);
  border-radius: var(--r-input);
  font-size: .9rem;
}

/* ---- Add-question modal: typed blocks ---------------------------------- */
/* wireTypedQuestion sets [hidden]; keep block flow otherwise. */
.exd-qkind { display: block; }
.exd-qkind[hidden] { display: none; }
/* tighten the last field before the modal footer */
.exd-qkind > .field:last-child,
.exd-qkind > .grid-2:last-child { margin-bottom: 0; }

/* ---- Small screens ------------------------------------------------------ */
@media (max-width: 600px) {
  .exd-controls__row { flex-direction: column; align-items: stretch; }
  .exd-controls__actions .btn { width: 100%; }
  .exd-section-head { align-items: flex-start; }
}


/* ========================= css3/homework.css ========================= */
/* ─────────────────────────────────────────────────────────────────────────
   Manual-grading UI for quiz homework results (homework-detail.ejs)
   Namespace: hwg-  (homework-grade). Reuses existing .field / .btn / .pill /
   .modal / .ans-q classes; these rules only add what's new.
   ───────────────────────────────────────────────────────────────────────── */

/* Section-level hint that a quiz is manually graded. */
.hwg-note {
  margin: 0 0 10px;
}

/* Student's short-answer text — preserve their line breaks and wrap long words
   so a pasted paragraph reads naturally instead of on one line. */
.hwg-answer-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
  line-height: 1.5;
}

/* Grade panel inside the per-student answers modal. */
.hwg-grade {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border, #e5e5e5);
}

.hwg-grade__title {
  margin: 0 0 4px;
  font-family: var(--serif, serif);
  font-size: 1.02rem;
  color: var(--primary, #333);
}

.hwg-grade__note {
  margin: 0 0 12px;
}

.hwg-grade__form {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}

/* Keep the number input compact rather than full-width. */
.hwg-grade__field {
  margin: 0;
  max-width: 160px;
  flex: 0 0 auto;
}

.hwg-grade__field input {
  width: 100%;
}

/* Nudge the button to sit flush with the input baseline. */
.hwg-grade__form .btn {
  margin-bottom: 1px;
}


/* ========================= css3/phase.css ========================= */
/* Progress tab — "pass students to the next phase"
   Namespaced `prg-` / `.prg`. Reuses existing .section-card, .list,
   .list__row, .list__main, .list__link, .btn, .pill. These rules only
   add layout/spacing specific to the progress roster. */

.prg__head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}

.prg__explain {
  margin: 6px 0 0;
  line-height: 1.5;
}

.prg__note {
  margin: 8px 0 0;
}

.prg__empty {
  margin: 12px 0 0;
}

.prg__list {
  margin-top: 12px;
}

/* Keep the roster row readable when the two action buttons wrap on
   narrow screens. */
.prg__row {
  gap: 12px;
  flex-wrap: wrap;
}

.prg__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

@media (max-width: 560px) {
  .prg__actions {
    width: 100%;
  }
  .prg__actions .inline,
  .prg__actions .btn {
    flex: 1 1 auto;
  }
}

/* ==========================================================================
   PROFILE PAGE — scoped visual redesign (.profile-page)
   CSS ONLY. Reuses existing design tokens. Every rule is namespaced under
   `.profile-page`, so no other page/component is affected. No markup logic
   changed — a single presentational wrapper was added around the content.
   ========================================================================== */
.profile-page{ --pf-line:#EFE7D8; --pf-ink:#173A3B; }

/* ---- Page header: calmer, with a divider under the title ---- */
.profile-page .page-head{
  margin-bottom:26px;padding-bottom:20px;border-bottom:1px solid var(--pf-line);
}
.profile-page .page-head h1{
  font-size:clamp(2.1rem,4.2vw,2.85rem);line-height:1.04;letter-spacing:-.01em;
  color:var(--primary);margin:0;
}
.profile-page .page-head .muted{font-size:1.04rem;margin-top:6px;}

/* ---- Section cards: airier padding, softer border, layered shadow ---- */
.profile-page .section-card{
  border-radius:20px;padding:26px 28px;border-color:var(--pf-line);
  box-shadow:0 1px 2px rgba(36,53,54,.04),0 16px 36px -26px rgba(6,80,83,.4);
}
.profile-page .section-card + .section-card,
.profile-page .grid-2,
.profile-page .grid-2 + .section-card{margin-top:22px;}
/* Section title with a small gold→teal accent bar */
.profile-page .section-card h2{
  font-family:var(--serif);font-size:1.4rem;font-weight:700;color:var(--pf-ink);
  display:flex;align-items:center;gap:11px;margin:0 0 18px;
}
.profile-page .section-card h2::before{
  content:"";flex:0 0 auto;width:5px;height:22px;border-radius:3px;
  background:linear-gradient(180deg,var(--gold),var(--primary));
}

/* ---- Stat grid: icon badges, refined numerals + uppercase labels ---- */
.profile-page .stat-grid{gap:18px;}
.profile-page .stat{
  padding:22px;border-radius:18px;gap:8px;border-color:var(--pf-line);
  box-shadow:0 1px 2px rgba(36,53,54,.04);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
.profile-page .stat:hover{transform:translateY(-3px);box-shadow:0 16px 34px -20px rgba(6,80,83,.45);}
.profile-page .stat__icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:48px;height:48px;border-radius:14px;background:var(--primary-soft);margin-bottom:8px;
}
.profile-page .stat__icon .ico{width:27px;height:27px;}
.profile-page .stat__value{font-size:1.85rem;line-height:1.12;color:var(--pf-ink);}
.profile-page .stat__label{
  font-size:.76rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);
}
/* Accent (cohort) card: keep the teal gradient, add a soft gold glow */
.profile-page .stat--accent{position:relative;overflow:hidden;}
.profile-page .stat--accent::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(140px 120px at 88% -12%,rgba(217,168,92,.38),transparent 70%);
}
.profile-page .stat--accent .stat__icon{background:rgba(255,255,255,.16);}
.profile-page .stat--accent .stat__value{color:#fff;}
.profile-page .stat--accent .stat__label{color:rgba(255,255,255,.85);}

/* ---- Two-up row: Makamak report + Overall attendance ---- */
.profile-page .grid-2 > .section-card{display:flex;flex-direction:column;gap:16px;margin-top:0;}
.profile-page .grid-2 > .section-card h2{margin-bottom:0;}
.profile-page .grid-2 .btn{align-self:flex-start;}
.profile-page .grid-2 > .section-card[style*="center"]{                 /* the attendance card */
  align-items:center;justify-content:center;text-align:center;
}
.profile-page .grid-2 > .section-card[style*="center"] .muted{max-width:32ch;}
.profile-page .ring{filter:drop-shadow(0 10px 20px rgba(6,80,83,.14));}

/* Makamak preview card — has NO styles anywhere else, so it's fully styled
   here. Vertical gaps keep the note clear of the button and the card border. */
.profile-page .mq-pcard{display:flex;flex-direction:column;gap:16px;}
.profile-page .mq-pcard__date{margin:0;}
.profile-page .mq-pcard__list{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;}
.profile-page .mq-pcard__row{display:flex;align-items:center;gap:12px;padding:11px 14px;
  border:1px solid var(--pf-line);border-radius:12px;background:#FCFAF4;}
.profile-page .mq-pcard__rank{flex:0 0 auto;width:26px;height:26px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--primary);color:#fff;font-weight:700;font-size:.82rem;}
.profile-page .mq-pcard__name{flex:1;min-width:0;font-weight:600;color:var(--pf-ink);}
.profile-page .mq-pcard__pct{flex:0 0 auto;}
.profile-page .mq-pcard__foot{margin:0;}
.profile-page .mq-pcard__note{margin:0;line-height:1.65;}

/* ---- Attendance by phase: neat ring cards + chip-style missed list ---- */
/* align-items:start → each phase card sizes to its OWN content (no forced
   equal-height that left Phase 2 with a big empty gap). */
.profile-page .attn-phases{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:18px;align-items:start;}
.profile-page .attn-phase{
  border-color:var(--pf-line);border-radius:16px;padding:24px 20px;
  background:linear-gradient(180deg,#fff,#FCFAF4);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
.profile-page .attn-phase:hover{transform:translateY(-3px);box-shadow:0 14px 30px -20px rgba(6,80,83,.4);}
.profile-page .attn-phase__title{
  font-family:var(--serif);font-size:1.15rem;font-weight:700;color:var(--pf-ink);margin-top:14px;
}
.profile-page .attn-phase__count{margin-top:4px;}
.profile-page ul.attn-phase__missed{margin-top:14px;gap:8px;}
.profile-page ul.attn-phase__missed li{
  background:#FAF6EC;border:1px solid var(--pf-line);border-radius:10px;padding:9px 12px;gap:10px;
}
.profile-page .attn-item__title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

/* ---- Exam results: cleaner rows with a teal edge ---- */
.profile-page .list{gap:12px;}
.profile-page .list__row{
  border-radius:14px;border-color:var(--pf-line);padding:16px 18px;
  border-left:4px solid var(--primary);
}
.profile-page .list__row:hover{box-shadow:0 12px 26px -18px rgba(6,80,83,.4);}
.profile-page .list__main strong{font-size:1.05rem;color:var(--pf-ink);}

/* ---- Phones: tighten padding so cards never crowd the edges ---- */
@media (max-width:600px){
  .profile-page .section-card{padding:20px 18px;}
  .profile-page .stat{padding:18px;}
  .profile-page .stat__value{font-size:1.6rem;}
}

/* ==========================================================================
   MAKAMAK (مقامك) — assessment, report, profile card, staff panel
   Everything here is namespaced under .mq-* / .mqr-* so nothing else on the
   site is touched. Reuses the existing tokens (--primary, --gold, --r-card,
   --sp-*, --sh-*, --ease/--dur) and the existing components (.btn, .pill,
   .section-card, .mini-list, .empty) — no new palette, no external asset.

   Direction: the assessment toggles `.mq-rtl` on <body> and sets dir on
   <html>; the report wrapper (.mqr / .mqr-mini) carries its own dir. So the
   two directions can coexist on one page (an Arabic report inside the LTR
   staff panel). Rules below use logical properties wherever possible; the
   few that cannot are given an explicit [dir="rtl"] pair.
   ========================================================================== */
:root{
  /* Manrope has no Arabic — Tajawal is the Arabic face used on the landing
     pages. Only a family reference; the font itself is requested by the view. */
  --mq-ar:"Tajawal","Noto Kufi Arabic",var(--sans);
  --mq-line:#EFE7D8;          /* softer than --border, matches .profile-page */
  --mq-ink:#173A3B;           /* deep teal for headings */
  --mq-tint:#FCFAF4;          /* barely-there warm surface */
}

/* ==========================================================================
   A · ASSESSMENT PAGE  (body.mq-page > .mq-shell > #mq-app)
   ========================================================================== */

/* The page is a focused, one-question-at-a-time flow, so ALL of the app's
   chrome comes off: the top bar and the bottom tab bar both go. The way out is
   the "save & exit" link at the foot of the page, which keeps the student's
   progress. `mq-page` is set server-side (routes/maqamak.js passes bodyClass),
   so the nav never flashes before the script runs. */
body.mq-page .topbar,
body.mq-page .bottomnav{display:none;}

/* …and the sidebar's offset has to come off with it. The desktop sidebar block
   near the end of this file gives `body.app .container` a left margin the width
   of a sidebar that is `display:none` here — which left the whole assessment
   sitting ~130px right of centre on every screen ≥720px, and pinned against the
   edge below ~1250px. The extra `.mq-page` outweighs that rule, so neither
   block has to care which of them is written first. The shell below owns the
   page's vertical spacing now, so the container keeps only its side gutter. */
body.app.mq-page .container{max-width:none;margin-inline:auto;padding:0 var(--sp-4);}

/* TEMPORARY testing aid — the "Simulate answers" button. Deliberately styled as
   an obvious dev affordance (dashed, muted, off to one side) so it can never be
   mistaken for part of the assessment. Remove with the rest of the simulate
   code. Never rendered when NODE_ENV=production. */
.mq-simulate{margin:var(--sp-5) auto 0;text-align:center;display:flex;
  flex-direction:column;align-items:center;gap:6px;opacity:.75;}
.mq-simulate .btn{border-style:dashed;}
.mq-simulate__note{font-size:.75rem;letter-spacing:.02em;text-transform:uppercase;
  color:var(--muted);}
body.mq-page{background:
  radial-gradient(900px 420px at 50% -12%,rgba(6,80,83,.07),transparent 70%),
  radial-gradient(700px 340px at 100% 108%,rgba(201,132,26,.06),transparent 65%),
  var(--bg);
  background-attachment:fixed;}
body.mq-rtl{font-family:var(--mq-ar);}
body.mq-rtl h1,body.mq-rtl h2,body.mq-rtl h3,body.mq-rtl h4{font-family:var(--mq-ar);}

/* The flow owns the whole viewport and sits in the middle of it. The centring
   is done with auto margins rather than `justify-content:center` on purpose:
   auto margins collapse to 0 as soon as the content is taller than the screen,
   so a 9-option question scrolls from its first line instead of having its head
   cut off above the scroll origin — which is exactly what centred flex does.
   `svh` keeps the frame clear of the mobile browser's collapsing chrome. */
.mq-shell{
  width:100%;min-height:100vh;min-height:100svh;
  display:flex;flex-direction:column;
  padding-block:var(--sp-5) calc(var(--sp-5) + env(safe-area-inset-bottom));
}
.mq-shell > :first-child{margin-block-start:auto;}
.mq-shell > :last-child{margin-block-end:auto;}
.mq-app{display:block;width:100%;}

/* Every screen is the same centred column — wide enough that a long Arabic
   option is not a ladder of two-word lines, narrow enough that it stays one
   readable measure on a 27" display. */
.mq-screen{
  display:flex;flex-direction:column;gap:var(--sp-5);
  width:100%;max-width:600px;margin-inline:auto;
  animation:mqIn 260ms var(--ease);
}
.mq-screen--center{justify-content:center;text-align:center;}

/* The question screen is a FIXED FRAME, not a box that shrink-wraps its
   content: progress at the top, nav pinned to the foot, the card growing
   between them. Without the min-height the header and the buttons hopped up and
   down the viewport as a 4-option question was followed by a 9-option one, and
   the student had to re-find the Next button after every answer. */
.mq-screen--q{max-width:660px;gap:var(--sp-4);min-height:min(680px,80vh);}
.mq-screen--q{min-height:min(680px,80svh);}
@keyframes mqIn{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}

/* ---- Brand head ---- */
.mq-brand{display:flex;flex-direction:column;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-1);}
.mq-brand__tag{font-family:var(--serif);font-size:2rem;line-height:1;font-weight:700;color:var(--primary);}
body.mq-rtl .mq-brand__tag{font-family:var(--mq-ar);font-size:1.75rem;}
.mq-brand__badge{
  font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--gold-dark);background:var(--gold-soft);border-radius:var(--r-pill);padding:4px 12px;
}
.mq-card--prose .mq-brand{align-items:flex-start;margin-bottom:var(--sp-3);}

/* ---- Cards ---- */
.mq-card{
  background:var(--surface);border:1px solid var(--mq-line);border-radius:20px;
  padding:26px 24px;box-shadow:0 1px 2px rgba(36,53,54,.04),0 18px 40px -30px rgba(6,80,83,.45);
}
.mq-card--narrow{max-width:460px;margin-inline:auto;width:100%;}
.mq-card--prose{text-align:start;}
/* `flex:0 0 auto` so the card keeps its natural height inside the fixed
   question frame — stretching it would leave a 4-option question as a mostly
   empty box with its buttons stranded at the bottom of a tall white field. */
.mq-card--q{padding:28px 26px;flex:0 0 auto;}

.mq-h1{font-family:var(--serif);font-size:1.6rem;line-height:1.2;color:var(--mq-ink);margin:0 0 var(--sp-2);}
body.mq-rtl .mq-h1{font-family:var(--mq-ar);}
.mq-h1:focus{outline:none;}
.mq-lede{color:var(--muted);font-size:1rem;line-height:1.6;margin:0;}
.mq-muted{color:var(--muted);}
.mq-small{font-size:.82rem;}
.mq-screen--center .mq-small{margin-top:var(--sp-2);}

/* Intro prose — trusted inline markup from lib/maqamak/content.js */
.mq-prose{margin:var(--sp-4) 0 0;line-height:1.75;color:var(--text);}
.mq-prose p + p{margin-top:var(--sp-3);}
.mq-prose b{color:var(--mq-ink);}
.mq-prose bdi{font-weight:600;}
.mq-note{
  margin-top:var(--sp-4);padding:var(--sp-3) var(--sp-4);line-height:1.65;font-size:.9rem;
  color:var(--gold-dark);background:var(--gold-soft);border-radius:12px;
  border-inline-start:3px solid var(--gold);
}

/* ---- Language picker ---- */
.mq-langs{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-3);margin-top:var(--sp-5);}
@media(min-width:420px){.mq-langs{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}}
.mq-lang{
  font:inherit;font-size:1.05rem;font-weight:700;color:var(--mq-ink);
  min-height:60px;padding:14px 18px;cursor:pointer;
  background:var(--mq-tint);border:1.5px solid var(--mq-line);border-radius:16px;
  transition:border-color var(--dur) var(--ease),background var(--dur),transform var(--dur) var(--ease);
}
.mq-lang[lang="ar"]{font-family:var(--mq-ar);}
.mq-lang:hover{border-color:var(--primary);background:var(--primary-soft);color:var(--primary);}
.mq-lang:active{transform:scale(.97);}

/* ---- Fields ---- */
.mq-field{display:flex;flex-direction:column;gap:6px;margin-top:var(--sp-5);text-align:start;}
.mq-label{font-size:.82rem;font-weight:700;color:var(--muted);}
.mq-input,.mq-textarea{font-size:16px;}          /* 16px stops iOS zooming on focus */
.mq-input{min-height:48px;}
.mq-textarea{min-height:150px;line-height:1.7;}
.mq-hint{
  font-size:.86rem;line-height:1.55;color:var(--gold-dark);
  background:var(--gold-soft);border-radius:10px;padding:8px 12px;margin:0 0 var(--sp-4);
}
.mq-field-error{color:var(--danger);font-size:.85rem;font-weight:600;margin:0;}
.mq-field-error:empty{display:none;}

/* ---- Progress ---- */
.mq-top{display:flex;flex-direction:column;gap:var(--sp-2);flex:0 0 auto;}
.mq-bar{position:relative;height:6px;border-radius:var(--r-pill);background:#EBE5D8;overflow:hidden;}
.mq-bar__fill{
  position:absolute;inset-block:0;inset-inline-start:0;width:0;
  border-radius:var(--r-pill);background:linear-gradient(90deg,var(--primary),var(--mist));
  transition:width 320ms var(--ease);
}
.mq-top__meta{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);flex-wrap:wrap;}
.mq-count{font-size:.8rem;font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums;}
.mq-saved{display:inline-flex;align-items:center;gap:5px;font-size:.76rem;color:var(--success);}
.mq-saved::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--success);flex:0 0 auto;}
.mq-saved:empty{display:none;}

/* ---- Question ---- */
.mq-pre{
  font-size:.92rem;line-height:1.7;color:var(--muted);
  background:var(--mq-tint);border:1px solid var(--mq-line);border-radius:12px;
  padding:var(--sp-3) var(--sp-4);margin-bottom:var(--sp-4);
}
.mq-stem{
  font-family:var(--serif);font-size:1.34rem;line-height:1.4;font-weight:700;color:var(--mq-ink);
  margin:0 0 var(--sp-4);letter-spacing:normal;
}
body.mq-rtl .mq-stem{font-family:var(--mq-ar);font-size:1.22rem;line-height:1.6;}
.mq-stem:focus{outline:none;}
.mq-stem:focus-visible{outline:2px solid var(--primary);outline-offset:4px;border-radius:6px;}

/* ---- Options ---- */
.mq-opts{display:flex;flex-direction:column;gap:var(--sp-2);}
.mq-opt{
  display:flex;align-items:center;gap:var(--sp-3);
  width:100%;min-height:56px;padding:12px 14px;
  font:inherit;font-size:.97rem;line-height:1.55;text-align:start;color:var(--text);
  background:var(--surface);border:1.5px solid var(--mq-line);border-radius:14px;cursor:pointer;
  transition:border-color var(--dur) var(--ease),background var(--dur),box-shadow var(--dur),transform var(--dur) var(--ease);
}
.mq-opt:hover{border-color:var(--mist);background:var(--mq-tint);}
.mq-opt:active{transform:scale(.99);}
.mq-opt.is-on{border-color:var(--primary);background:var(--primary-soft);box-shadow:0 0 0 1px var(--primary);}
/* The selected state is itself a tinted, ringed box, so the keyboard ring needs
   to sit clear of it to stay legible on a picked option. */
.mq-opt:focus-visible,.mq-lang:focus-visible{outline:2px solid var(--primary);outline-offset:3px;}
.mq-opt__mark{
  flex:0 0 auto;width:30px;height:30px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:.82rem;font-weight:700;font-variant-numeric:tabular-nums;
  background:#F1ECE0;color:var(--muted);
  transition:background var(--dur),color var(--dur),border-color var(--dur);
}
.mq-opt.is-on .mq-opt__mark{background:var(--primary);color:#fff;}
.mq-opt__txt{flex:1;min-width:0;overflow-wrap:break-word;}

/* Affordance per question kind: radio = circle, checkbox = square,
   ranking = a dashed slot that fills with the rank numeral. */
.mq-opts--multi .mq-opt__mark,
.mq-opts--exclude2 .mq-opt__mark{border-radius:9px;}
.mq-opts--multi .mq-opt:not(.is-on) .mq-opt__mark,
.mq-opts--exclude2 .mq-opt:not(.is-on) .mq-opt__mark{background:transparent;border:1.5px solid var(--mq-line);}
.mq-opts--ranked2 .mq-opt:not(.is-on) .mq-opt__mark,
.mq-opts--rankall .mq-opt:not(.is-on) .mq-opt__mark{background:transparent;border:1.5px dashed #D9D0BC;}
.mq-opts--ranked2 .mq-opt.is-on .mq-opt__mark,
.mq-opts--rankall .mq-opt.is-on .mq-opt__mark{background:var(--gold);color:#fff;}

/* exclude2: "pick the two you would NOT do" — selected means ruled out, so it
   must read as a removal, not an endorsement. Declared after .is-on to win. */
.mq-opt.is-out{border-color:#E3B3AC;background:#FDF2F0;box-shadow:none;}
.mq-opt.is-out .mq-opt__mark{background:var(--danger);color:#fff;border-color:var(--danger);}
.mq-opt.is-out .mq-opt__txt{text-decoration:line-through;opacity:.7;}

/* ---- Nav / actions ----
   ONE order everywhere, in both directions: the way back sits at the inline
   START, the action that moves the student on sits at the inline END, pushed
   there by an auto margin rather than by a physical float — so Arabic and
   English read the same way round and nothing swaps places when the language
   is switched mid-flow. */
.mq-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-3);margin-top:var(--sp-5);}
.mq-screen--center .mq-actions{justify-content:center;}
.mq-actions .btn{min-height:48px;}
/* The intro's row is the question nav in miniature — same order, same anchor. */
.mq-actions--nav{margin-top:var(--sp-6);}
.mq-actions--nav .btn--primary{margin-inline-start:auto;}

.mq-nav{
  position:sticky;bottom:0;z-index:5;
  /* wrap, not shrink: .btn is `white-space:nowrap`, so a squeezed button spills
     its label instead of narrowing. If a longer translation ever stops the pair
     fitting, Next drops to a second line and stays anchored to the end. */
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-3);
  /* pinned to the foot of the fixed question frame (.mq-screen--q), so it holds
     the same place from question to question */
  margin-block-start:auto;
  padding-block:var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom));
  /* On a long question the row overlaps the options as the page scrolls, so it
     needs OPAQUE ground of its own: the fade this used to be left the ghost
     "Back" printed over an option and unreadable. The fade now lives above the
     row instead, so the card still looks continuous rather than cut off. */
  background:var(--bg);
}
.mq-nav::before{
  content:"";position:absolute;inset-inline:0;bottom:100%;height:26px;pointer-events:none;
  background:linear-gradient(180deg,rgba(251,248,241,0),var(--bg));
}
.mq-btn{flex:0 0 auto;min-height:50px;}
.mq-btn--next{margin-inline-start:auto;min-width:180px;}

/* Phones: one column of full-width targets. Back stays FIRST in the DOM, so
   the reading order, the tab order and the visual order agree, and the primary
   lands nearest the thumb. */
@media(max-width:480px){
  .mq-nav,.mq-actions{flex-direction:column;align-items:stretch;gap:var(--sp-2);}
  .mq-btn,.mq-actions .btn{width:100%;min-width:0;}
  .mq-btn--next,.mq-actions--nav .btn--primary{margin-inline-start:0;}
}

.mq-exit{text-align:center;margin:var(--sp-5) 0 0;}
.mq-exit a{font-size:.85rem;font-weight:600;color:var(--muted);}
.mq-exit a:hover{color:var(--primary);}

/* ---- Loading / errors ---- */
.mq-spinner{
  width:34px;height:34px;margin:0 auto var(--sp-3);border-radius:50%;
  border:3px solid var(--primary-soft);border-top-color:var(--primary);
  animation:mqSpin 800ms linear infinite;
}
@keyframes mqSpin{to{transform:rotate(360deg);}}
.mq-error{
  color:#8A2018;background:#FDECEA;border:1px solid #F0C9C2;border-radius:12px;
  padding:var(--sp-3) var(--sp-4);font-size:.9rem;line-height:1.6;margin:0;
}
.mq-screen--center .mq-error{margin-bottom:var(--sp-4);}
@media(prefers-reduced-motion:reduce){
  .mq-spinner{animation:none;border-top-color:var(--primary-soft);
    background:radial-gradient(circle,var(--primary) 32%,transparent 33%);}
  /* Every screen change re-runs mqIn and the bar re-animates its width; for a
     student who asked for less motion that is 63 slides in a row. State still
     changes instantly — only the movement goes. */
  .mq-screen{animation:none;}
  .mq-bar__fill{transition:none;}
  .mq-opt,.mq-lang{transition:none;}
  .mq-opt:active,.mq-lang:active{transform:none;}
}

/* ---- Done: the report, rendered inline where the student sits ---- */
.mq-screen.mq-done{max-width:880px;gap:var(--sp-4);}
.mq-done__report{width:100%;}
.mq-done__actions{
  position:sticky;bottom:0;z-index:5;display:flex;justify-content:center;
  padding-block:var(--sp-4) calc(var(--sp-4) + env(safe-area-inset-bottom));
  background:var(--bg);                         /* same reason as .mq-nav */
}
.mq-done__actions::before{
  content:"";position:absolute;inset-inline:0;bottom:100%;height:26px;pointer-events:none;
  background:linear-gradient(180deg,rgba(251,248,241,0),var(--bg));
}
.mq-done__end{min-width:220px;min-height:50px;}

/* ==========================================================================
   B · RESULT REPORT  (.mqr — student page, inline done screen, staff panel)
   ========================================================================== */
.mqr{display:block;}
.mqr:lang(ar){font-family:var(--mq-ar);}
.mqr:lang(ar) h1,.mqr:lang(ar) h2,.mqr:lang(ar) h3,.mqr:lang(ar) h4{font-family:var(--mq-ar);}
.mqr .section-card + .section-card,
.mqr .section-card + .mqr-details,
.mqr .mqr-details + .mqr-details,
.mqr .mqr-details + .section-card{margin-top:18px;}

/* ---- Header ---- */
.mqr-head{margin-bottom:var(--sp-6);padding-bottom:var(--sp-4);border-bottom:1px solid var(--mq-line);}
.mqr-head h1{font-size:clamp(1.7rem,4vw,2.4rem);line-height:1.1;color:var(--primary);}
.mqr-head p{margin-top:6px;display:flex;flex-wrap:wrap;align-items:center;gap:8px;}
.mqr-staff-pill{margin-inline-start:4px;}

/* Section titles: same gold→teal accent bar the profile page uses. */
.mqr .section-card h2{
  font-family:var(--serif);font-size:1.3rem;color:var(--mq-ink);
  display:flex;align-items:center;gap:11px;margin:0 0 16px;
}
.mqr .section-card h2::before{
  content:"";flex:0 0 auto;width:5px;height:22px;border-radius:3px;
  background:linear-gradient(180deg,var(--gold),var(--primary));
}
.mqr-sub{
  font-family:var(--sans);font-size:.76rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted);margin:var(--sp-5) 0 var(--sp-3);
}
.mqr-sub:first-child{margin-top:0;}
.mqr-lede{margin:-8px 0 var(--sp-4);}

/* ---- Narrative ---- */
.mqr-narrative__p{line-height:1.85;font-size:1.02rem;}
.mqr-narrative__p + .mqr-narrative__p{margin-top:var(--sp-3);}

/* ---- Profile: horizontal percentage bars ----
   Two rows on phones (label+value, then the track) so a long Arabic domain
   name can never squeeze the track or push the page sideways. */
.mqr-bars{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-3);}
.mqr-bar{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px var(--sp-3);}
.mqr-bar__label{grid-column:1;min-width:0;font-weight:600;font-size:.94rem;overflow-wrap:break-word;}
.mqr-bar__val{
  grid-column:2;font-size:.82rem;font-weight:700;color:var(--primary);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.mqr-bar__track{
  grid-column:1 / -1;position:relative;display:block;
  height:9px;border-radius:var(--r-pill);background:#EFE9DB;overflow:hidden;
}
.mqr-bar__fill{
  position:absolute;inset-block:0;inset-inline-start:0;display:block;
  border-radius:var(--r-pill);background:linear-gradient(90deg,var(--primary),var(--mist));
}
@media(min-width:600px){
  .mqr-bar{grid-template-columns:minmax(120px,26%) minmax(0,1fr) auto;}
  .mqr-bar__label{grid-column:1;}
  .mqr-bar__track{grid-column:2;}
  .mqr-bar__val{grid-column:3;}
}

/* ---- Chips ---- */
.mqr-chips{display:flex;flex-wrap:wrap;gap:6px;}
/* Reasons and trait words are whole phrases, not one-word tags — .pill is
   `white-space:nowrap`, which would overflow 320px. Same specificity as
   .pill and declared later, so it wins without !important. */
.mqr-chip{white-space:normal;text-align:start;max-width:100%;line-height:1.45;padding:5px 12px;}

.mqr-aud{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-4);margin-top:var(--sp-2);}
@media(min-width:640px){.mqr-aud{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--sp-5);}}

/* ---- Purposes, ranked ---- */
.mqr-maq{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;}
.mqr-maq__item{
  display:flex;align-items:center;gap:var(--sp-3);
  padding:9px 12px;border-radius:12px;background:var(--mq-tint);border:1px solid var(--mq-line);
}
.mqr-maq__item.is-top{background:var(--primary-soft);border-color:#BBD4D0;}
.mqr-maq__rank{
  flex:0 0 auto;width:26px;height:26px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:.78rem;font-weight:700;font-variant-numeric:tabular-nums;
  background:#E7E0CE;color:var(--muted);
}
.mqr-maq__item.is-top .mqr-maq__rank{background:var(--primary);color:#fff;}
.mqr-maq__name{min-width:0;overflow-wrap:break-word;}
.mqr-maq__item.is-top .mqr-maq__name{font-weight:700;color:var(--mq-ink);}
.mqr-maq__guide{margin-top:var(--sp-3);font-size:.9rem;line-height:1.7;color:var(--muted);}

/* ==========================================================================
   C · THE THREE HEADLINE PROJECT CARDS — the centre of the whole tool
   ========================================================================== */
.mqr-card{
  position:relative;margin-top:var(--sp-4);padding:var(--sp-5) var(--sp-5) var(--sp-4);
  background:linear-gradient(180deg,#fff,var(--mq-tint));
  border:1px solid var(--mq-line);border-radius:18px;
  box-shadow:0 1px 2px rgba(36,53,54,.04),0 18px 38px -30px rgba(6,80,83,.5);
}
/* #1 carries a gold edge and a little more lift than #2 and #3. */
.mqr-card:first-of-type{
  border-color:#E4CFA4;
  box-shadow:0 1px 2px rgba(36,53,54,.04),0 22px 46px -28px rgba(201,132,26,.55);
}
.mqr-card:first-of-type::before{
  content:"";position:absolute;inset-block-start:0;inset-inline:18px;height:3px;
  border-radius:0 0 3px 3px;background:linear-gradient(90deg,var(--gold),var(--light-gold));
}
.mqr-card__head{display:flex;align-items:flex-start;gap:var(--sp-4);}
.mqr-card__rank{
  flex:0 0 auto;width:46px;height:46px;border-radius:14px;
  display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--serif);font-size:1.65rem;font-weight:700;line-height:1;
  color:#fff;background:linear-gradient(140deg,var(--primary),#0A7E82);
  box-shadow:0 8px 18px -10px rgba(6,80,83,.75);
}
.mqr-card:first-of-type .mqr-card__rank{
  background:linear-gradient(140deg,var(--gold),var(--gold-dark));
  box-shadow:0 8px 18px -10px rgba(201,132,26,.8);
}
.mqr-card__title{min-width:0;flex:1;}
.mqr-card__title h3{
  font-size:1.22rem;line-height:1.3;color:var(--mq-ink);margin:0 0 8px;overflow-wrap:break-word;
}
.mqr-card__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;}
.mqr-card__pct{font-size:.85rem;padding:5px 13px;font-variant-numeric:tabular-nums;}
.mqr-card__def{margin-top:var(--sp-4);line-height:1.75;color:var(--text);}
.mqr-card__sub{
  font-family:var(--sans);font-size:.74rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted);margin:var(--sp-4) 0 var(--sp-2);
}
.mqr-ex{margin:0;padding-inline-start:1.15em;display:flex;flex-direction:column;gap:5px;
  line-height:1.65;color:var(--text);}
.mqr-ex li::marker{color:var(--gold);}

.mqr-minv{
  margin-top:var(--sp-4);padding:var(--sp-3) var(--sp-4);border-radius:12px;
  background:var(--gold-soft);border-inline-start:3px solid var(--gold);
  display:flex;flex-direction:column;gap:3px;
}
.mqr-minv__label{font-size:.72rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--gold-dark);}
.mqr-minv__body{line-height:1.7;color:var(--text);}

.mqr-warns{
  margin-top:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-radius:12px;
  background:#FDF2F0;border-inline-start:3px solid #D98B7E;
}
.mqr-warns__label{display:block;font-size:.72rem;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:#8A2018;margin-bottom:4px;}
.mqr-warns ul{margin:0;padding-inline-start:1.15em;display:flex;flex-direction:column;gap:4px;
  line-height:1.65;font-size:.93rem;}

/* ---- Collapsible sections ---- */
.mqr-details{padding:0;}
.mqr-details > *:not(.mqr-details__summary){padding-inline:22px;}
.mqr-details > *:last-child{padding-bottom:22px;}
.mqr-details__summary{
  display:flex;align-items:center;gap:var(--sp-2);cursor:pointer;list-style:none;
  padding:18px 22px;font-family:var(--serif);font-size:1.12rem;font-weight:700;color:var(--mq-ink);
  border-radius:var(--r-card);transition:background var(--dur);
}
.mqr-details__summary::-webkit-details-marker{display:none;}
.mqr-details__summary:hover{background:var(--mq-tint);}
.mqr-details__summary::after{
  content:"";flex:0 0 auto;margin-inline-start:auto;width:8px;height:8px;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:translateY(-2px) rotate(45deg);transition:transform var(--dur) var(--ease);
}
.mqr-details[open] .mqr-details__summary::after{transform:translateY(2px) rotate(-135deg);}
.mqr-details[open] .mqr-details__summary{border-bottom:1px solid var(--mq-line);border-radius:var(--r-card) var(--r-card) 0 0;}
.mqr-details .mqr-lede{margin:14px 0 6px;}

/* ---- Ruled-out, grouped by reason ---- */
.mqr-blockgroup{padding-block:12px;}
.mqr-blockgroup + .mqr-blockgroup{border-top:1px solid var(--mq-line);}
.mqr-blockgroup__reason{font-weight:700;font-size:.92rem;color:var(--mq-ink);margin-bottom:4px;}
.mqr-blockgroup__items{font-size:.9rem;line-height:1.7;color:var(--muted);}
.mqr-blockgroup__item{display:inline;}

/* ==========================================================================
   D · STAFF ONLY — confidential. Must never be mistaken for the student's
   own report, and must never reach paper (see the print block at the end).
   ========================================================================== */
.mqr-staff{
  margin-top:22px;padding:0 20px 20px;overflow:hidden;
  background:#FFFBF2;border:2px solid var(--gold);border-radius:var(--r-card);
  box-shadow:0 1px 2px rgba(36,53,54,.05);
}
.mqr-staff--boxed{margin-top:var(--sp-5);}
.mqr-staff__banner{
  display:flex;align-items:center;flex-wrap:wrap;gap:8px;
  margin-inline:-20px;margin-bottom:18px;padding:11px 20px;
  background:var(--gold-soft);border-bottom:1px solid #E8D3A6;
  font-size:.9rem;color:var(--gold-dark);
}
.mqr-staff__banner strong{
  font-size:.74rem;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
}
.mqr-staff__icon{display:inline-flex;align-items:center;color:var(--gold-dark);flex:0 0 auto;}
.mqr-staff__banner .muted{color:var(--gold-dark);opacity:.8;}

/* Staff block as a collapsible fold: the gold banner IS the <summary>. It has
   to beat .mqr-staff's own padding (declared later in the file than
   .mqr-details), hence the explicit overrides rather than relying on order. */
.mqr-staff--fold{padding:0;}
.mqr-staff__summary{
  margin-inline:0;margin-bottom:0;
  border-radius:calc(var(--r-card) - 2px) calc(var(--r-card) - 2px) 0 0;
  color:var(--gold-dark);
}
.mqr-staff--fold:not([open]) .mqr-staff__summary{
  border-bottom:0;border-radius:calc(var(--r-card) - 2px);
}
.mqr-staff--fold[open] .mqr-staff__summary{border-bottom:1px solid #E8D3A6;}
.mqr-staff__summary:hover{background:#F5E4C0;}
.mqr-staff__summary::after{border-color:var(--gold-dark);}
.mqr-staff--fold > *:not(.mqr-staff__summary){padding-inline:20px;}
.mqr-staff--fold > *:first-of-type:not(.mqr-staff__summary){padding-top:18px;}
.mqr-staff--fold > *:last-child{padding-bottom:20px;}

.mqr-staff__written,.mqr-staff__flags{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-4);}
.mqr-staff__flags{margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px dashed #E8D3A6;}
@media(min-width:720px){
  .mqr-staff__written{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}
  .mqr-staff__flags{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}
}
.mqr-qa{display:flex;flex-direction:column;gap:5px;min-width:0;}
.mqr-qa__label{font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--gold-dark);}
.mqr-qa__body{
  white-space:pre-wrap;line-height:1.65;font-size:.93rem;overflow-wrap:break-word;
  background:var(--surface);border:1px solid #EEE0C4;border-radius:10px;padding:10px 12px;
}

/* ---- Header strip above the report in the staff tab ----
   The tab shows the FULL report (views/maqamak/_report.ejs with hideHead), so
   this is the only chrome around it: what it is, when it was taken, and a
   standing reminder that staff are seeing more than the student does. It sits
   OUTSIDE the report's cards, so it gets its own spacing rather than a box. */
.rep-mqhead{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
  gap:10px;margin:0 0 var(--sp-4);}
.rep-mqhead h2{margin:0;}
.rep-mqhead__meta{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}
/* The report's own first card shouldn't re-add the gap this strip already made. */
.rep-mqhead + .mqr > .section-card:first-child,
.rep-mqhead + .section-card{margin-top:0;}

/* ---- Compact staff mini-view ----
   Retained for reuse: the facilitator tab now renders the full report instead,
   but these keep any other compact embedding of a result styled. */
.mqr-mini{display:flex;flex-direction:column;gap:var(--sp-5);}
.mqr-mini:lang(ar){font-family:var(--mq-ar);}
.mqr-mini:lang(ar) h3{font-family:var(--mq-ar);}
.mqr-mini .mqr-sub{margin-top:0;}
.mqr-mini__doms{display:flex;flex-wrap:wrap;gap:6px;}

/* ==========================================================================
   E · PROFILE SUMMARY CARD (.mq-pcard) — sits beside the attendance ring in
   a .grid-2, so it must stay compact and never drive that row's height.
   ========================================================================== */
.mq-pcard{display:flex;flex-direction:column;gap:var(--sp-3);flex:1;min-width:0;}
.mq-pcard__date{margin:0;}
.mq-pcard__list{display:flex;flex-direction:column;gap:6px;}
.mq-pcard__row{
  display:flex;align-items:center;gap:10px;min-width:0;
  padding:8px 10px;border-radius:11px;background:var(--mq-tint);border:1px solid var(--mq-line);
}
.mq-pcard__rank{
  flex:0 0 auto;width:24px;height:24px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:.75rem;font-weight:700;font-variant-numeric:tabular-nums;
  background:var(--primary-soft);color:var(--primary);
}
.mq-pcard__row:first-child .mq-pcard__rank{background:var(--primary);color:#fff;}
.mq-pcard__name{flex:1;min-width:0;font-size:.94rem;font-weight:600;line-height:1.4;overflow-wrap:break-word;}
.mq-pcard__pct{flex:0 0 auto;}
.mq-pcard__note{margin:0;line-height:1.6;}
.mq-pcard__foot{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-top:auto;padding-top:var(--sp-1);}

/* ==========================================================================
   F · RTL — the few things logical properties cannot carry
   ========================================================================== */
[dir="rtl"] .mqr-bar__fill,
[dir="rtl"] .mq-bar__fill{background:linear-gradient(270deg,var(--primary),var(--mist));}
[dir="rtl"] .mqr-card:first-of-type::before{background:linear-gradient(270deg,var(--gold),var(--light-gold));}
/* .mini-row__meta is hard-coded `text-align:right` globally; inside an RTL
   report that pushes the percentage away from the row edge. */
[dir="rtl"] .mqr-mini .mini-row__meta,
[dir="rtl"] .mqr-details .mini-row__meta{text-align:left;}
[dir="rtl"] .mqr-warns__label,[dir="rtl"] .mqr-minv__label,
[dir="rtl"] .mqr-qa__label,[dir="rtl"] .mqr-sub,
[dir="rtl"] .mqr-card__sub{letter-spacing:normal;text-transform:none;}
body.mq-rtl .mq-brand__badge{letter-spacing:normal;text-transform:none;}

/* ==========================================================================
   G · NARROW PHONES (≤360px) — nothing may scroll the page sideways
   ========================================================================== */
@media(max-width:400px){
  body.app.mq-page .container{padding-inline:var(--sp-3);}
  .mq-card{padding:20px 16px;}
  .mq-card--q{padding:20px 15px;}
  .mq-shell{padding-block:var(--sp-3) calc(var(--sp-4) + env(safe-area-inset-bottom));}
  .mq-opt{padding:11px 11px;gap:10px;font-size:.94rem;}
  .mq-opt__mark{width:27px;height:27px;}
  .mq-stem{font-size:1.18rem;}
  .mq-brand__tag{font-size:1.6rem;}
  .mq-btn{font-size:.9rem;padding-inline:14px;}
  .mqr-card{padding:var(--sp-4) var(--sp-4) var(--sp-3);}
  .mqr-card__head{gap:var(--sp-3);}
  .mqr-card__rank{width:38px;height:38px;font-size:1.35rem;border-radius:12px;}
  .mqr-card__title h3{font-size:1.08rem;}
  .mqr-staff{padding:0 14px 14px;}
  .mqr-staff__banner{margin-inline:-14px;padding-inline:14px;}
  .mqr-staff__summary{margin-inline:0;padding-inline:16px;}
  .mqr-staff--fold > *:not(.mqr-staff__summary){padding-inline:14px;}
  .mqr-staff--fold > *:last-child{padding-bottom:14px;}
  .mqr-details__summary{padding:15px 16px;}
  .mqr-details > *:not(.mqr-details__summary){padding-inline:16px;}
  .mqr-details > *:last-child{padding-bottom:16px;}
}

/* ==========================================================================
   H · PRINT — the report on paper. App chrome and the confidential staff
   block are removed; collapsed <details> are forced open.
   ========================================================================== */
@media print{
  /* The staff block holds notes the student must never read. !important is
     deliberate here: nothing further down any cascade may bring it back. */
  .mqr-staff,.mqr-staff-pill{display:none!important;}

  .topbar,.bottomnav,#toast-root,.mq-exit,.mq-nav,.mq-top,
  .mq-done__actions,.back-link,.tabs{display:none!important;}

  body,body.mq-page{background:#fff!important;}
  .container{max-width:none;padding:0;}
  .mq-screen,.mq-screen.mq-done{max-width:none;animation:none;padding-block:0;}

  .mqr .section-card,.mqr-details,.mqr-card{
    box-shadow:none;border-color:#CFCFCF;background:#fff;
    break-inside:avoid;page-break-inside:avoid;
  }
  .mqr-card{margin-top:14px;}
  .mqr-head h1{color:#000;}

  /* Bars, chips and rank badges are the report's data — keep their fills. */
  .mqr-bar__fill,.mqr-bar__track,.mqr-card__rank,.mqr-maq__rank,
  .pill,.mqr-minv,.mqr-warns{-webkit-print-color-adjust:exact;print-color-adjust:exact;}

  /* A closed <details> is hidden by the UA stylesheet, which no author rule
     of normal weight can reach — !important is the only lever. */
  /* A closed <details> is hidden by content-visibility on ::details-content in
     current Chrome, so forcing its CHILDREN to display:block never reaches them
     and 11 recommendations silently vanished from the printout. Unhide the
     pseudo-element itself; keep the child rule for pre-::details-content
     engines. .mqr-staff stays display:none — verified. */
  .mqr-details::details-content{content-visibility:visible!important;}
  .mqr-details:not([open]) > *{display:block!important;}
  .mqr-details__summary::after{display:none;}
  .mqr-details__summary{background:none;}

  a[href]{text-decoration:none;color:inherit;}
}

/* ==========================================================================
   DASHBOARDS  (body.dash-page — director, facilitator/assistant, student)

   Scoped to the dashboards on purpose. .stat, .list__row, .section-card and
   .mini-row are shared with the dense management screens (rosters, gradebooks,
   settings), where this much air would be wrong. Tagging the body from the
   route keeps the roomier treatment here and leaves those alone.
   ========================================================================== */

/* ---- Head ---- */
.dash-page .page-head.dash-head{
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;
  gap:var(--sp-5);margin-bottom:var(--sp-8);padding-bottom:var(--sp-5);
  border-bottom:1px solid var(--border);
}
.dash-head__main{min-width:0;}
.dash-head__main h1{margin:0 0 6px;letter-spacing:-.015em;}
.dash-head__sub{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:0;}
.dash-head__cohort{font-family:var(--serif);font-size:1.3rem;font-weight:600;color:var(--primary);}
.dash-head__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-3);}
.dash-head__actions .btn{padding-inline:22px;}

/* Inline cohort picker: label beside the control instead of stacked above it,
   so it sits level with the buttons next to it. */
.field--inline{display:flex;align-items:center;gap:10px;margin:0;}
.field--inline .field__label{margin:0;white-space:nowrap;}
.field--inline select{margin:0;min-width:190px;}
.dash-picker{margin:0;}

/* ---- Stat tiles ---- */
.dash-page .stat-grid{gap:var(--sp-5);margin-bottom:var(--sp-8);}
.dash-page .stat{
  gap:var(--sp-2);padding:var(--sp-6);border-radius:var(--r-card);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
/* The icons are <img> PNGs, so `color` can't tint them — on the dark accent
   tile the artwork all but disappeared. Seat every tile icon in a soft chip
   instead: tinted on the light tiles, near-white on the accent one. Same
   approach the profile page already uses. */
.dash-page .stat__icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:52px;height:52px;border-radius:15px;background:var(--primary-soft);
  margin-bottom:var(--sp-3);
}
.dash-page .stat__icon .ico{width:28px;height:28px;}
.dash-page .stat--accent .stat__icon{background:rgba(255,255,255,.92);}
.dash-page .stat--warn .stat__icon{background:var(--gold-soft);}
.dash-page .stat__value{font-size:2.6rem;line-height:1.05;}
.dash-page .stat__label{font-size:.86rem;letter-spacing:.01em;}
.dash-page .stat--link:hover{transform:translateY(-3px);box-shadow:var(--sh-lg);}
/* A non-zero backlog shouldn't read as a neutral statistic. */
.dash-page .stat--warn .stat__value{color:var(--gold-dark);}
.dash-page .stat--warn .stat__icon{color:var(--gold);}

/* ---- Body ---- */
.dash-page .dash-grid{gap:var(--sp-6);margin-top:0;}
.dash-page .section-card{padding:var(--sp-6);}
.dash-page .section-card + .section-card{margin-top:var(--sp-6);}
.section-card__head{margin-bottom:var(--sp-4);gap:var(--sp-4);}
.section-card__head h2{margin:0;}
.dash-lede{margin:0 0 var(--sp-4);}
.dash-more{margin:var(--sp-4) 0 0;}

/* ---- Rows ---- */
.dash-page .dash-list{display:flex;flex-direction:column;gap:var(--sp-3);}
.dash-row{
  display:flex;align-items:center;gap:var(--sp-4);padding:var(--sp-4) var(--sp-5);
  color:inherit;text-decoration:none;
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease),
             border-color var(--dur) var(--ease);
}
.dash-row:hover{transform:translateY(-1px);box-shadow:var(--sh-md);border-color:var(--primary-soft);}
.dash-row:hover strong{color:var(--primary);}
.dash-row .list__actions{gap:var(--sp-3);}
.dash-row__go{color:var(--muted);opacity:0;transform:translateX(-4px);
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease);}
.dash-row:hover .dash-row__go{opacity:1;transform:none;}
[dir="rtl"] .dash-row__go{transform:scaleX(-1) translateX(-4px);}
[dir="rtl"] .dash-row:hover .dash-row__go{transform:scaleX(-1);}
@media (hover:none){.dash-row__go{opacity:1;transform:none;}}

.dash-minilist .mini-row{padding-block:var(--sp-3);}
.dash-page a.mini-row{color:inherit;text-decoration:none;transition:background var(--dur);
  border-radius:10px;padding-inline:10px;margin-inline:-10px;}
.dash-page a.mini-row:hover{background:var(--primary-soft);}

.empty--tight{padding-block:var(--sp-6);}
.empty--tight .empty__title{margin-bottom:4px;}

/* ---- Two-column body from tablet up ---- */
@media (min-width:980px){
  .dash-page .dash-grid--2{grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);align-items:start;}
}

/* ---- Phone ---- */
@media (max-width:600px){
  .dash-page .page-head.dash-head{
    align-items:stretch;gap:var(--sp-4);margin-bottom:var(--sp-6);
  }
  .dash-head__actions{width:100%;}
  .dash-head__actions .btn,.dash-picker,.field--inline select{flex:1 1 100%;width:100%;}
  .dash-page .stat{padding:var(--sp-5);}
  .dash-page .stat__value{font-size:2.1rem;}
  .dash-page .stat-grid{gap:var(--sp-4);margin-bottom:var(--sp-6);}
  .dash-page .section-card{padding:var(--sp-5);}
  .dash-row{padding:var(--sp-4);}
}

@media (prefers-reduced-motion:reduce){
  .dash-page .stat,.dash-row,.dash-row__go{transition:none;}
  .dash-page .stat--link:hover,.dash-row:hover{transform:none;}
}

/* ==========================================================================
   "MY PROJECT" FORMS  (#pf-app — views/project/fill.ejs)

   These run inside the Makamak assessment chrome: the page sets body.mq-page
   and the runner reuses .mq-shell / .mq-screen / .mq-card / .mq-nav / .mq-bar
   and friends, so only the genuinely new parts live here — a section screen
   shows SEVERAL fields at once, where Makamak shows a single question.
   ========================================================================== */

.pf-card{text-align:start;}
.pf-stem{font-size:clamp(1.5rem,3.4vw,2rem);margin-bottom:var(--sp-5);}

/* Field stack */
.pf-fields{display:flex;flex-direction:column;gap:var(--sp-6);}
.pf-field{display:flex;flex-direction:column;gap:6px;margin:0;}
.pf-label{font-weight:700;color:var(--mq-ink);font-size:1.02rem;line-height:1.35;}
/* Quiet supporting text — deliberately NOT .mq-hint, which is Makamak's filled
   instruction callout and would outweigh the field it describes. */
.pf-hint{margin:0 0 2px;color:var(--muted);font-size:.88rem;line-height:1.5;
  background:none;border:0;padding:0;}
.pf-input,.pf-textarea{width:100%;}
.pf-textarea{min-height:120px;resize:vertical;line-height:1.6;}
.pf-input[type="date"],.pf-input[type="number"]{max-width:260px;}

/* A required field the student tried to skip. */
.pf-field--bad .pf-input,.pf-field--bad .pf-textarea,.pf-field--bad .pf-choices{
  border-color:var(--danger);box-shadow:0 0 0 3px rgba(192,57,43,.10);}
.pf-field--bad .pf-label{color:var(--danger);}

/* Character counter, only near the cap. */
.pf-count{align-self:flex-end;font-size:.75rem;color:var(--muted);min-height:1em;}

/* Choice chips (the `select` kind), radio-style. */
.pf-select{display:flex;flex-direction:column;gap:10px;}
.pf-choices{display:flex;flex-wrap:wrap;gap:8px;border:1px solid transparent;
  border-radius:var(--r-card);}
.pf-choice{
  appearance:none;cursor:pointer;font:inherit;font-weight:600;font-size:.94rem;
  padding:10px 18px;min-height:44px;border-radius:var(--r-pill);
  background:var(--surface);color:var(--text);
  border:1.5px solid var(--mq-line);
  transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease),
             color var(--dur) var(--ease);
}
.pf-choice:hover{border-color:var(--primary);background:var(--mq-tint);}
.pf-choice.is-on{background:var(--primary-soft);border-color:var(--primary);color:var(--primary);}
.pf-other{max-width:340px;}

/* Done screen: a short confirmation above the answer summary. */
.pf-done-head{text-align:center;margin-bottom:var(--sp-5);}

/* ---- The submitted-answers summary (views/project/_answers.ejs) ----
   Shared by the done screen, the profile modal and the staff view, so it has
   to sit comfortably on a dark-ish page and inside a white modal alike. */
.pf-answers{display:flex;flex-direction:column;gap:var(--sp-5);text-align:start;}
.pf-answers__group{border:1px solid var(--mq-line);border-radius:var(--r-card);
  background:var(--surface);padding:var(--sp-5);}
.pf-answers__title{margin:0 0 var(--sp-4);font-family:var(--serif);font-size:1.14rem;
  font-weight:700;color:var(--mq-ink);padding-bottom:10px;
  border-bottom:1px solid var(--mq-line);}
.pf-answers__qa + .pf-answers__qa{margin-top:var(--sp-4);}
.pf-answers__hint{display:block;margin:2px 0 4px;}
.pf-answers .mqr-qa__body{white-space:pre-wrap;}

@media (max-width:600px){
  .pf-fields{gap:var(--sp-5);}
  .pf-answers__group{padding:var(--sp-4);}
  .pf-input[type="date"],.pf-input[type="number"]{max-width:100%;}
}

/* ---- "My Project" cards: the student's two forms (cohort tab) ---- */
.pf-cards{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-5);}
@media (min-width:900px){.pf-cards{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start;}}
.pf-card-form{display:flex;flex-direction:column;gap:var(--sp-3);margin:0;}
.pf-card-form__head{display:flex;align-items:flex-start;gap:var(--sp-4);}
.pf-card-form__step{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:50%;font-family:var(--serif);font-weight:700;
  background:var(--primary-soft);color:var(--primary);
}
.pf-card-form__title{display:flex;flex-wrap:wrap;align-items:center;gap:10px;min-width:0;}
.pf-card-form__title h2{margin:0;}
.pf-card-form__lede{margin:0;color:var(--muted);line-height:1.6;}
.pf-card-form__actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:auto;padding-top:var(--sp-2);}
.pf-card-form__note{margin:0;}
.pf-card-form--done .pf-card-form__step{background:var(--primary);color:#fff;}
.pf-card-form--locked{opacity:.85;}
.pf-card-form--locked .pf-card-form__step{background:var(--border);color:var(--muted);}
.pf-card-form__locked{display:flex;gap:10px;align-items:flex-start;
  background:var(--mq-tint);border:1px dashed var(--mq-line);
  border-radius:var(--r-card);padding:var(--sp-4);}
.pf-card-form__locked p{margin:0;}
.pf-card-form__lock{flex:0 0 auto;color:var(--muted);display:inline-flex;}

/* ---- Staff view of the two forms ---- */
.pf-staff__state{flex:0 0 auto;}
.pf-staff__gate{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--sp-4);
  background:var(--mq-tint);border:1px solid var(--mq-line);border-radius:var(--r-card);
  padding:var(--sp-4) var(--sp-5);margin-bottom:var(--sp-5);
}
.pf-staff__gate form{margin:0;}
.pf-staff__sub{font-family:var(--serif);font-size:1.02rem;margin:var(--sp-5) 0 var(--sp-3);
  display:flex;align-items:center;gap:8px;}
.pf-staff__sub:first-of-type{margin-top:0;}

/* ---- Profile recap of the two forms ---- */
.pf-profile{display:flex;flex-direction:column;}
.pf-profile__row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--sp-4);padding:var(--sp-4) 0;border-bottom:1px solid var(--border);}
.pf-profile__row:last-child{border-bottom:0;padding-bottom:0;}
.pf-profile__main{min-width:0;}
.pf-profile__actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}

/* The answer modals hold a lot of text — let them use the height available. */
.modal--wide .pf-answers{max-height:min(70vh,720px);overflow-y:auto;padding-inline-end:4px;}

/* ---- Project form + its comment thread (shared by the student page and the
       staff report tab — views/project/_form-thread.ejs) ---- */
.pf-thread{scroll-margin-top:90px;}      /* deep links from "Leave a comment" */
.pf-thread__head{align-items:flex-start;gap:var(--sp-4);margin-bottom:var(--sp-4);}
.pf-thread__head h2{margin:0;}
.pf-thread__lede{margin:4px 0 0;}
.pf-thread__state{flex:0 0 auto;}
.pf-thread__answers{border:1px solid var(--mq-line);border-radius:var(--r-card);
  background:var(--mq-tint);margin-bottom:var(--sp-5);}
.pf-thread__answers .pf-answers{padding-top:var(--sp-4);}
.pf-thread__sub{font-family:var(--serif);font-size:1.05rem;margin:0 0 var(--sp-3);
  display:flex;align-items:center;gap:8px;}
.pf-thread__comments{border-top:1px solid var(--border);padding-top:var(--sp-5);}
.pf-thread__post{margin-top:var(--sp-4);padding-top:var(--sp-4);
  border-top:1px dashed var(--border);}
.pf-thread__post .field{margin-bottom:var(--sp-3);}
.pf-modal-head{gap:var(--sp-4);margin-bottom:var(--sp-4);align-items:center;}

/* ==========================================================================
   M18 — .pill must be intrinsically sized everywhere
   ------------------------------------------------------------------------
   `.pill` is a bare inline <span>, which shrink-wraps fine in flow. But it is
   very often a FLEX ITEM of a *column* container — `.list__main`
   (styles.css:169), `.stat`, `.quick`, `.sess-card__body`, `.feed__body`,
   `.phase-tile__body`, the report/mini-row stacks, the project-form cards …
   In a column flex container the cross axis is horizontal, so the default
   `align-self:stretch` blew every pill out to the full row width: measured
   848px "student" bars on /director/users at 1440, 324px at 390, 262px at 320.
   Pinning the *width* (rather than align-self) fixes the cross-size without
   touching vertical alignment, so every row-flex context that already looked
   right — `.list__row`, `.list__actions`, `.mini-row`, `.rep-phase__head`
   (flex:0 0 auto), `.att-row .pill` (align-self:center), `.cal-today-row`,
   `.mqr-chips` — is left exactly as it was. `max-width:100%` keeps a long
   Arabic phrase inside its container instead of forcing a scrollbar, and
   `fit-content` degrades to plain shrink-to-fit in ordinary inline flow.
   ========================================================================== */
.pill{width:fit-content;max-width:100%;}

/* ==========================================================================
   STUDENT EXAMS PAGE — scoped redesign (.exams-page). CSS only; namespaced so
   nothing else is affected.
   ========================================================================== */
.exams-page{ --ex-line:#EFE7D8; --ex-ink:#173A3B; }
.exams-page .page-head{margin-bottom:24px;padding-bottom:18px;border-bottom:1px solid var(--ex-line);}
.exams-page .page-head h1{color:var(--primary);}
.exams-page .list{gap:16px;}
.exams-page .exam-card{
  align-items:center;border-radius:18px;border-color:var(--ex-line);padding:20px 24px;
  border-left:4px solid var(--primary);
  box-shadow:0 1px 2px rgba(36,53,54,.04),0 14px 30px -24px rgba(6,80,83,.4);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease);
}
.exams-page .exam-card:hover{transform:translateY(-2px);box-shadow:0 18px 34px -20px rgba(6,80,83,.5);}
.exams-page .exam-card--locked{border-left-color:#CFC7B6;opacity:.92;}
.exams-page .exam-card .list__main{gap:10px;}
.exams-page .exam-card strong{font-family:var(--serif);font-size:1.4rem;font-weight:700;color:var(--ex-ink);line-height:1.15;}
.exams-page .exam-pills{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}
.exams-page .exam-meta{font-size:.9rem;color:var(--muted);}
.exams-page .list__actions .btn{padding:10px 24px;font-size:.95rem;}
@media (max-width:600px){
  .exams-page .exam-card{padding:18px;}
  .exams-page .exam-card strong{font-size:1.2rem;}
}

/* ==========================================================================
   SIDEBAR NAVIGATION (desktop ≥720, logged-in app).
   Reflows the existing .topbar into a fixed left sidebar. The mobile top bar +
   bottom tab bar are untouched (they only render <720). CSS only — no markup
   change. Scoped to body.app; body.mq-page still hides the chrome.
   ========================================================================== */
@media (min-width:720px){
  body.app{ --sidebar-w:262px; }

  body.app .topbar{
    position:fixed; inset:0 auto 0 0; width:var(--sidebar-w); height:100dvh;
    border-bottom:none; border-right:1px solid rgba(0,0,0,.14);
    background:linear-gradient(185deg,var(--primary) 0%,#053B3D 100%);
    -webkit-backdrop-filter:none; backdrop-filter:none;
    box-shadow:2px 0 26px -18px rgba(6,80,83,.7); overflow-y:auto; z-index:45;
  }
  body.app .topbar__inner{
    max-width:none; margin:0; padding:22px 15px; height:100%;
    flex-direction:column; align-items:stretch; gap:20px;
  }
  /* Brand on a white badge so the teal/gold logo reads on the teal ground. */
  body.app .topbar .brand{gap:12px; padding:2px 6px;}
  body.app .topbar .brand__mark{height:auto; width:34px; padding:5px; background:#fff;
    border-radius:11px; box-shadow:0 6px 16px -8px rgba(0,0,0,.5);}
  body.app .topbar .brand__word{color:#fff; font-size:1.28rem;}

  /* Nav → vertical list; branded icons turned white so they read on the teal. */
  body.app .topnav{display:flex; flex-direction:column; gap:4px; margin-left:0;}
  body.app .topnav__link{display:flex; align-items:center; gap:12px; padding:11px 12px;
    border-radius:12px; font-size:.95rem; color:rgba(234,243,242,.82); white-space:nowrap; position:relative;}
  body.app .topnav__link .ico{width:20px; height:20px; filter:brightness(0) invert(1); opacity:.85;}
  body.app .topnav__link:hover{background:rgba(255,255,255,.08); color:#fff;}
  body.app .topnav__link:hover .ico{opacity:1;}
  body.app .topnav__link.is-active{background:rgba(255,255,255,.12); color:#fff;}
  body.app .topnav__link.is-active .ico{opacity:1;}
  body.app .topnav__link.is-active::before{content:""; position:absolute; left:-15px; top:50%;
    transform:translateY(-50%); width:4px; height:22px; border-radius:0 3px 3px 0; background:var(--gold);}

  /* User + Sign out pinned to the bottom of the sidebar. */
  body.app .topbar__right{margin-left:0; margin-top:auto; flex-direction:column; align-items:stretch;
    gap:12px; border-top:1px solid rgba(255,255,255,.14); padding-top:14px;}
  body.app .topbar__user{color:#fff;}
  body.app .topbar__user__name{color:#fff;}
  body.app .topbar__right form.inline{display:block;}
  body.app .topbar__right .btn--ghost{width:100%; justify-content:center; color:#EAF3F2;
    border-color:rgba(255,255,255,.24); background:transparent;}
  body.app .topbar__right .btn--ghost:hover{background:rgba(255,255,255,.09); color:#fff;}

  /* Page content sits to the right of the sidebar, centred in the space left. */
  body.app .container{
    margin-left:calc(var(--sidebar-w) + max(0px,(100% - var(--sidebar-w) - 960px)/2));
    margin-right:auto;
  }
}

/* ==========================================================================
   PROGRAM-WIDE INDEX PAGES — Homework / Exams / Projects (director).
   Collapsible cohort/phase groups (<details>/<summary>) with rows that link
   into the existing manage pages. Native disclosure — no JS.
   ========================================================================== */
.agg-group{padding:0; overflow:hidden;}
.agg-group + .agg-group{margin-top:18px;}
.agg-group > summary, .agg-sub > summary{list-style:none; cursor:pointer; user-select:none;}
.agg-group > summary::-webkit-details-marker, .agg-sub > summary::-webkit-details-marker{display:none;}
.agg-group__head{display:flex; align-items:center; gap:12px; padding:20px 24px;
  font-family:var(--serif); font-size:1.35rem; font-weight:700; color:#173A3B;}
.agg-group[open] > .agg-group__head{border-bottom:1px solid var(--line);}
.agg-group__head > span:first-child{flex:1;}
.agg-group__body{padding:18px 24px 22px;}

.agg-sub{margin-bottom:14px;}
.agg-sub:last-child{margin-bottom:0;}
.agg-sub__head{display:flex; align-items:center; gap:10px; padding:6px 0 10px;
  font-size:.72rem; font-weight:800; letter-spacing:.09em; text-transform:uppercase; color:var(--gold-dark);}

.agg-count{background:var(--primary-soft); color:var(--primary); font-size:.7rem; font-weight:800;
  padding:2px 9px; border-radius:999px; font-family:var(--sans); letter-spacing:0;}
.agg-chevron{margin-left:auto; flex:0 0 auto;}
.agg-chevron::before{content:"\203A"; display:inline-block; font-size:1.3em; line-height:1;
  color:var(--muted); transition:transform .18s ease;}
details[open] > summary .agg-chevron::before{transform:rotate(90deg);}

.agg-row{text-decoration:none; color:inherit; align-items:center;
  transition:box-shadow var(--dur) var(--ease),transform var(--dur) var(--ease),border-color var(--dur);}
.agg-row:hover{box-shadow:var(--sh-md); transform:translateY(-1px); border-color:var(--primary-soft);}
.agg-row .list__main strong{color:var(--text);}
.agg-row__go{color:var(--primary); font-weight:700; font-size:.85rem; white-space:nowrap;}
.agg-proj-title{max-width:22ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; opacity:.85;}
@media(max-width:520px){ .agg-proj-title{display:none;} }

/* ==========================================================================
   PER-QUESTION PROJECT COMMENTS — each answer shows its own comments + a
   comment box (staff) / reply (student). Reuses the existing .pcomment styles.
   ========================================================================== */
.pf-fields__gtitle{font-family:var(--serif); font-size:1.12rem; color:var(--primary); margin:16px 0 4px;}
.pf-field{padding:16px 0; border-top:1px solid var(--border);}
.pf-fields__group .pf-field:first-of-type{border-top:none;}
.pf-field__q{font-size:.72rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--gold-dark); margin-bottom:3px;}
.pf-field__hint{margin:0 0 7px;}
.pf-field__a{margin:0 0 12px; white-space:pre-wrap; overflow-wrap:anywhere; color:var(--text); line-height:1.6;}
.pf-field__comments{display:flex; flex-direction:column; gap:10px; margin-bottom:10px;}
.pf-field__post{background:#FAF6EC; border:1px solid var(--border); border-radius:12px; padding:12px 14px;}
.pf-field__post textarea, .pcomment__reply-form textarea{
  width:100%; border:1px solid var(--border); border-radius:var(--r-input,10px);
  padding:9px 11px; font:inherit; resize:vertical; background:var(--surface); color:var(--text);}
.pf-thread__sub{font-family:var(--serif); font-size:1.15rem; color:var(--primary); margin:18px 0 10px;}
.pf-thread__comments{margin-top:10px; border-top:1px solid var(--border); padding-top:8px;}

/* "＋ Add a comment" disclosure under each answer (staff, current draft only) */
.pf-field__addbox{margin-top:2px;}
.pf-field__addbox > summary{list-style:none; cursor:pointer; display:inline-flex; align-items:center;
  font-size:.82rem; font-weight:700; color:var(--primary);}
.pf-field__addbox > summary::-webkit-details-marker{display:none;}
.pf-field__addbox[open] > summary{margin-bottom:8px;}
/* Draft list → each row opens a popup with that draft's answers + comments. */
.pf-draftlist{display:flex; flex-direction:column; gap:10px; margin-top:8px;}
.pf-draftrow{display:flex; align-items:center; gap:12px; width:100%; text-align:left; cursor:pointer;
  padding:14px 18px; border:1px solid var(--border); border-radius:14px; background:var(--surface);
  font:inherit; color:var(--text);
  transition:box-shadow var(--dur) var(--ease),transform var(--dur) var(--ease),border-color var(--dur);}
.pf-draftrow:hover{box-shadow:var(--sh-md); transform:translateY(-1px); border-color:var(--primary-soft);}
.pf-draftrow--current{border-color:var(--primary-soft); background:linear-gradient(180deg,#fff,#F6FBFA);}
.pf-draftrow__no{font-family:var(--serif); font-weight:700; font-size:1.1rem; color:var(--primary); flex:0 0 auto;}
.pf-draftrow__meta{flex:0 0 auto;}
.pf-draftrow__go{margin-left:auto; color:var(--primary); font-weight:700; font-size:.86rem; white-space:nowrap;}
/* The one "Post comments" bar for the whole review. Sticky to the bottom of
   the modal body: the reviewer may be seventeen answers down when they finish
   writing, and hunting back up (or down) for a submit button is exactly the
   friction that made them post one comment at a time in the first place. */
.pf-postbar{position:sticky; bottom:0; z-index:2;
  display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap;
  margin-top:var(--sp-4); padding:12px 14px calc(12px + env(safe-area-inset-bottom));
  background:var(--surface); border-top:1px solid var(--border);
  box-shadow:0 -8px 20px -14px rgba(36,53,54,.5);}
.pf-postbar p{margin:0;}
@media(max-width:520px){
  .pf-postbar{flex-direction:column; align-items:stretch; text-align:center;}
  .pf-postbar .btn{width:100%;}
}

.pf-modal-actions{display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap;
  background:var(--primary-soft); border-radius:12px; padding:12px 14px; margin-bottom:16px;}
.pf-modal-actions p{margin:0; flex:1; min-width:200px;}

/* ==========================================================================
   "Crafted by Darsh" credit — landing footer + app sidebar.
   ========================================================================== */
.darsh-credit{display:inline-flex; align-items:center; gap:11px; text-decoration:none;
  color:var(--muted); font-size:1rem; font-weight:700; transition:color .18s ease;}
.darsh-credit:hover{color:var(--primary);}
.darsh-credit__logo{width:36px; height:36px; object-fit:contain; display:block;}

/* Landing nav: "Crafted by Darsh" stacked directly UNDER the brand, its logo
   lined up with the Bilisan logo (dark logo, reads on the cream/hero nav). */
.lp-nav .lp-nav__brand{grid-column:1; justify-self:start; display:flex; flex-direction:column;
  align-items:flex-start; gap:2px; min-width:0;}
/* Bigger brand; logo + wordmark stay vertically centred on each other. */
.lp-nav .brand{align-items:center; gap:12px;}
.lp-nav .brand__mark{height:52px;}
.lp-nav .brand__word{font-size:1.6rem;}
/* padding-left centres the 26px Darsh logo under the 52px brand logo above it. */
.darsh-credit--lpnav{display:flex; align-items:center; font-size:.86rem; gap:9px;
  color:var(--muted); padding-left:13px; white-space:nowrap;}
.darsh-credit--lpnav .darsh-credit__logo{width:26px; height:26px;}
@media (max-width:600px){
  .lp-nav .brand__mark{height:44px;}
  .lp-nav .brand__word{font-size:1.32rem;}
  .darsh-credit--lpnav{padding-left:9px; font-size:.82rem;}
  .darsh-credit--lpnav .darsh-credit__logo{width:24px; height:24px;}
}
/* The nav is a fixed overlay and is now two lines tall — give the hero clearance. */
@media (min-width:860px){ .hero{padding-top:118px;} }
@media (max-width:600px){ .hero{padding-top:112px;} }

/* App credit — two logos, one shown per context:
   · PHONES (logged in): tight under the brand in the top bar, DARK logo
   · LAPTOP: at the foot of the teal sidebar, LIGHT logo                        */
.darsh-logo--dark{display:none;}
@media (max-width:719px){
  body.app .topbar__inner{flex-wrap:wrap; row-gap:0;}
  body.app .topbar .brand{align-items:center; gap:12px;}
  body.app .topbar .brand__word{font-size:1.5rem;}    /* bigger program name */
  body.app .topbar .brand__mark{height:48px;}         /* bigger program logo */
  /* padding-left centres the 26px Darsh logo under the 48px brand logo. */
  body.app .darsh-credit--side{display:flex; align-items:center; flex-basis:100%; order:5;
    margin-top:0; padding-left:11px; gap:9px; color:var(--muted); font-size:.86rem;
    font-weight:600; text-decoration:none; white-space:nowrap;}
  body.app .darsh-credit--side .darsh-logo--light{display:none;}
  body.app .darsh-credit--side .darsh-logo--dark{display:block; width:26px; height:26px;}
}
@media (min-width:720px){
  body.app .darsh-credit--side{display:flex; align-items:center; gap:12px; margin-top:12px;
    padding:11px 12px; border-radius:12px; color:rgba(234,243,242,.85);
    font-size:.98rem; font-weight:700; text-decoration:none;}
  body.app .darsh-credit--side:hover{background:rgba(255,255,255,.07); color:#fff;}
  body.app .darsh-credit--side .darsh-logo--dark{display:none;}
  body.app .darsh-credit--side .darsh-logo--light{display:block; width:40px; height:40px;}
}

/* ==========================================================================
   SELF-ASSESSMENT  (the pre/post questionnaire)

   Three surfaces, one prefix:

     .sa-top / .sa-sec / .sa-row / .sa-field / .sa-submit
        the FORM (body.sa-page > .sa-shell). A 34-question page that has to
        stay readable, so: the scale is printed once per section and the five
        options sit in FIXED columns all the way down. That alignment is the
        single most important thing here — .sa-legend__nums and .sa-row__opts
        share --sa-opts and the same 5-column grid, so the numbers in the
        header sit exactly over the dots below them. Change one, change both.

     .sa-score / .sa-answers
        the READ-ONLY display, included from five places (cohort tab, profile,
        staff report, and two modals). No page-specific rules — it must look
        right inside a .section-card and inside a .modal__body alike.

     .sa-cards / .sa-card / .sa-profile / .sa-staff / .sa-list
        the CARDS that announce it and the staff roster.

   Reuses the app's existing primitives (.btn, .pill, .section-card, .stat,
   .list, .empty, .modal). Only NEW classes are defined below.
   ========================================================================== */

/* --------------------------------------------------------------------------
   A · THE FORM PAGE
   -------------------------------------------------------------------------- */

/* The whole app's chrome comes off, exactly as it does for Makamak: this is a
   focused sitting, and the top bar's own sticky positioning would fight the
   form's. The way out is the Exit link in .sa-top. Set server-side
   (bodyClass:'sa-page'), so the nav never flashes first. */
body.sa-page .topbar,
body.sa-page .bottomnav{display:none;}
body.sa-page .container{padding-top:0;padding-bottom:var(--sp-6);
  /* CENTRE IT. On a wide screen `body.app .container` reserves --sidebar-w on
     the left for the vertical nav and centres the page in what is left. This
     page HIDES that nav, so the reserved gutter stayed behind as dead space and
     the whole form sat visibly right of centre. With no sidebar to sit beside,
     plain auto margins are correct. Must stay AFTER the body.app rule (same
     specificity, later wins) — do not move it above. */
  margin-left:auto;margin-right:auto;}
body.sa-page{background:
  radial-gradient(900px 420px at 50% -12%,rgba(6,80,83,.06),transparent 70%),
  var(--bg);
  background-attachment:fixed;}

.sa-shell{width:100%;max-width:900px;margin:0 auto;
  /* The five option columns. Everything that must line up reads this. */
  --sa-opts:clamp(190px,34vw,270px);
  /* The right gutter the "Strongly Agree" pole lives in. Rows reserve it too,
     so the option columns land in the same place on the legend and every row. */
  --sa-pole:clamp(74px,9vw,104px);}

/* ---- sticky header: identity + progress + jump bar ---- */
.sa-top{position:sticky;top:0;z-index:30;margin:0 calc(var(--sp-4) * -1);
  padding:var(--sp-4) var(--sp-4) 0;
  background:color-mix(in srgb,var(--bg) 88%,transparent);
  backdrop-filter:blur(10px);
  border-bottom:1px solid var(--border);}
.sa-top__bar{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-4);}
.sa-top__title{font-family:var(--serif);font-size:1.35rem;line-height:1.2;margin:0;color:var(--primary);}
.sa-top__sub{margin:2px 0 0;color:var(--muted);font-size:.85rem;}
.sa-top__exit{flex:0 0 auto;}

.sa-progress{height:5px;border-radius:var(--r-pill);background:var(--primary-soft);
  margin:var(--sp-3) 0 var(--sp-2);overflow:hidden;}
.sa-progress__fill{display:block;height:100%;width:0;border-radius:inherit;
  background:var(--grad-primary);transition:width var(--dur) var(--ease);}

.sa-top__meta{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;
  padding-bottom:var(--sp-3);}
.sa-count{font-size:.82rem;color:var(--muted);white-space:nowrap;}
.sa-count strong{color:var(--primary);font-size:.95rem;}

.sa-jump{display:flex;gap:6px;overflow-x:auto;flex:1;min-width:0;
  scrollbar-width:none;-ms-overflow-style:none;
  /* The scrollbar is hidden, so a chip clipped flat at the edge reads as a
     rendering fault rather than as "there is more this way". Fade the last
     28px, the standard cue. Cosmetic only — the strip scrolls either way. */
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);
          mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);}
.sa-jump::-webkit-scrollbar{display:none;}
.sa-jump__chip{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;
  padding:5px 11px;border-radius:var(--r-pill);border:1px solid var(--border);
  background:var(--surface);color:var(--muted);font-size:.76rem;font-weight:600;
  text-decoration:none;transition:all var(--dur) var(--ease);}
.sa-jump__chip:hover{border-color:var(--primary);color:var(--primary);}
.sa-jump__chip.is-current{border-color:var(--primary);color:var(--primary);
  background:var(--primary-soft);}
.sa-jump__chip.is-done{border-color:rgba(47,143,111,.45);color:var(--success);}
.sa-jump__n{font-weight:700;opacity:.75;font-variant-numeric:tabular-nums;}

/* ---- intro ---- */
.sa-intro{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);
  padding:var(--sp-4) var(--sp-5);margin:var(--sp-5) 0 var(--sp-4);box-shadow:var(--sh-sm);}
.sa-intro__lede{margin:0 0 var(--sp-3);color:var(--text);font-size:.95rem;line-height:1.6;}
.sa-intro__scale{margin:0 0 var(--sp-3);font-size:.82rem;color:var(--muted);
  display:flex;flex-wrap:wrap;gap:4px 14px;align-items:baseline;}
.sa-intro__scale strong{color:var(--text);}
.sa-intro__step b{display:inline-block;min-width:1.1em;color:var(--primary);}
.sa-intro__due{display:flex;align-items:center;gap:7px;margin:0;font-size:.86rem;color:var(--gold-dark);}

.sa-note{margin:var(--sp-3) 0 0;padding:10px 13px;border-radius:var(--r-input);
  font-size:.84rem;line-height:1.55;color:var(--muted);
  background:var(--bg);border:1px solid var(--border);}
.sa-note--info{background:var(--primary-soft);border-color:rgba(6,80,83,.18);color:var(--primary);}

.sa-alert{margin:0 0 var(--sp-4);padding:11px 14px;border-radius:var(--r-input);
  background:rgba(192,57,43,.08);border:1px solid rgba(192,57,43,.3);
  color:var(--danger);font-size:.88rem;font-weight:600;}
.sa-alert[hidden]{display:none;}

/* ---- a section ---- */
.sa-sec{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);
  margin-bottom:var(--sp-4);box-shadow:var(--sh-sm);overflow:hidden;
  scroll-margin-top:150px;}   /* clears the sticky header when a chip jumps here */
.sa-sec__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5) var(--sp-3);}
.sa-sec__title{font-family:var(--serif);font-size:1.15rem;margin:0;color:var(--primary);}
.sa-sec__blurb{margin:2px 0 0;color:var(--muted);font-size:.83rem;}
.sa-sec__badge{flex:0 0 auto;font-size:.72rem;font-weight:700;letter-spacing:.03em;
  text-transform:uppercase;color:var(--muted);padding:4px 9px;border-radius:var(--r-pill);
  background:var(--bg);border:1px solid var(--border);white-space:nowrap;}
.sa-sec__badge.is-done{color:var(--success);border-color:rgba(47,143,111,.4);
  background:rgba(47,143,111,.08);}

/* ---- the scale legend: printed ONCE per section ----
   NOT sticky, deliberately. It was (pinned below .sa-top) and it did two bad
   things: it sat over the FIRST statement of its own section, and it swallowed
   clicks aimed at the rows scrolled underneath it — a decorative bar
   intercepting a radio is about the worst failure this page could have. A
   section holds 5-7 rows, so in the flow the legend stays on screen alongside
   its own rows, which is the only thing it was sticky for. Leave it there. */
/* ONE GRID, shared by the legend and every row beneath it. Four columns:
      1  the question number          (legend leaves it empty)
      2  the statement                (legend: "Strongly Disagree", right-aligned)
      3  the five options             (legend: the five numbers)
      4  a gutter                     (legend: "Strongly Agree"; rows leave it empty)
   Both selectors MUST keep the same template and the same gap. They were flex
   with different rules and the numbers did not sit over the dots — the one
   thing this layout exists to guarantee. Column 4 is why the options are not
   flush right: it is the space the high pole needs. */
.sa-legend,
.sa-row{display:grid;align-items:center;gap:var(--sp-3);
  grid-template-columns:1.6em minmax(0,1fr) var(--sa-opts) var(--sa-pole);}

.sa-legend{padding:8px var(--sp-5);background:var(--bg);border-block:1px solid var(--border);
  font-size:.7rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);}
.sa-legend__pole--low{grid-column:2;text-align:right;}
.sa-legend__nums{grid-column:3;display:grid;grid-template-columns:repeat(5,1fr);
  text-align:center;color:var(--primary);font-size:.78rem;}
.sa-legend__pole--high{grid-column:4;text-align:left;}

/* ---- one statement ---- */
.sa-rows{display:flex;flex-direction:column;}
.sa-row{padding:9px var(--sp-5);border-top:1px solid var(--border);
  transition:background var(--dur) var(--ease);scroll-margin-top:190px;}
.sa-rows > .sa-row:first-child{border-top:0;}
.sa-row:hover{background:var(--bg);}
.sa-row.is-missing{background:rgba(192,57,43,.06);
  box-shadow:inset 3px 0 0 var(--danger);}
.sa-row__no{text-align:right;font-size:.75rem;font-weight:700;
  color:var(--mist);font-variant-numeric:tabular-nums;}
.sa-row__stmt{min-width:0;font-size:.92rem;line-height:1.45;color:var(--text);}
.sa-row__opts{display:grid;grid-template-columns:repeat(5,1fr);justify-items:center;}

.sa-opt{display:flex;align-items:center;justify-content:center;cursor:pointer;
  padding:3px;margin:0;}
/* The native radio stays in the DOM (it is what gets submitted and what the
   keyboard drives) but is visually replaced by .sa-opt__dot. Not display:none —
   that removes it from the accessibility tree and from tab order. */
.sa-opt input[type="radio"]{position:absolute;opacity:0;width:1px;height:1px;margin:0;}
.sa-opt__dot{display:flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:50%;border:2px solid var(--border);
  background:var(--surface);color:transparent;font-size:.8rem;font-weight:700;
  transition:all var(--dur) var(--ease);user-select:none;}
.sa-opt:hover .sa-opt__dot{border-color:var(--primary);transform:scale(1.08);}
.sa-opt input:checked + .sa-opt__dot{background:var(--primary);border-color:var(--primary);
  color:#fff;box-shadow:0 3px 10px -3px rgba(6,80,83,.6);}
.sa-opt input:focus-visible + .sa-opt__dot{outline:2px solid var(--primary);outline-offset:3px;}

/* ---- the non-statement fields (name, date, the written answer) ---- */
.sa-field{display:flex;flex-direction:column;gap:5px;
  padding:var(--sp-3) var(--sp-5);border-top:1px solid var(--border);
  scroll-margin-top:190px;}
.sa-rows > .sa-field:first-child{border-top:0;}
.sa-field.is-missing{background:rgba(192,57,43,.06);box-shadow:inset 3px 0 0 var(--danger);}
.sa-field__label{font-size:.92rem;font-weight:600;color:var(--text);}
.sa-field__hint{font-size:.8rem;color:var(--muted);line-height:1.45;}
.sa-req{color:var(--danger);}
.sa-field__input{width:100%;max-width:520px;}
.sa-field__input--sm{max-width:220px;}

/* ---- submit ---- */
.sa-submit{text-align:center;padding:var(--sp-5) 0 var(--sp-8);}
.sa-submit__note{margin:0 0 var(--sp-3);font-size:.86rem;font-weight:600;color:var(--warning);}
.sa-submit__note.is-ready{color:var(--success);}
.sa-submit__actions{display:flex;gap:var(--sp-3);justify-content:center;flex-wrap:wrap;}
.sa-submit__fine{margin:var(--sp-3) auto 0;max-width:44ch;}

@media (max-width:720px){
  /* The statement moves above its options, and the options spread the full
     width. The five columns still line up with each other — only the legend's
     alignment to them is lost, which is why the legend goes too. */
  .sa-shell{--sa-opts:100%;--sa-pole:0px;}
  .sa-legend{display:none;}                       /* no columns left to label */
  .sa-row{grid-template-columns:minmax(0,1fr);padding:var(--sp-3) var(--sp-4);gap:6px;}
  .sa-row__stmt{order:1;}
  .sa-row__no{display:none;}                      /* decorative once it can not sit beside the text */
  .sa-row__opts{order:2;}
  .sa-opt__dot{width:34px;height:34px;color:var(--muted);}
  .sa-sec__head,.sa-field{padding-inline:var(--sp-4);}
  .sa-top__title{font-size:1.1rem;}
  .sa-sec{scroll-margin-top:170px;}
}

/* --------------------------------------------------------------------------
   B · THE SCORECARD  (.sa-score) — read-only, included everywhere
   -------------------------------------------------------------------------- */
.sa-score{margin:var(--sp-3) 0 0;}
.sa-score__head{display:flex;align-items:center;gap:var(--sp-4);flex-wrap:wrap;
  padding-bottom:var(--sp-3);}
.sa-score__big{display:flex;align-items:baseline;gap:3px;}
.sa-score__num{font-family:var(--serif);font-size:2.1rem;font-weight:700;line-height:1;color:var(--primary);}
.sa-score__den{font-size:.9rem;color:var(--muted);}
.sa-score__meta{display:flex;flex-direction:column;gap:4px;}

.sa-score__bars{display:flex;flex-direction:column;gap:7px;}
.sa-score__bar{display:grid;grid-template-columns:minmax(0,1fr) minmax(90px,2fr) 2.4em;
  align-items:center;gap:var(--sp-3);}
.sa-score__label{font-size:.83rem;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sa-score__track{display:block;height:7px;border-radius:var(--r-pill);
  background:var(--primary-soft);overflow:hidden;}
.sa-score__fill{display:block;height:100%;border-radius:inherit;background:var(--primary);
  transition:width var(--dur) var(--ease);}
.sa-score__fill--ok{background:var(--success);}
.sa-score__fill--primary{background:var(--primary);}
.sa-score__fill--warn{background:var(--warning);}
.sa-score__fill--muted{background:var(--border);}
.sa-score__val{font-size:.83rem;font-weight:700;color:var(--text);text-align:right;
  font-variant-numeric:tabular-nums;}
.sa-score__key{margin:var(--sp-3) 0 0;line-height:1.5;}

@media (max-width:560px){
  .sa-score__bar{grid-template-columns:minmax(0,1fr) 2.4em;row-gap:3px;}
  .sa-score__track{grid-column:1 / -1;order:2;}
}

/* --------------------------------------------------------------------------
   C · THE ANSWERS  (.sa-answers) — read-only, included everywhere
   -------------------------------------------------------------------------- */
.sa-answers{display:flex;flex-direction:column;gap:var(--sp-5);
  /* The five answer columns and the gutter the "Strongly Agree" pole sits in —
     the read-only twin of --sa-opts / --sa-pole on the form. Declared here so
     the fragment carries its own layout wherever it is included (a modal, a
     .section-card, the staff report). */
  --sa-adots:150px;
  --sa-apole:clamp(58px,7vw,92px);}
.sa-answers__group{border:1px solid var(--border);border-radius:var(--r-card);overflow:hidden;
  background:var(--surface);}
/* The section header is a <summary>: the whole bar is the toggle.
   BOTH the list-style and the WebKit pseudo-element are needed to drop the
   default disclosure triangle — Safari ignores one, Firefox the other — and
   display:flex only takes effect on a <summary> once the marker is gone. */
.sa-answers__head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:10px var(--sp-4);background:var(--bg);
  cursor:pointer;user-select:none;list-style:none;
  transition:background var(--dur) var(--ease);}
.sa-answers__head::-webkit-details-marker{display:none;}
.sa-answers__head:hover{background:var(--primary-soft);}
.sa-answers__head:focus-visible{outline:2px solid var(--primary);outline-offset:-2px;}
/* The rule under the header belongs to the OPEN state. On a collapsed section
   it would be a line under the last thing on screen, which reads as a mistake. */
.sa-answers__group[open] > .sa-answers__head{border-bottom:1px solid var(--border);}
.sa-answers__title{font-family:var(--serif);font-size:1rem;margin:0;color:var(--primary);}
.sa-answers__meta{display:flex;align-items:center;gap:10px;flex:0 0 auto;}
.sa-answers__chev{color:var(--muted);font-size:1rem;line-height:1;
  transition:transform var(--dur) var(--ease);}
.sa-answers__group[open] > .sa-answers__head .sa-answers__chev{transform:rotate(180deg);}
@media (prefers-reduced-motion:reduce){.sa-answers__chev{transition:none;}}

/* ONE GRID for the legend and its rows, same four columns and same reasoning as
   .sa-legend / .sa-row on the form: the numbers in the header MUST sit over the
   dots beneath them, and only a shared template guarantees that. */
.sa-answers__legend,
.sa-answers__row{display:grid;align-items:center;gap:var(--sp-3);
  grid-template-columns:1.6em minmax(0,1fr) var(--sa-adots) var(--sa-apole);}

.sa-answers__legend{padding:6px var(--sp-4);border-bottom:1px solid var(--border);
  font-size:.66rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);}
.sa-answers__legend > span:first-child{grid-column:2;text-align:right;}
.sa-answers__nums{grid-column:3;display:grid;grid-template-columns:repeat(5,1fr);
  text-align:center;color:var(--primary);}
.sa-answers__legend > span:last-child{grid-column:4;text-align:left;}

.sa-answers__row{padding:7px var(--sp-4);border-top:1px solid var(--border);}
.sa-answers__row:first-of-type{border-top:0;}
.sa-answers__row.is-blank{opacity:.55;}
.sa-answers__no{text-align:right;font-size:.72rem;font-weight:700;
  color:var(--mist);font-variant-numeric:tabular-nums;}
.sa-answers__stmt{min-width:0;font-size:.86rem;line-height:1.4;color:var(--text);}
.sa-answers__dots{display:grid;grid-template-columns:repeat(5,1fr);justify-items:center;}
.sa-answers__dot{display:flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:50%;background:var(--primary-soft);
  color:transparent;font-size:.7rem;font-weight:700;}
.sa-answers__dot.is-on{background:var(--primary);color:#fff;}

.sa-answers__qa{padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--border);
  display:flex;flex-direction:column;gap:3px;}
.sa-answers__qa:first-of-type{border-top:0;}
.sa-answers__label{font-size:.82rem;font-weight:700;color:var(--muted);
  text-transform:uppercase;letter-spacing:.03em;}
.sa-answers__body{margin:0;font-size:.94rem;line-height:1.6;color:var(--text);
  white-space:pre-wrap;}

@media (max-width:620px){
  /* The shared grid collapses to one column: statement, then its dots beneath.
     The legend goes with it — there are no columns left for it to label. */
  .sa-answers{--sa-adots:100%;--sa-apole:0px;}
  .sa-answers__legend{display:none;}
  .sa-answers__row{grid-template-columns:minmax(0,1fr);gap:4px;}
  .sa-answers__no{display:none;}
  .sa-answers__stmt{order:1;}
  .sa-answers__dots{order:2;justify-items:start;grid-template-columns:repeat(5,28px);}
}

/* --------------------------------------------------------------------------
   D · THE CARDS + THE STAFF ROSTER
   -------------------------------------------------------------------------- */
.sa-cards{display:flex;flex-direction:column;gap:var(--sp-4);}
.sa-card{position:relative;overflow:hidden;}
.sa-card::before{content:"";position:absolute;inset:0 auto 0 0;width:3px;background:var(--border);}
.sa-card--todo::before{background:var(--primary);}
.sa-card--reopened::before{background:var(--accent);}
.sa-card--locked::before{background:var(--success);}
.sa-card__head{display:flex;align-items:flex-start;gap:var(--sp-3);margin-bottom:var(--sp-3);}
.sa-card__step{flex:0 0 auto;width:30px;height:30px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--primary-soft);color:var(--primary);font-weight:800;font-size:.9rem;}
.sa-card__title{flex:1;min-width:0;}
.sa-card__title h2{margin:0 0 5px;font-size:1.1rem;}
.sa-card__pills{display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
.sa-card__lede{margin:0 0 var(--sp-2);color:var(--muted);font-size:.92rem;line-height:1.55;}
.sa-card__due{display:flex;align-items:center;gap:6px;margin:0 0 var(--sp-3);}
.sa-card__actions{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin-top:var(--sp-3);}
.sa-card__fine{margin-top:var(--sp-2);line-height:1.5;}

.sa-profile{display:flex;flex-direction:column;gap:var(--sp-4);}
.sa-profile__item{padding-top:var(--sp-3);border-top:1px solid var(--border);}
.sa-profile__item:first-child{padding-top:0;border-top:0;}
.sa-profile__head{display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-3);flex-wrap:wrap;}
.sa-profile__actions{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;}

.sa-staff{display:block;margin-bottom:var(--sp-6);}
.sa-staff__head{margin-bottom:var(--sp-3);}
.sa-list__row{gap:var(--sp-4);}
/* The bar sits between the name and the actions, and is the first thing to go
   when the row runs out of width — the name and the buttons matter more. */
.sa-list__score{flex:0 0 190px;display:flex;align-items:center;gap:var(--sp-3);}
.sa-list__score .sa-score__track{flex:1;}
.sa-list__num{font-size:.9rem;font-weight:700;color:var(--text);white-space:nowrap;
  font-variant-numeric:tabular-nums;}
.sa-list__num .muted{font-weight:400;font-size:.78rem;}
@media (max-width:820px){ .sa-list__score{display:none;} }

/* Print: the answers are the point; the form chrome is not. */
@media print{
  .sa-top,.sa-submit,.sa-jump,.sa-card__actions,.sa-profile__actions{display:none!important;}
  .sa-answers__group,.sa-sec{break-inside:avoid;}
}

/* --------------------------------------------------------------------------
   STUDENT DASHBOARD — the "Open my cohort" call to action.

   It was `btn btn--primary btn--sm`, tucked directly under the cohort/phase
   subtitle with no separation, and read as an afterthought. It is the single
   most-pressed control on the page: everything a student actually does lives
   inside their cohort. Give it room above and below, make it a real target,
   and lift it off the page so the eye lands on it first.
   -------------------------------------------------------------------------- */
.dash-cta{margin-top:var(--sp-5);margin-bottom:var(--sp-2);
  gap:10px;padding-inline:26px;
  box-shadow:0 12px 28px -12px rgba(6,80,83,.75);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease);}
.dash-cta:hover{transform:translateY(-2px);
  box-shadow:0 18px 34px -12px rgba(6,80,83,.85);}
.dash-cta:active{transform:translateY(0);}
@media (prefers-reduced-motion:reduce){
  .dash-cta,.dash-cta:hover{transform:none;transition:none;}
}

/* --------------------------------------------------------------------------
   SELF-ASSESSMENT — the two-line form list  (.sa-forms, _list.ejs)

   Beginning and End as one row each, with the whole result behind "Show
   summary". Shared by the student's profile and the staff student report.
   -------------------------------------------------------------------------- */
.sa-forms{display:flex;flex-direction:column;margin-top:var(--sp-3);}
.sa-formrow{display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-4);padding:var(--sp-3) 0;border-top:1px solid var(--border);}
.sa-forms > .sa-formrow:first-child{border-top:0;padding-top:0;}
.sa-formrow__main{min-width:0;}
.sa-formrow__title{display:block;}
.sa-formrow__side{display:flex;align-items:center;gap:var(--sp-2);
  flex-wrap:wrap;justify-content:flex-end;flex:0 0 auto;}
.sa-formrow__score{display:inline-flex;align-items:center;}
.sa-forms__hint{margin:var(--sp-4) 0 var(--sp-2);}

@media (max-width:600px){
  /* Stack, so the buttons keep a full-width tap target instead of being
     squeezed against the title. */
  .sa-formrow{flex-direction:column;align-items:stretch;gap:var(--sp-3);}
  .sa-formrow__side{justify-content:flex-start;}
}

/* --------------------------------------------------------------------------
   RICH TEXT EDITOR  (.rt-*, public/js/richtext.js)

   The box staff type a homework description / session objective into. The
   REAL field is still the <textarea data-richtext> — richtext.js moves it in
   here, hides it and keeps its value in step; with JS off none of this exists
   and the textarea is just a textarea. So everything below is decoration on a
   working form.

   Note: .rt-u and .rt-c--* (the RENDER classes, used on the pages students
   read) are defined elsewhere in this file — nothing here touches them. The
   editor shows its colours off [data-rt-c], which only ever exists inside the
   editable.
   -------------------------------------------------------------------------- */
.rt{position:relative;display:flex;flex-direction:column;}

/* Visually hidden, NOT display:none — the browser has to be able to focus it
   to report a validation failure, and a display:none control can't be. */
.rt-input{position:absolute;left:0;bottom:0;width:1px;height:1px;min-height:0;
  padding:0;margin:0;border:0;opacity:0;overflow:hidden;resize:none;pointer-events:none;}

.rt-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);
  padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-bottom:0;
  border-radius:var(--r-input) var(--r-input) 0 0;}
.rt-group{display:flex;align-items:center;gap:4px;}
.rt-group + .rt-group{padding-left:var(--sp-2);border-left:1px solid var(--border);}

.rt-btn{font:inherit;line-height:1;min-width:36px;height:36px;padding:0 8px;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--muted);background:var(--surface);border:1px solid var(--border);border-radius:8px;
  transition:background var(--dur) var(--ease),color var(--dur),border-color var(--dur);}
.rt-btn:hover{background:var(--primary-soft);color:var(--primary);}
.rt-btn.is-on{background:var(--primary-soft);color:var(--primary);border-color:var(--primary);}
.rt-btn--b{font-weight:800;}
.rt-btn--i{font-style:italic;font-family:var(--serif);font-size:1.05rem;}
.rt-btn--u{text-decoration:underline;text-underline-offset:2px;}

/* A swatch is the colour itself; the ring is what says "this one is on". */
.rt-swatch{padding:0;}
.rt-swatch__dot{width:18px;height:18px;border-radius:var(--r-pill);
  background:var(--rt-sw,transparent);box-shadow:inset 0 0 0 1px rgba(36,53,54,.18);}
.rt-swatch.is-on{border-color:var(--primary);box-shadow:0 0 0 2px rgba(6,80,83,.18);}
/* The palette — the same hexes as COLORS in lib/richtext.js. */
.rt-swatch[data-rt-color="teal"]{--rt-sw:#065053;}
.rt-swatch[data-rt-color="gold"]{--rt-sw:#9A6510;}
.rt-swatch[data-rt-color="red"]{--rt-sw:#A62D3B;}
.rt-swatch[data-rt-color="green"]{--rt-sw:#2F6B45;}
/* "No colour": a struck-through empty circle. */
.rt-swatch--none .rt-swatch__dot{background:var(--surface);position:relative;overflow:hidden;}
.rt-swatch--none .rt-swatch__dot::after{content:"";position:absolute;left:-2px;right:-2px;top:50%;
  height:1px;background:var(--danger);transform:rotate(-45deg);}

/* The editable itself wears the app's field styling (see `input,textarea`
   near the top of this file), minus the top corners the toolbar now owns.
   white-space:pre-wrap is load-bearing, not cosmetic: it is what makes the
   line breaks and repeated spaces the writer types survive as typed. */
.rt-editable{font:inherit;color:var(--text);background:var(--surface);
  border:1px solid var(--border);border-radius:0 0 var(--r-input) var(--r-input);
  padding:11px 13px;min-height:7.5em;max-height:60vh;overflow-y:auto;
  line-height:1.6;white-space:pre-wrap;overflow-wrap:break-word;
  transition:border var(--dur),box-shadow var(--dur);}
.rt-editable:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 3px rgba(6,80,83,.15);}
.rt:focus-within .rt-toolbar{border-color:var(--primary);}
.rt-editable.is-empty::before{content:attr(data-rt-placeholder);color:var(--muted);opacity:.75;}

/* What the four marks look like WHILE writing — deliberately the same as what
   lib/richtext.js renders on the page afterwards. */
.rt-editable b,.rt-editable strong{font-weight:700;}
.rt-editable u{text-decoration:underline;text-underline-offset:2px;}
.rt-editable [data-rt-c]{font-weight:600;}   /* as .rt-c does on the page */
.rt-editable [data-rt-c="teal"]{color:#065053;}
.rt-editable [data-rt-c="gold"]{color:#9A6510;}
.rt-editable [data-rt-c="red"]{color:#A62D3B;}
.rt-editable [data-rt-c="green"]{color:#2F6B45;}

@media (max-width:600px){
  /* Thumbs, not cursors: full 40px targets, and the toolbar is free to wrap
     onto a second row rather than squeeze. */
  .rt-toolbar{gap:6px;padding:6px;}
  .rt-btn{min-width:40px;height:40px;}
  .rt-group + .rt-group{padding-left:6px;}
  .rt-editable{min-height:8.5em;}
}

@media (prefers-reduced-motion:reduce){
  .rt-btn,.rt-editable{transition:none;}
}
