/* ==========================================================================
   SE / SPG Project Portal — mock design system
   A thin design layer on top of Bootstrap 5.3.
   Port target: save as app/static/css/portal.css and load it AFTER a single
   Bootstrap stylesheet in base.html.
   ========================================================================== */

/* ---------- 1. Design tokens ---------------------------------------------- */
:root {
  /* shared navy / teal / sky palette */
  --brand-900: #092f3a;
  --brand-800: #0b5060;
  --brand-700: #116d7b;
  --brand-600: #188b97;
  --brand-500: #37aab1;
  --brand-100: #dff4f3;
  --brand-050: #f1faf9;
  --brand-fill: #116d7b;
  --brand-fill-hover: #0b5060;
  --brand-fill-active: #092f3a;

  --accent-700: #195e85;
  --accent-600: #2c82aa;
  --accent-500: #62b7d3;
  --accent-100: #e1f3f8;

  /* neutrals */
  --ink-900: #17191e;
  --ink-800: #262a31;
  --ink-700: #3c414a;
  --ink-600: #545a64;
  --ink-500: #6c727c;
  --ink-400: #949aa4;
  --ink-300: #c4c8ce;

  --line:      #dce8eb;
  --line-soft: #e4eef0;
  --surface:   #ffffff;
  --surface-1: #f9fcfc;
  --surface-2: #f5fafb;   /* page background */
  --surface-3: #eaf2f4;

  /* semantic */
  --ok-700: #14663f;   --ok-600: #1a7d4c;   --ok-bg: #e6f4ec;   --ok-line: #b9e0cb;
  --warn-700: #8a5800; --warn-600: #a76c00; --warn-bg: #fbf0da; --warn-line: #f0d9a8;
  --danger-700: #97231c; --danger-600: #b52d24; --danger-bg: #fce9e7; --danger-line: #f2c4be;
  --info-700: #1c527a; --info-600: #216691; --info-bg: #e6f0f7; --info-line: #bcd7e8;
  --ok-fill: #1a7d4c; --ok-fill-hover: #14663f;
  --danger-fill: #b52d24; --danger-fill-hover: #97231c;

  /* layout — single source of truth for the page frame.
     Every page (header bar and content) is centred in the same 1320px
     column with the same side padding, so the left and right edges line
     up when navigating between pages. --field-max keeps single-column
     form inputs at a readable measure inside a full-width card. */
  --page-max: 1320px;
  --page-inline: clamp(16px, 4vw, 32px);
  --field-max: 560px;

  /* radius */
  --r-xs: 6px;
  --r-sm: 9px;
  --r-md: 13px;
  --r-lg: 18px;
  --r-pill: 999px;

  /* elevation */
  --shadow-xs: 0 1px 2px rgba(20, 24, 33, .05);
  --shadow-sm: 0 1px 2px rgba(20, 24, 33, .04), 0 2px 6px rgba(20, 24, 33, .06);
  --shadow-md: 0 4px 12px rgba(20, 24, 33, .09), 0 2px 4px rgba(20, 24, 33, .05);
  --shadow-lg: 0 16px 40px rgba(20, 24, 33, .16);

  --ring: 0 0 0 3px rgba(55, 170, 177, .22);

  /* type */
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* map Bootstrap primary onto the brand */
  --bs-primary: var(--brand-700);
  --bs-primary-rgb: 17, 109, 123;
  --bs-link-color: var(--brand-700);
  --bs-link-color-rgb: 17, 109, 123;
  --bs-link-hover-color: var(--brand-800);
  --bs-border-color: var(--line);
  --bs-body-color: var(--ink-700);
  --bs-body-bg: var(--surface-2);
  --bs-body-font-family: var(--font-sans);
  --bs-border-radius: var(--r-sm);
  --bs-border-radius-sm: var(--r-xs);
  --bs-border-radius-lg: var(--r-md);
}

/* Course-specific mock content; the production templates should use the
   selected term's course to render the appropriate branch. */
:root[data-course="se"] [data-course-only="spg"],
:root[data-course="spg"] [data-course-only="se"] { display: none !important; }

.course-switch {
  display: inline-flex; flex: none; padding: 3px; gap: 2px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface-2);
}
.course-switch button {
  border: 0; background: transparent; color: var(--ink-500);
  padding: 4px 10px; border-radius: var(--r-pill);
  font-size: .78rem; font-weight: 700; line-height: 1.35;
}
.course-switch button.is-active {
  background: linear-gradient(150deg, var(--brand-fill), var(--brand-fill-hover)); color: #fff;
  box-shadow: var(--shadow-xs);
}
.course-switch button:focus-visible { outline: 2px solid var(--brand-700); outline-offset: 2px; }
.tile.is-selected { border-color: var(--brand-500); box-shadow: var(--ring); }
#milestones, #spg-submissions { scroll-margin-top: 80px; }
@media (max-width: 520px) {
  .course-switch button { padding: 4px 7px; }
  .app-header__inner .app-user { gap: 4px; }
  .app-header__inner { gap: 8px; }
  .app-user [data-theme-toggle], .app-user > .avatar, .app-user > a.btn { display: none; }
  .app-user .dropdown-toggle { font-size: 0; padding: 4px; }
  .app-user .dropdown-toggle .avatar { font-size: .72rem; }
}
@media (max-width: 360px) {
  .app-header__inner .brand > span:last-child { display: none; }
}

/* ---------- 2. Dark theme --------------------------------------------------
   Applied when the OS prefers dark and the page hasn't forced a light theme,
   or when data-theme="dark" is set explicitly. Bootstrap components also read
   [data-bs-theme] which the mock sets alongside data-theme.                */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink-900: #f3f8f9;
    --ink-800: #e1ecee;
    --ink-700: #cad9dc;
    --ink-600: #b4c7cb;
    --ink-500: #9fb2b9;
    --ink-400: #8299a2;
    --ink-300: #526b74;

    --line:      #2d414c;
    --line-soft: #263944;
    --surface:   #1b2a34;
    --surface-1: #15232d;
    --surface-2: #101c26;
    --surface-3: #20313c;

    --brand-800: #a2e8e2;
    --brand-700: #75d9d2;
    --brand-600: #52c6c4;
    --brand-500: #43b9bd;
    --brand-100: #14393e;
    --brand-050: #102a30;
    --accent-700: #9bdaf1;
    --accent-600: #75c8e1;
    --accent-500: #4aa9c9;
    --accent-100: #143546;

    --ok-bg: #12271d;   --ok-line: #1f4633;   --ok-700: #74dba0; --ok-600: #58c68d;
    --warn-bg: #2a2110; --warn-line: #4a3a17; --warn-700: #f2c76f; --warn-600: #e0ad4f;
    --danger-bg: #2c1613; --danger-line: #4c231e; --danger-700: #f4a29b; --danger-600: #e28880;
    --info-bg: #12222e;  --info-line: #204458;  --info-700: #88c9e8; --info-600: #6bb2d8;

    --bs-primary-rgb: 117, 217, 210;
    --bs-link-color-rgb: 117, 217, 210;
    --bs-link-color: var(--brand-700);
    --bs-link-hover-color: var(--brand-800);
    --shadow-sm: 0 1px 2px rgba(0,0,0,.3), 0 2px 6px rgba(0,0,0,.35);
    --shadow-md: 0 4px 14px rgba(0,0,0,.45);
    --shadow-lg: 0 18px 44px rgba(0,0,0,.55);
    --ring: 0 0 0 3px rgba(117, 217, 210, .28);
  }
}
:root[data-theme="dark"] {
  --ink-900: #f3f8f9; --ink-800: #e1ecee; --ink-700: #cad9dc; --ink-600: #b4c7cb;
  --ink-500: #9fb2b9; --ink-400: #8299a2; --ink-300: #526b74;
  --line: #2d414c; --line-soft: #263944;
  --surface: #1b2a34; --surface-1: #15232d; --surface-2: #101c26; --surface-3: #20313c;
  --brand-800: #a2e8e2; --brand-700: #75d9d2; --brand-600: #52c6c4; --brand-500: #43b9bd;
  --brand-100: #14393e; --brand-050: #102a30;
  --accent-700: #9bdaf1; --accent-600: #75c8e1; --accent-500: #4aa9c9; --accent-100: #143546;
  --ok-bg: #12271d; --ok-line: #1f4633; --ok-700: #74dba0; --ok-600: #58c68d;
  --warn-bg: #2a2110; --warn-line: #4a3a17; --warn-700: #f2c76f; --warn-600: #e0ad4f;
  --danger-bg: #2c1613; --danger-line: #4c231e; --danger-700: #f4a29b; --danger-600: #e28880;
  --info-bg: #12222e; --info-line: #204458; --info-700: #88c9e8; --info-600: #6bb2d8;
  --bs-primary-rgb: 117, 217, 210;
  --bs-link-color-rgb: 117, 217, 210;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.3), 0 2px 6px rgba(0,0,0,.35);
  --shadow-md: 0 4px 14px rgba(0,0,0,.45);
  --shadow-lg: 0 18px 44px rgba(0,0,0,.55);
  --ring: 0 0 0 3px rgba(117, 217, 210, .28);
}

/* ---------- 3. Base ------------------------------------------------------- */
* { box-sizing: border-box; }

body {
  font-family: var(--font-sans);
  background: var(--surface-2);
  color: var(--ink-700);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 { color: var(--ink-900); font-weight: 650; letter-spacing: -0.011em; }
h1 { font-size: 1.9rem; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.2rem; }
h4 { font-size: 1.05rem; }

a { text-decoration: none; }
a:hover { text-decoration: underline; }
/* Only text links underline on hover. A link styled as a control (a button, chip,
   menu item or page number) behaves like the <button> beside it, which does not. */
.btn:hover, .btn:focus, .chip:hover, .dropdown-item:hover, .dropdown-item:focus,
.page-link:hover, .nav-link:hover, .list-group-item-action:hover { text-decoration: none; }
/* Bootstrap underlines a link-styled button at rest; its colour already marks it. */
.btn-link { text-decoration: none; }

hr { border-color: var(--line); opacity: 1; }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-xs); }

::selection { background: var(--brand-100); color: var(--brand-800); }

.text-muted { color: var(--ink-500) !important; }
.text-soft  { color: var(--ink-400); }
.mono { font-family: var(--font-mono); }
.eyebrow {
  font-size: .72rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-400);
}

/* ---------- 4. App shell ------------------------------------------------- */
.app-header {
  position: sticky; top: 0; z-index: 1030;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}
.app-header__inner {
  max-width: var(--page-max); margin: 0 auto;
  padding: 0 var(--page-inline);
  height: 62px;
  display: flex; align-items: center; gap: 28px;
}
.brand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 700; color: var(--ink-900); white-space: nowrap;
}
.brand:hover { text-decoration: none; }
.brand__mark {
  width: 30px; height: 30px; border-radius: 8px;
  display: grid; place-items: center;
  background: linear-gradient(150deg, var(--brand-fill), var(--brand-fill-hover));
  color: #fff; font-size: 15px; box-shadow: var(--shadow-xs);
}
.brand__mark img { width: 20px; height: 20px; display: block; }
.brand small { display: block; font-weight: 500; color: var(--ink-400); font-size: .7rem; letter-spacing: .02em; }

.app-nav { display: flex; align-items: center; gap: 2px; margin-right: auto; }
.app-nav a {
  --_c: var(--ink-600);
  color: var(--_c); font-weight: 550; font-size: .9rem;
  padding: 7px 12px; border-radius: var(--r-xs);
  display: inline-flex; align-items: center; gap: 7px;
  transition: background .14s ease, color .14s ease;
}
.app-nav a:hover { color: var(--ink-900); background: var(--surface-2); text-decoration: none; }
.app-nav a.is-active { color: var(--brand-700); background: var(--brand-050); }
.app-nav a .bi { font-size: 1rem; opacity: .8; }

.app-user { display: flex; align-items: center; gap: 10px; }
.avatar {
  width: 34px; height: 34px; border-radius: var(--r-pill);
  display: grid; place-items: center; flex: none;
  background: var(--brand-100); color: var(--brand-800);
  font-weight: 650; font-size: .85rem; border: 1px solid var(--brand-100);
}
.avatar--lg { width: 56px; height: 56px; font-size: 1.15rem; }
.avatar--sm { width: 28px; height: 28px; font-size: .72rem; }

.nav-toggle {
  display: none; border: 1px solid var(--line); background: var(--surface);
  width: 38px; height: 38px; border-radius: var(--r-xs); color: var(--ink-700);
  font-size: 1.15rem;
}

.app-nav__mobile { display: none; }

@media (max-width: 860px) {
  .app-nav { display: none; }
  .nav-toggle { display: grid; place-items: center; order: 3; }
  .app-user { margin-left: auto; order: 2; }
  .app-header__inner { gap: 14px; }
  .brand small { display: none; }
  .app-nav__mobile {
    display: block; border-bottom: 1px solid var(--line); background: var(--surface);
  }
  .app-nav__mobile.is-open { padding: 8px var(--page-inline) 14px; }
  .app-nav__mobile a {
    display: flex; align-items: center; gap: 10px;
    padding: 11px 12px; border-radius: var(--r-xs); color: var(--ink-700); font-weight: 550;
  }
  .app-nav__mobile a.is-active { color: var(--brand-700); background: var(--brand-050); }
}

/* ---------- 5. Page layout -------------------------------------------------
   Replaces the <div class="col-2"></div> … spacer pattern.               */
.page {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: clamp(20px, 4vw, 40px) var(--page-inline) 80px;
}
/* The old .page--narrow / .page--wide tiers are gone: every page shares the
   one frame above. Content that needs to stay narrow (form fields, prose)
   is constrained inside the card instead, so the page edges never shift. */

/* A Bootstrap .container nested inside page content would re-introduce its
   own breakpoint widths and re-centre the block. Inside .page the frame is
   already set, so neutralise it. */
/* Keep Bootstrap's own 12px inline padding: it cancels the negative gutter
   margins of any .row nested inside. Only the breakpoint max-width and the
   auto-centring are removed. */
.page .container,
.page .container-fluid { max-width: none; width: 100%; }

.page-head { margin-bottom: 28px; }
.page-head__row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.page-head h1 { margin: 0 0 6px; }
.page-head p { margin: 0; color: var(--ink-500); max-width: 60ch; }
.page-head__actions { display: flex; gap: 10px; flex-wrap: wrap; }

.breadcrumbs {
  display: flex; align-items: center; gap: 8px;
  font-size: .84rem; color: var(--ink-400); margin-bottom: 16px; flex-wrap: wrap;
}
.breadcrumbs a { color: var(--ink-500); }
.breadcrumbs a:hover { color: var(--brand-700); }
.breadcrumbs .bi { font-size: .7rem; }

.section { margin-top: 34px; }
.section > h2, .section > h3 { margin-bottom: 14px; }
.section__label {
  display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
}
.section__label h2, .section__label h3 { margin: 0; }
.section__label .count {
  font-size: .8rem; font-weight: 600; color: var(--ink-500);
  background: var(--surface-3); padding: 1px 8px; border-radius: var(--r-pill);
}

/* ---------- 6. Cards ---------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
}
.card-header {
  background: transparent;
  border-bottom: 1px solid var(--line-soft);
  padding: 16px 20px;
  font-weight: 600; color: var(--ink-900);
  display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap; /* chips (status, deadline) move to a second line on phones */
}
.card-header .bi { color: var(--ink-400); }
.card-body { padding: 20px; }
.card-footer {
  background: var(--surface-1); border-top: 1px solid var(--line-soft);
  padding: 14px 20px;
}
.card--flush .card-body { padding: 0; }
.card--pad-lg .card-body { padding: 28px; }

.tile {
  display: block; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 18px; box-shadow: var(--shadow-xs);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
  color: inherit;
}
a.tile:hover, button.tile:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-md);
  border-color: var(--brand-100); text-decoration: none;
}
.tile__icon {
  width: 40px; height: 40px; border-radius: var(--r-sm);
  display: grid; place-items: center; margin-bottom: 12px;
  background: var(--brand-050); color: var(--brand-700); font-size: 1.15rem;
}
.tile__icon--accent { background: var(--accent-100); color: var(--accent-700); }
.tile__icon--info { background: var(--info-bg); color: var(--info-700); }
.tile__icon--ok   { background: var(--ok-bg); color: var(--ok-700); }
/* Tiles used as modal triggers are buttons; reset the UA button defaults so they
   match the anchor tiles exactly. */
button.tile { width: 100%; text-align: left; font: inherit; cursor: pointer; }
.tile h4 { margin: 0 0 4px; font-size: .98rem; }
.tile p { margin: 0; font-size: .86rem; color: var(--ink-500); }

/* ---------- 7. Stats -------------------------------------------------- */
.stat {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 16px 18px;
}
.stat__label {
  font-size: .78rem; font-weight: 600; color: var(--ink-500);
  text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px;
  display: flex; align-items: center; gap: 6px;
}
.stat__value { font-size: 1.8rem; font-weight: 700; color: var(--ink-900); line-height: 1; }
.stat__meta { font-size: .8rem; color: var(--ink-400); margin-top: 6px; }
.stat--accent { border-left: 3px solid var(--brand-700); }
.stat--ok    { border-left: 3px solid var(--ok-600); }
.stat--warn  { border-left: 3px solid var(--warn-600); }
.stat--info  { border-left: 3px solid var(--info-600); }

/* ---------- 8. Buttons ------------------------------------------------ */
.btn {
  --bs-btn-font-weight: 600;
  --bs-btn-border-radius: var(--r-sm);
  --bs-btn-padding-y: .5rem;
  --bs-btn-padding-x: .95rem;
  --bs-btn-font-size: .9rem;
  letter-spacing: -.005em;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: transform .12s ease, box-shadow .14s ease, background-color .14s ease;
}
.btn:active { transform: translateY(1px); }
.btn .bi { font-size: 1.05em; }

.btn-primary {
  --bs-btn-bg: var(--brand-fill); --bs-btn-border-color: var(--brand-fill);
  --bs-btn-hover-bg: var(--brand-fill-hover); --bs-btn-hover-border-color: var(--brand-fill-hover);
  --bs-btn-active-bg: var(--brand-fill-active); --bs-btn-active-border-color: var(--brand-fill-active);
  --bs-btn-disabled-bg: var(--brand-fill); --bs-btn-disabled-border-color: var(--brand-fill);
  --bs-btn-color: #fff; --bs-btn-hover-color: #fff; --bs-btn-active-color: #fff;
  box-shadow: var(--shadow-xs);
}
.btn-primary:hover { box-shadow: var(--shadow-sm); }

.btn-outline-primary {
  --bs-btn-color: var(--brand-700); --bs-btn-border-color: var(--line);
  --bs-btn-hover-bg: var(--brand-050); --bs-btn-hover-border-color: var(--brand-100);
  --bs-btn-hover-color: var(--brand-800);
  --bs-btn-active-bg: var(--brand-100); --bs-btn-active-color: var(--brand-800);
  --bs-btn-active-border-color: var(--brand-100);
  background: var(--surface);
}
.btn-ghost {
  --bs-btn-color: var(--ink-600);
  background: transparent; border: 1px solid transparent;
}
.btn-ghost:hover { background: var(--surface-2); color: var(--ink-900); }

.btn-outline-secondary {
  --bs-btn-color: var(--ink-600); --bs-btn-border-color: var(--line);
  --bs-btn-hover-bg: var(--surface-2); --bs-btn-hover-color: var(--ink-900);
  --bs-btn-hover-border-color: var(--ink-300);
  background: var(--surface);
}
.btn-success { --bs-btn-bg: var(--ok-fill); --bs-btn-border-color: var(--ok-fill);
  --bs-btn-hover-bg: var(--ok-fill-hover); --bs-btn-hover-border-color: var(--ok-fill-hover); }
.btn-danger  { --bs-btn-bg: var(--danger-fill); --bs-btn-border-color: var(--danger-fill);
  --bs-btn-hover-bg: var(--danger-fill-hover); --bs-btn-hover-border-color: var(--danger-fill-hover); }

.btn-lg { --bs-btn-padding-y: .7rem; --bs-btn-padding-x: 1.3rem; --bs-btn-font-size: .98rem; }
.btn-sm { --bs-btn-padding-y: .34rem; --bs-btn-padding-x: .7rem; --bs-btn-font-size: .82rem; }

/* ---------- 9. Badges / status ------------------------------------------ */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .77rem; font-weight: 600; line-height: 1;
  padding: 5px 10px; border-radius: var(--r-pill);
  background: var(--surface-3); color: var(--ink-600);
  border: 1px solid transparent; white-space: nowrap;
}
.chip .bi { font-size: .9em; }
.chip::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .9; flex: none;
}
.chip--plain::before { display: none; }
.chip--ok      { background: var(--ok-bg); color: var(--ok-700); border-color: var(--ok-line); }
.chip--warn    { background: var(--warn-bg); color: var(--warn-700); border-color: var(--warn-line); }
.chip--danger  { background: var(--danger-bg); color: var(--danger-700); border-color: var(--danger-line); }
.chip--info    { background: var(--info-bg); color: var(--info-700); border-color: var(--info-line); }
.chip--brand   { background: var(--brand-050); color: var(--brand-700); border-color: var(--brand-100); }
.chip--accent  { background: var(--accent-100); color: var(--accent-700); border-color: var(--accent-100); }
.chip--live::before { animation: pulse-dot 1.4s ease-in-out infinite; }

@keyframes pulse-dot { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
@keyframes blink { 0%,49%,100% { opacity: 1; } 50%,99% { opacity: .5; } }

.tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .78rem; font-weight: 550;
  padding: 3px 9px; border-radius: var(--r-xs);
  background: var(--surface-2); color: var(--ink-600); border: 1px solid var(--line);
}
.tag--rank { padding-left: 6px; }
.tag--rank b { color: var(--brand-700); }

/* ---------- 10. Forms ------------------------------------------------- */
.form-label { font-weight: 600; color: var(--ink-800); font-size: .875rem; margin-bottom: 5px; }
.form-label .req { color: var(--danger-600); margin-left: 2px; }
.form-control, .form-select {
  border-color: var(--line); border-radius: var(--r-sm);
  padding: .55rem .8rem; font-size: .92rem; color: var(--ink-800);
  background-color: var(--surface);
}
.form-control::placeholder { color: var(--ink-400); }
.form-control:focus, .form-select:focus {
  border-color: var(--brand-500); box-shadow: var(--ring);
}
.form-text { color: var(--ink-400); font-size: .8rem; }
.form-control.is-valid, .was-validated .form-control:valid { border-color: var(--ok-600); }
.form-control.is-invalid { border-color: var(--danger-600); }
.input-hint { display: flex; align-items: center; gap: 6px; font-size: .8rem; margin-top: 6px; }
.input-hint--ok { color: var(--ok-700); }
.input-hint--err { color: var(--danger-700); }

/* Label column, then a field column capped at --field-max. The card still
   spans the full page frame, so page edges match every other screen, while
   the inputs themselves stay at a readable width. */
.field-row {
  display: grid; grid-template-columns: 190px minmax(0, var(--field-max)); gap: 16px;
  align-items: start;
  padding: 16px 0; border-bottom: 1px solid var(--line-soft);
}
.field-row:last-child { border-bottom: 0; }
.field-row > .form-label { padding-top: 8px; margin: 0; }
@media (max-width: 640px) { .field-row { grid-template-columns: 1fr; gap: 6px; } }

.segmented {
  display: inline-flex; background: var(--surface-3); border-radius: var(--r-sm);
  padding: 3px; gap: 3px;
}
.segmented button, .segmented label, .segmented a {
  border: 0; background: transparent; color: var(--ink-600);
  font-weight: 600; font-size: .85rem; padding: 6px 14px; border-radius: var(--r-xs);
  cursor: pointer; margin: 0;
}
.segmented a { display: inline-flex; align-items: center; gap: 6px; }
.segmented a:hover { color: var(--ink-900); text-decoration: none; }
.segmented button.is-active, .segmented a.is-active, .segmented input:checked + label {
  background: var(--surface); color: var(--brand-700); box-shadow: var(--shadow-xs);
}
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented input:focus-visible + label { outline: 2px solid var(--brand-500); outline-offset: 1px; }
.segmented input:disabled + label { opacity: .5; cursor: not-allowed; }

/* ---------- 11. Tables ------------------------------------------------ */
.table-card { overflow: hidden; }
.table {
  --bs-table-bg: transparent;
  margin: 0; color: var(--ink-700); font-size: .9rem;
}
.table thead th {
  background: var(--surface-1); color: var(--ink-500);
  font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  border-bottom: 1px solid var(--line); padding: 12px 16px; white-space: nowrap;
}
.table tbody td, .table tbody th {
  padding: 13px 16px; border-bottom: 1px solid var(--line-soft); vertical-align: middle;
}
.table tbody tr:last-child td, .table tbody tr:last-child th { border-bottom: 0; }
.table tbody tr { transition: background .12s ease; }
.table-hover tbody tr:hover { background: var(--surface-1); }
.table .cell-strong { color: var(--ink-900); font-weight: 600; }

/* ---------- 12. Member card ----------------------------------------- */
.member {
  display: flex; gap: 14px; padding: 16px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--shadow-xs);
}
.member__body { min-width: 0; flex: 1; }
.member__name { font-weight: 650; color: var(--ink-900); margin: 0 0 2px; }
.member__row {
  display: flex; align-items: center; gap: 8px;
  font-size: .84rem; color: var(--ink-500); margin-top: 4px; min-width: 0;
}
.member__row .bi { color: var(--ink-400); flex: none; }
.member__row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member__roles { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }

/* ---------- 13. Timeline (CSS only) --------------------------------- */
.timeline {
  display: flex; gap: 0; overflow-x: auto; padding: 8px 4px 18px;
  scrollbar-width: thin;
}
.timeline__step {
  flex: 1 0 150px; position: relative; padding-top: 30px; text-align: center;
}
.timeline__step::before {
  content: ""; position: absolute; top: 13px; left: 0; right: 0; height: 2px;
  background: var(--line);
}
.timeline__step:first-child::before { left: 50%; }
.timeline__step:last-child::before { right: 50%; }
.timeline__dot {
  position: absolute; top: 6px; left: 50%; transform: translateX(-50%);
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--ink-300); z-index: 1;
}
.timeline__step.is-done .timeline__dot { background: var(--brand-700); border-color: var(--brand-700); }
.timeline__step.is-done::before { background: var(--brand-500); }
.timeline__step.is-now .timeline__dot {
  background: var(--accent-500); border-color: var(--accent-500);
  box-shadow: 0 0 0 4px var(--accent-100);
}
.timeline__date { font-weight: 700; color: var(--ink-900); font-size: .9rem; }
.timeline__label { font-size: .8rem; color: var(--ink-500); margin-top: 2px; }
.timeline__step.is-now .timeline__label { color: var(--accent-700); font-weight: 600; }

/* progress bar */
.progress-track {
  height: 8px; background: var(--surface-3); border-radius: var(--r-pill); overflow: hidden;
}
.progress-track__fill {
  height: 100%; background: linear-gradient(90deg, var(--brand-fill), var(--accent-500));
  border-radius: inherit;
}

/* ---------- 14. Notices --------------------------------------------- */
.notice {
  display: flex; gap: 12px; padding: 14px 16px;
  border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--surface-1); font-size: .88rem;
}
.notice .bi { font-size: 1.1rem; flex: none; margin-top: 1px; }
.notice__body { min-width: 0; }
/* A notice body may hold a heading line, prose and a list; they stack. */
.notice__body > strong { display: block; margin-bottom: 4px; }
.notice__body > p { margin: 0 0 6px; }
.notice__body > p:last-child { margin-bottom: 0; }
.notice__body > ul { margin: 0; padding-left: 18px; }
.notice__body > ul > li + li { margin-top: 3px; }
.notice__body > ul:last-child { margin-bottom: 0; }
.notice strong { color: var(--ink-900); }
.notice--warn   { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn-700); }
.notice--warn .bi, .notice--warn strong { color: var(--warn-700); }
.notice--danger { background: var(--danger-bg); border-color: var(--danger-line); color: var(--danger-700); }
.notice--danger .bi, .notice--danger strong { color: var(--danger-700); }
.notice--info   { background: var(--info-bg); border-color: var(--info-line); color: var(--info-700); }
.notice--info .bi, .notice--info strong { color: var(--info-700); }
.notice--ok     { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok-700); }
.notice--ok .bi, .notice--ok strong { color: var(--ok-700); }

/* ---------- 15. Empty state --------------------------------------- */
.empty {
  text-align: center; padding: 48px 24px;
  border: 1px dashed var(--line); border-radius: var(--r-md);
  background: var(--surface-1);
}
.empty .bi { font-size: 2.4rem; color: var(--ink-300); margin-bottom: 10px; }
.empty h4 { margin: 0 0 4px; }
.empty p { margin: 0 auto; color: var(--ink-500); max-width: 42ch; font-size: .9rem; }

/* ---------- 16. Rule / guidance list ------------------------------ */
.rules { counter-reset: rule; display: grid; gap: 12px; }
.rule {
  display: grid; grid-template-columns: 28px 1fr; gap: 14px;
  padding: 16px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--shadow-xs);
}
.rule::before {
  counter-increment: rule; content: counter(rule);
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center; font-weight: 700; font-size: .82rem;
  background: var(--brand-100); color: var(--brand-700);
}
.rule h4 { margin: 0 0 4px; font-size: .95rem; }
.rule p { margin: 0; font-size: .87rem; color: var(--ink-500); }
.rule ul { margin: 6px 0 0; padding-left: 18px; font-size: .87rem; color: var(--ink-500); }
.rule li { margin-bottom: 3px; }

/* ---------- 17. Filters rail ----------------------------------- */
.filters {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 18px; box-shadow: var(--shadow-xs);
}
.filters h3 {
  font-size: .9rem; display: flex; align-items: center; gap: 8px; margin: 0 0 14px;
}
.filters h3 .bi { color: var(--brand-700); }
.filter-group { margin-bottom: 18px; }
.filter-group > label:not(.filter-opt), .filter-label {
  font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-500); display: block; margin-bottom: 8px;
}
.filter-opt {
  display: flex; align-items: center; gap: 9px; padding: 7px 9px;
  border-radius: var(--r-xs); font-size: .87rem; cursor: pointer;
}
.filter-opt:hover { background: var(--surface-2); }
.filter-opt input { accent-color: var(--brand-700); }

/* ---------- 18. Auth / marketing ------------------------------- */
/* The header keeps the same 1320px frame as every other page. The two halves
   still bleed to the viewport edges (the gradient is meant to), but their
   content is inset to that frame: the sign-in column starts on the frame's
   left edge, level with the brand, and the illustration column ends on its
   right edge. --auth-gutter is the inner padding at the fold. */
.auth-page {
  /* 100% here resolves against the panel's own grid area (half the viewport),
     so the inset lands on exactly the same x as the header's content edge:
     half-viewport - half-frame + the frame's own side padding. Using the
     grid area rather than 100vw keeps it correct when a classic scrollbar
     is present, which 100vw would ignore. */
  --auth-inset: max(var(--page-inline), calc(100% - var(--page-max) / 2 + var(--page-inline)));
  --auth-gutter: clamp(24px, 3.5vw, 56px);
}
.auth {
  min-height: calc(100vh - 62px);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* -- left: the sign-in form -- */
.auth__panel {
  padding: clamp(32px, 5vw, 64px) var(--auth-gutter) clamp(32px, 5vw, 64px) var(--auth-inset);
  display: flex; flex-direction: column;
  width: 100%;
}
.auth__panel-main { margin-block: auto; width: 100%; }
.auth__panel-main,
.auth__foot { max-width: 480px; }
.auth__badge {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 11px; margin-bottom: 24px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--surface); color: var(--brand-700);
  font-size: .75rem; font-weight: 700; letter-spacing: .02em;
}
.auth__badge .bi { font-size: .9rem; }
.auth__panel h1 {
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
  line-height: 1.15; letter-spacing: -.035em;
  max-width: 14ch; margin: 0 0 14px;
}
.auth__lede { color: var(--ink-500); margin: 0 0 24px; font-size: .95rem; line-height: 1.6; max-width: 52ch; }

.gsi-btn {
  position: relative;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  width: 100%;
  border: 1px solid var(--ink-300); background: var(--surface);
  border-radius: var(--r-sm); padding: 15px 46px;
  font-weight: 600; font-size: .95rem; color: var(--ink-800);
  box-shadow: var(--shadow-sm);
  transition: box-shadow .15s ease, transform .12s ease, border-color .15s ease;
}
.gsi-btn:hover {
  text-decoration: none; box-shadow: var(--shadow-md);
  border-color: var(--brand-500); transform: translateY(-1px);
}
.gsi-btn:active { transform: translateY(0); }
.gsi-btn svg { flex: none; }
.gsi-btn > .bi { position: absolute; right: 18px; color: var(--brand-700); }

.trust {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: .82rem; color: var(--ink-400); margin: 12px 0 0; line-height: 1.5;
}
.trust .bi { color: var(--ok-600); font-size: .95rem; margin-top: 1px; flex: none; }
.trust b { color: var(--ink-600); font-weight: 600; }

.auth__note {
  display: flex; gap: 10px; margin-top: 24px;
  padding: 12px 14px; border-radius: var(--r-sm);
  background: var(--surface-1); border: 1px solid var(--line);
  font-size: .83rem; color: var(--ink-500);
}
.auth__note .bi { color: var(--ink-400); flex: none; margin-top: 1px; }

.auth__foot {
  margin-top: 24px; padding-top: 24px; border-top: 1px solid var(--line-soft);
  display: flex; gap: 8px 18px; flex-wrap: wrap; align-items: center;
  justify-content: space-between;
  font-size: .8rem; color: var(--ink-400);
}
.auth__foot nav { display: flex; gap: 18px; flex-wrap: wrap; }
.auth__foot a { color: var(--ink-500); }
.auth__foot a:hover { color: var(--brand-700); }

/* -- illustration panel -- */
.auth__aside {
  position: relative; overflow: hidden;
  padding: clamp(32px, 5vw, 64px) var(--auth-inset) clamp(32px, 5vw, 64px) var(--auth-gutter);
  display: flex; flex-direction: column; justify-content: center; gap: 24px;
  color: #fff;
  background: linear-gradient(150deg, #163c5b 0%, #105767 53%, #0c7477 100%);
}
.auth__aside::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(560px 340px at 4% 2%, rgba(116, 219, 218, .25), transparent 72%),
    radial-gradient(620px 400px at 105% 104%, rgba(4, 31, 52, .3), transparent 65%);
}
.auth__aside::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .55;
  background-image: radial-gradient(rgba(255,255,255,.10) 1px, transparent 1.4px);
  background-size: 24px 24px;
  -webkit-mask-image: linear-gradient(175deg, transparent, #000 28%, #000 72%, transparent);
  mask-image: linear-gradient(175deg, transparent, #000 28%, #000 72%, transparent);
}
.auth__aside > * { position: relative; z-index: 1; }
.auth__aside .eyebrow { color: #9be6dd; }
.auth__aside h2 {
  color: #fff; font-size: clamp(1.5rem, 2.6vw, 2rem);
  line-height: 1.18; max-width: 24ch; margin: 8px 0;
}
.auth__aside-heading > p { color: rgba(255,255,255,.72); font-size: .9rem; margin: 0; }
.auth__visual {
  width: 100%; margin: 0;
  padding: 7px; background: #e5f7f5;
  border-radius: var(--r-lg);
  box-shadow: 0 20px 42px rgba(3, 34, 51, .26);
}
.auth__visual img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 1.65; object-fit: cover; object-position: center 58%;
  border-radius: 12px;
}
.auth__steps {
  width: 100%;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px;
}
.auth__steps span {
  display: flex; align-items: center; gap: 8px;
  padding-top: 12px; border-top: 1px solid rgba(255,255,255,.2);
  color: rgba(255,255,255,.8); font-size: .79rem; font-weight: 600;
}
.auth__steps .bi { color: #9be6dd; font-size: 1rem; flex: none; }

@media (max-width: 900px) {
  .auth-page { --auth-inset: var(--page-inline); --auth-gutter: var(--page-inline); }
  .auth { grid-template-columns: minmax(0, 1fr); }
  .auth__aside { order: -1; align-items: center; padding: 16px var(--auth-inset); }
  .auth__aside-heading, .auth__steps { display: none; }
  .auth__visual { max-width: 500px; padding: 5px; }
  .auth__visual img { aspect-ratio: 2.2; object-position: center 64%; }
  .auth__panel { margin: 0 auto; padding: clamp(28px, 8vw, 56px) var(--auth-inset); }
  .auth__panel-main, .auth__foot { max-width: 520px; margin-inline: auto; }
  .auth__foot { flex-direction: column; align-items: flex-start; gap: 12px; }
}
@media (max-width: 520px) {
  .auth__aside { padding-block: 12px; }
  .auth__visual img { max-height: 175px; }
  .auth__panel h1 { font-size: 2rem; }
}
.trust b { overflow-wrap: anywhere; }

/* ---------- 19. Misc helpers --------------------------------- */
.stack   { display: grid; gap: 16px; }
.stack-sm { display: grid; gap: 10px; }
.cluster { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
.grid-auto { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

.divider-text {
  display: flex; align-items: center; gap: 12px; color: var(--ink-400);
  font-size: .8rem; margin: 20px 0;
}
.divider-text::before, .divider-text::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}

/* ---------- 20. Flask integration --------------------------------------- */
.google-signin { width: 100%; min-height: 44px; }
.google-signin .g_id_signin {
  width: 100%; min-height: 44px;
  display: flex; align-items: center;
}
.google-signin .g_id_signin > div,
.google-signin iframe { width: 100% !important; max-width: 400px; }

/* term_form_new keeps .container-fluid on its <main> because
   term_form_validation.js inserts its alert as that element's first child.
   .page controls the width, so only Bootstrap's own gutter is overridden here. */
.portal-page > main.container-fluid,
.portal-page > main.container { --bs-gutter-x: 0; }
.portal-page .modal-content {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink-700);
  box-shadow: var(--shadow-lg);
}
.portal-page .modal-header,
.portal-page .modal-footer { border-color: var(--line-soft); }
.portal-page .dropdown-menu {
  border-color: var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-md);
}
.portal-page .dropdown-item { color: var(--ink-700); }
.portal-page .dropdown-item:hover { color: var(--ink-900); background: var(--surface-2); }
.portal-page .alert-info { color: var(--info-700); background: var(--info-bg); border-color: var(--info-line); }
.portal-page .alert-warning { color: var(--warn-700); background: var(--warn-bg); border-color: var(--warn-line); }
.portal-page .alert-danger { color: var(--danger-700); background: var(--danger-bg); border-color: var(--danger-line); }
.portal-page .alert-success { color: var(--ok-700); background: var(--ok-bg); border-color: var(--ok-line); }
.portal-page .form-check-input:checked {
  background-color: var(--brand-fill) !important; border-color: var(--brand-fill) !important;
}

/* Page-specific bridges for the existing data-heavy templates. The markup keeps
   its server-side behaviour while inheriting the mock's spacing and surfaces. */
.formation-status { margin-bottom: 32px; }

.portal-page .text-dark { color: var(--ink-800) !important; }
.portal-page .text-body-secondary { color: var(--ink-500) !important; }
.portal-page .bg-light,
.portal-page .bg-white { background-color: var(--surface-1) !important; }
.portal-page .list-group-item {
  color: var(--ink-700); background: var(--surface); border-color: var(--line-soft);
}
.portal-page .table-responsive,
.portal-page table.table {
  border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
  background: var(--surface);
}

.profile-page .page > .row { display: block; }
.profile-page .page > .row > .col-7 { width: 100%; }
.profile-page .page > .row > .col-3 { display: none; }
.profile-page #studentProfileForm {
  margin-top: 24px; padding: 12px 28px 24px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-sm);
}
.profile-page #studentProfileForm > .row {
  display: grid; grid-template-columns: 190px minmax(0, var(--field-max)); gap: 16px;
  align-items: start; padding: 16px 0; margin: 0;
  border-bottom: 1px solid var(--line-soft);
}
.profile-page #studentProfileForm > .row > [class*="col-"] { width: 100%; padding: 0; }
.profile-page #studentProfileForm > .row > label { padding-top: 9px; font-weight: 600; color: var(--ink-800); }
.profile-page #studentProfileForm > center { display: flex; justify-content: flex-end; margin-top: 22px; }
.profile-page #studentProfileForm > center .btn {
  width: auto !important; color: #fff !important;
  background: var(--brand-fill) !important; border-color: var(--brand-fill) !important;
  border-radius: var(--r-sm) !important;
}

.join-requests-page .status-card { border-radius: var(--r-md) !important; }
.join-requests-page .status-card .container { padding: 0; }
.join-requests-page .status-card h6 { color: var(--ink-700); line-height: 1.55; }


.upload-page h4 { font-size: 1.05rem; }
.team-dashboard-page [style*="color: #007bff"],
.peer-review-page [style*="color: #007bff"] { color: var(--brand-700) !important; }

@media (max-width: 780px) {
  .profile-page #studentProfileForm > .row { grid-template-columns: 1fr; gap: 6px; }
}

@media (max-width: 760px) {
  .portal-page > .container > .row > [class*="col-"],
  .portal-page > .container-fluid > .row > [class*="col-"] {
    width: 100%;
  }
}

.filetree {
  font-family: var(--font-mono); font-size: .82rem; line-height: 1.9;
  background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 14px 16px; color: var(--ink-600);
}
.filetree .bi { color: var(--accent-600); }
.filetree .zip { color: var(--brand-600); }

.code-pill {
  font-family: var(--font-mono); font-size: .82rem; font-weight: 600;
  background: var(--surface-3); color: var(--ink-800);
  padding: 2px 7px; border-radius: var(--r-xs);
}

.link-arrow { display: inline-flex; align-items: center; gap: 5px; font-weight: 600; }
.link-arrow .bi { transition: transform .14s ease; }
.link-arrow:hover .bi { transform: translateX(3px); }

/* ---------- 20. Bootstrap component polish ------------------------- */
.pagination {
  --bs-pagination-color: var(--ink-600);
  --bs-pagination-bg: var(--surface);
  --bs-pagination-border-color: var(--line);
  --bs-pagination-hover-color: var(--brand-800);
  --bs-pagination-hover-bg: var(--brand-050);
  --bs-pagination-hover-border-color: var(--brand-100);
  --bs-pagination-focus-box-shadow: var(--ring);
  --bs-pagination-active-bg: var(--brand-fill);
  --bs-pagination-active-border-color: var(--brand-fill);
  --bs-pagination-disabled-bg: var(--surface-1);
  --bs-pagination-disabled-border-color: var(--line);
}
.form-check-input { border-color: var(--ink-300); }
.form-check-input:checked { background-color: var(--brand-fill); border-color: var(--brand-fill); }
.form-check-input:focus { border-color: var(--brand-500); box-shadow: var(--ring); }
.form-range { accent-color: var(--brand-700); }
.modal-content {
  border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow-lg); background: var(--surface);
}
.modal-header, .modal-footer { border-color: var(--line-soft); }
.modal-header .modal-title { font-weight: 650; }
.dropdown-menu {
  --bs-dropdown-border-color: var(--line);
  --bs-dropdown-border-radius: var(--r-sm);
  --bs-dropdown-link-active-bg: var(--brand-050);
  --bs-dropdown-link-active-color: var(--brand-800);
  box-shadow: var(--shadow-md); padding: 6px;
}
.dropdown-item { border-radius: var(--r-xs); font-size: .9rem; padding: 8px 10px; }
.input-group-text { background: var(--surface-2); border-color: var(--line); color: var(--ink-500); font-size: .88rem; }
.btn-group .btn { transform: none; }

/* mock-only: page frame in the gallery */
.mock-note {
  background: var(--accent-100); color: var(--accent-700);
  border: 1px solid var(--accent-100); border-radius: var(--r-sm);
  padding: 10px 14px; font-size: .82rem; font-weight: 500;
  display: flex; gap: 8px; align-items: center;
}

/* ---------- 21. Portal-wide refinement ---------------------------------- */
.portal-page { min-height: 100vh; overflow-x: hidden; }
.portal-page .page { width: 100%; }
.portal-page .page > .row[style*="margin-bottom"] { margin-bottom: 56px !important; }
.portal-page .page-head h1 { line-height: 1.15; }
.portal-page .page-head strong { color: var(--ink-800); }
.portal-page .card-header h1,
.portal-page .card-header h2,
.portal-page .card-header h3,
.portal-page .card-header h4,
.portal-page .card-header h5,
.portal-page .card-header h6 { margin-bottom: 0; }
.portal-page .alert {
  border: 1px solid var(--line); border-radius: var(--r-sm); box-shadow: none;
}
.portal-page .badge { border-radius: var(--r-pill); font-weight: 650; }
.portal-page .text-primary { color: var(--brand-700) !important; }
.portal-page .text-success { color: var(--ok-700) !important; }
.portal-page .text-warning { color: var(--warn-700) !important; }
.portal-page .btn-outline-dark {
  --bs-btn-color: var(--ink-700); --bs-btn-border-color: var(--line);
  --bs-btn-hover-color: var(--ink-900); --bs-btn-hover-bg: var(--surface-2);
  --bs-btn-hover-border-color: var(--ink-300); background: var(--surface);
}
.portal-page code { color: var(--brand-700); background: var(--brand-050); padding: 2px 5px; border-radius: 4px; }
.portal-page .form-control:disabled,
.portal-page .form-control[readonly] { color: var(--ink-500); background: var(--surface-1); }
.portal-page .accordion-item { color: var(--ink-700); background: var(--surface); border-color: var(--line); }
.portal-page .accordion-button { color: var(--ink-800); background: var(--surface); box-shadow: none; }
.portal-page .accordion-button:not(.collapsed) { color: var(--brand-700); background: var(--brand-050); }

/* Instructor draft/final team pages. */
.instructor-teams-page .team-card { overflow: hidden; }
.instructor-teams-page .empty { padding-block: 72px; }

/* Student directory rail and data area. */
/* The two filter panels occupy the same rail slot; the view toggle shows one. */
.directory-page .students,
.directory-page .Teams { margin-bottom: 8px; }
.directory-page .Teams { margin-top: 26px; }

/* Forms and profile screens. */
.profile-page .profile-actions { display: flex; justify-content: flex-end; margin-top: 22px; }
.create-team-page .card--pad-lg { overflow: visible; }
.create-team-page .dropdown-menu { max-height: 240px !important; }
.term-form-page form > .card { margin-bottom: 20px; }
.term-form-page .form-control-lg,
.term-form-page .form-select-lg { font-size: .95rem; padding: .65rem .85rem; }

.course-admin-page .list-group { border-radius: var(--r-sm); overflow: hidden; }
.course-admin-page .page > .d-flex:first-child { gap: 16px; flex-wrap: wrap; }
.instructor-home-page .card.h-100 .card-body { padding: 22px; }
.term-dashboard-page .page > .row:first-child { align-items: start; }
.term-dashboard-page .card .p-3.border {
  height: 100%; padding: 18px !important; border-color: var(--line) !important;
  border-radius: var(--r-sm) !important; background: var(--surface-1);
}
.term-dashboard-page .card .p-3.border svg { width: 32px; height: 32px; }
.upload-page .card { margin-top: 12px; }

/* Student work surfaces. */
.join-requests-page .status-card { margin-bottom: 14px !important; box-shadow: var(--shadow-xs) !important; }
.join-requests-page .status-card:hover { transform: none !important; box-shadow: var(--shadow-sm) !important; }
.join-requests-page .request-title { margin: 0 0 6px; font-size: 1.05rem; }
.join-requests-page .status-alert { background: var(--surface-1) !important; border: 1px solid var(--line) !important; }
.manage-team-page .page-head { margin-bottom: 18px; }
.manage-team-page table { background: var(--surface); }
.peer-review-page .question-card .card-body textarea.form-control { max-width: var(--field-max); }
.peer-review-page .card-header[style],
.peer-review-page .card-body[style] { background: var(--surface) !important; border-color: var(--line-soft) !important; }
.peer-review-page [style*="color: #555"],
.peer-review-page [style*="color: #666"],
.peer-review-page [style*="color: #333"],
.peer-review-page [style*="color: #444"] { color: var(--ink-600) !important; }
.team-dashboard-page .card-header.bg-primary,
.team-dashboard-page .card-header.bg-dark,
.team-dashboard-page .card-header.bg-warning {
  color: var(--ink-900) !important; background: var(--surface-1) !important;
  border-color: var(--line-soft) !important;
}


@media (max-width: 780px) {
  .profile-page #studentProfileForm { padding-inline: 18px; }
  .portal-page .page-head { align-items: flex-start; }
  .portal-page .page-head__actions,
  .portal-page .page-head > .cluster { width: 100%; }
  .portal-page .page-head__actions .btn,
  .portal-page .page-head > .cluster .btn { flex: 1 1 auto; }
  .portal-page .table-responsive,
  .portal-page .team-card,
  .portal-page .card { max-width: 100%; }
  .portal-page .table-responsive { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .portal-page table.table {
    display: block; width: 100%; min-width: 0; overflow-x: auto;
    white-space: nowrap; -webkit-overflow-scrolling: touch;
  }
  .portal-page .table-responsive > table.table { display: table; min-width: 640px; }
  .portal-page .empty { padding: 42px 20px; }
}


/* ---------- 21. Instructor home ----------------------------------------- */
.instructor-home-page .card--pad-lg .form-select { width: auto; min-width: 240px; }
@media (max-width: 520px) {
  .instructor-home-page .card--pad-lg .form-select { width: 100%; min-width: 0; }
}

/* ---------- 22. Key/value tables and milestone chips -------------------- */
/* Label/value rows inside a card: the label reads as quiet metadata, the value
   sits flush right. Used by the term dashboard's information cards. */
.kv-table { --bs-table-bg: transparent; margin-bottom: 0; }
.kv-table > tbody > tr > th {
  color: var(--ink-500); font-weight: 600; font-size: .88rem;
  background: transparent; white-space: nowrap;
}
.kv-table > tbody > tr > td { color: var(--ink-800); font-size: .9rem; background: transparent; }
.kv-table > tbody > tr:last-child > th,
.kv-table > tbody > tr:last-child > td { border-bottom: 0; }

.milestone-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border: 1px solid var(--line);
  border-left: 3px solid var(--ok-600);
  border-radius: var(--r-sm); background: var(--surface-1);
}
.milestone-item h4 { margin: 0 0 2px; font-size: .92rem; font-weight: 600; }
.milestone-item p { margin: 0; font-size: .84rem; color: var(--ink-500); }

/* ---------- 23. Peer review ---------------------------------------------- */
/* .mcq-option and .selected are the hooks peer_review.html's script toggles,
   so the names stay; only the palette moves onto the shared tokens. */
.mcq-option {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 13px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface);
  transition: background .16s ease, border-color .16s ease;
}
.mcq-option:hover { background: var(--surface-2); border-color: var(--brand-100); }
.mcq-option.selected {
  background: var(--brand-050); border-color: var(--brand-500);
  box-shadow: var(--ring); font-weight: 550;
}
.mcq-option input { accent-color: var(--brand-700); flex: none; margin: 0; }
.mcq-option__text { flex: 1; color: var(--ink-800); }
.mcq-option__marks { font-size: .8rem; flex: none; }

.question-card__head { align-items: flex-start; }
.question-card__prompt { flex: 1; }
.question-card__prompt p {
  margin: 4px 0 0; color: var(--ink-900); font-weight: 550;
  font-size: .97rem; line-height: 1.5;
}
.question-card__head .tag { flex: none; }

.form-hint { font-size: .82rem; color: var(--ink-500); margin: 0 0 12px; font-weight: 500; }

/* The submit bar trails the question list and stays reachable while scrolling. */
.review-submit { position: sticky; bottom: 16px; box-shadow: var(--shadow-lg); }

.score-readout {
  font-size: 3rem; font-weight: 700; line-height: 1.1;
  color: var(--brand-700); margin: 24px 0 4px;
}
.terms-lead { color: var(--ink-800); font-weight: 500; margin-bottom: 14px; }
.terms-list { color: var(--ink-700); padding-left: 20px; margin-bottom: 18px; line-height: 1.6; }
.terms-list li + li { margin-top: 4px; }

/* A stat value that has to hold a date rather than a number. */
.stat__value--sm { font-size: 1.05rem; }

/* ---------- 24. Icon size helpers ---------------------------------------- */
/* Bootstrap Icons ships no size or spin utilities, unlike Font Awesome, so the
   few places that relied on fa-2x / fa-lg / fa-spin use these instead. */
.icon-lg { font-size: 1.35rem; }
.icon-2x { font-size: 2rem; }
.icon-3x { font-size: 3rem; }
.icon-spin { display: inline-block; animation: icon-spin 1s linear infinite; }
@keyframes icon-spin { to { transform: rotate(360deg); } }

/* ---------- 25. Resources & FAQ ------------------------------------------ */
/* FAQ topics beside a "Jump to" list; each question is its own collapsed accordion. */
.faq-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 28px; align-items: start; }
.faq-layout--single { grid-template-columns: minmax(0, 1fr); }
.faq-nav { display: flex; flex-direction: column; gap: 10px; position: sticky; top: 24px; }
.faq-nav a { color: var(--ink-500); font-weight: 550; font-size: .9rem; }
.faq-nav a:hover { color: var(--brand-700); text-decoration: none; }
.faq-groups { display: grid; gap: 30px; min-width: 0; }
/* Clear the phone top bar when a "Jump to" link scrolls here. */
.faq-group { scroll-margin-top: 80px; }
.faq-group .section__label h3 { font-size: 1.05rem; }
.faq-group .accordion {
  --bs-accordion-border-color: var(--line);
  --bs-accordion-border-radius: var(--r-sm);
  --bs-accordion-inner-border-radius: var(--r-sm);
  --bs-accordion-btn-focus-box-shadow: var(--ring);
  display: grid; gap: 8px;
}
/* Separated items each keep all four borders; Bootstrap drops the top one when they touch. */
.faq-group .accordion-item {
  border: 1px solid var(--line); border-radius: var(--r-sm) !important; overflow: hidden;
}
.faq-group .accordion-button { font-weight: 600; font-size: .95rem; }
/* Bootstrap draws the chevron as a fixed-colour image (blue in dark mode). Used as a
   mask instead, it takes the palette: quiet while closed, brand when open. */
.faq-group .accordion-button::after {
  background: var(--ink-400);
  -webkit-mask: var(--bs-accordion-btn-icon) center / contain no-repeat;
  mask: var(--bs-accordion-btn-icon) center / contain no-repeat;
}
.faq-group .accordion-button:not(.collapsed)::after { background: currentColor; }
.faq-group .accordion-body { color: var(--ink-600); font-size: .9rem; }
@media (max-width: 760px) {
  .faq-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .faq-nav { position: static; flex-direction: row; flex-wrap: wrap; gap: 8px 16px; }
  .faq-nav .eyebrow { flex-basis: 100%; }
}

/* Two links sitting inside a tile rather than the tile itself being the link. */
.resource-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

.pagination .page-link { color: var(--brand-700); border-color: var(--line); background: var(--surface); }
.pagination .page-link:hover { background: var(--brand-050); border-color: var(--brand-100); }
.pagination .active > .page-link { color: #fff; background: var(--brand-fill); border-color: var(--brand-fill); }
.pagination .disabled > .page-link { color: var(--ink-300); background: var(--surface-1); }

/* ---------- 26. Instructor team management ------------------------------- */
/* A sticky filter/stats rail beside the team list, replacing the col-3 / col-7 /
   col-2 grid the instructor pages used to fake with empty columns. */
.team-layout {
  display: grid;
  grid-template-columns: minmax(240px, 260px) minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
.team-layout__rail {
  display: flex; flex-direction: column; gap: 16px;
  position: sticky; top: 80px;
}
.team-layout__main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
@media (max-width: 1000px) {
  .team-layout { grid-template-columns: 1fr; }
  .team-layout__rail { position: static; }
}

.filter-group--foot { margin-bottom: 0; }
.filter-note {
  font-size: .8rem; color: var(--ink-400); margin: 8px 0 10px;
  line-height: 1.5;
}
.filter-note .bi { margin-right: 5px; vertical-align: -1px; }

/* Search input with a leading icon and a JS-toggled clear button. */
.search-field { position: relative; }
.search-field > .bi {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  color: var(--ink-400); font-size: .9rem; pointer-events: none;
}
.search-field .form-control { padding-left: 32px; padding-right: 32px; }
.search-field__clear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  border: 0; background: none; color: var(--ink-400);
  padding: 4px; line-height: 1; border-radius: var(--r-xs); cursor: pointer;
}
.search-field__clear:hover { color: var(--ink-700); background: var(--surface-2); }

/* Label/value row used in the statistics panel. */
.kv-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: .87rem; color: var(--ink-600);
}
.kv-row > span:first-child { display: flex; align-items: center; gap: 8px; }
.kv-row .bi { color: var(--ink-400); }

.team-card__head { justify-content: space-between; flex-wrap: wrap; gap: 12px; }
.team-card__name { font-size: 1rem; }
.team-card__size { font-size: .85rem; }

.form-range { flex: 1; }
.form-range::-webkit-slider-thumb { background: var(--brand-fill); }
.form-range::-webkit-slider-thumb:active { background: var(--brand-600); }
.form-range::-moz-range-thumb { background: var(--brand-fill); border: 0; }
.form-range::-webkit-slider-runnable-track { background: var(--surface-3); }
.form-range::-moz-range-track { background: var(--surface-3); }

/* Row of link/action buttons between a team card's header and its member table. */
.card-body--toolbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px;
  padding-block: 14px;
  border-bottom: 1px solid var(--line-soft);
}

/* Identity strip above the profile form. */
.profile-identity__name { font-size: 1.05rem; margin: 0 0 2px; }

/* One answer option inside the question-set editor (rows are added by JS):
   a correct-answer toggle, the option text and a remove button. */
.option-row + .option-row { margin-top: 8px; }

/* Requirement bullet lists inside a card. */
.spec-list { margin: 0; padding-left: 20px; color: var(--ink-600); font-size: .9rem; line-height: 1.7; }
.spec-list strong { color: var(--ink-900); }
.portal-page .table-responsive.csv-preview-scroll {
  max-height: 300px;
  overflow: auto;
  scrollbar-gutter: stable;
}

.course-card__name { font-size: 1rem; }
.course-card__id { font-size: .85rem; margin-bottom: 12px; }

/* ---------- 27. Join requests -------------------------------------------- */
/* The countdown script swaps these classes as a request nears its timeout. */
.timer-expired  { color: var(--danger-600); font-weight: 700; }
.timer-warning  { color: var(--warn-700); font-weight: 700; }
.timer-critical { color: var(--danger-600); font-weight: 700; animation: blink 1s linear infinite; }

.request-row { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.request-row__main { flex: 1 1 320px; min-width: 0; }
.request-row__side { flex: 0 0 170px; text-align: center; }
@media (max-width: 560px) { .request-row__side { flex: 1 1 100%; } }

.request-title { font-size: 1.02rem; margin: 6px 0 8px; }
.request-timer { font-size: .82rem; color: var(--ink-500); font-weight: 500; margin-bottom: 10px; }
.request-timer span { font-weight: 700; }

/* Terminal-state panel shown in place of the accept/decline buttons. */
.status-alert {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; font-size: .85rem; padding: 12px; margin: 0;
}
.status-icon { font-size: 1.3em; }
.btn:disabled { opacity: .6; cursor: not-allowed; }

/* ---------- 28. Team dashboard: repository setup ----------------------- */
/* "How to set up your repo" guidance panel. */
.repo-steps-panel {
  background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: 1rem 1rem .75rem;
}
.repo-steps-panel .repo-steps-title {
  display: flex; align-items: center; gap: .5rem;
  font-weight: 700; color: var(--ink-900); margin-bottom: .75rem;
}
.repo-steps-list { margin: 0; padding-left: 1.1rem; color: var(--ink-600); }
.repo-steps-list li { margin-bottom: .75rem; line-height: 1.5; }
.repo-steps-list li:last-child { margin-bottom: 0; }
.repo-steps-highlight { font-weight: 700; color: var(--brand-700); }

/* ---------- 29. Question set editor ------------------------------------- */
.option-list { margin: 16px 0; }
.option-display {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 12px; margin-bottom: 8px;
  background: var(--surface-1); border: 1px solid var(--line); border-radius: var(--r-sm);
}
.option-display:last-child { margin-bottom: 0; }
.option-display__rank {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  background: var(--brand-fill); color: #fff;
  display: grid; place-items: center; font-weight: 700; font-size: .8rem;
}
.option-display__text { flex: 1; color: var(--ink-800); font-size: .93rem; }

/* Small square badge holding an icon, used in dashboard detail rows. */
.icon-badge {
  width: 44px; height: 44px; flex: none; border-radius: var(--r-sm);
  display: grid; place-items: center;
  background: var(--brand-050); color: var(--brand-700); font-size: 1.15rem;
}
.icon-fixed { width: 16px; display: inline-block; }

/* Monospaced block for repo URLs and similar copyable values. */
.code-block {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .87rem; padding: 12px 14px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--ink-800);
  overflow-wrap: anywhere;
}

/* Divider that spans a card body's full width, pinned to its bottom edge. */
.card-inset-foot {
  border-top: 1px solid var(--line-soft);
  margin: 24px -20px -20px; padding: 16px 20px 20px;
}

/* Small utilities that replaced repeated inline styles. */
.scroll-200 { max-height: 200px; overflow-y: auto; }
.scroll-500 { max-height: 500px; overflow-y: auto; }
.col-divider { border-left: 1px solid var(--line); padding-left: 20px; }
@media (max-width: 768px) { .col-divider { border-left: 0; padding-left: 0; } }

.icon-badge--warn { background: var(--warn-bg); color: var(--warn-700); }
.icon-badge--ok { background: var(--ok-bg); color: var(--ok-700); }

.icon-badge--muted { background: var(--surface-3); color: var(--ink-500); }
/* A card header that should read as the page's live/priority item. */
.card-header--accent {
  justify-content: space-between;
  background: var(--warn-bg); border-bottom-color: var(--warn-line); color: var(--warn-700);
}
.card-header--accent .bi { color: var(--warn-700); }
/* Quiet inset panel inside a modal or card body. */
.panel-inset {
  background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 14px 16px;
}

/* The closing question in a confirmation modal: the prompt itself, so it sits
   outside the warning panel rather than being buried in it. */
.modal-question {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 16px 0 0; color: var(--ink-700); font-size: .92rem;
}
.modal-question .bi { color: var(--ink-400); flex: none; margin-top: 2px; }

/* ---------- 24. Submission uploads -------------------------------------- */
/* The drop zone sits inside an artifact card rather than replacing it, so the
   card keeps showing what was already uploaded while a new revision goes up. */
.submissions-page .artifact-card { box-shadow: none; }
.submissions-page .artifact-card + .artifact-card { margin-top: 0; }

.upload {
  border: 1px dashed var(--line);
  border-radius: var(--r-sm);
  padding: 14px;
  background: var(--surface-1);
  transition: border-color .14s ease, background .14s ease;
}
.upload.is-dragover {
  border-color: var(--brand-500);
  background: var(--brand-050);
}
.upload__status {
  margin: 8px 0 0;
  font-size: .85rem;
  color: var(--ink-500);
  min-height: 1.2em;
}
.upload__status--busy  { color: var(--brand-700); }
.upload__status--ok    { color: var(--ok-700); }
.upload__status--error { color: var(--danger-700); }

/* Each member's status on a per-member artifact, one row per member. */
.member-status { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--r-sm); }
.member-status__row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; padding: 9px 14px; }
.member-status__row + .member-status__row { border-top: 1px solid var(--line-soft); }
.member-status__name { flex: 1 1 180px; min-width: 0; font-weight: 550; color: var(--ink-800); font-size: .9rem; }
.member-status__links { display: flex; gap: 4px; }

.submissions-config-page .table code,
.submissions-page code { font-size: .82rem; }

/* ---------- 25. Structured form submissions ----------------------------- */
/* A question and its answer, stacked. Unlike .field-row this does not put the
   label in a side column: form prompts are full sentences with guidance, so
   they need the width. */
.field-block + .field-block {
  margin-top: 4px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
}
.field-block .form-control,
.field-block .form-select { max-width: var(--field-max); }
.field-block textarea.form-control { max-width: 100%; }

/* Read-only answers on the instructor's response view. */
.form-response-page .kv-list { margin: 0; }
.form-response-page .field-row dt { color: var(--ink-500); font-weight: 600; }
.form-response-page .field-row dd { color: var(--ink-800); white-space: pre-wrap; }

/* ---------- 26. Filter bars --------------------------------------------- */
/* A horizontal bar above wide tables, as opposed to the .filters sidebar rail
   the directory pages use: a team-by-artifact grid needs its full width. */
.filter-bar { box-shadow: var(--shadow-xs); }
.filter-bar .card-body { padding: 16px 20px; }
.filter-bar .filter-label { margin-bottom: 6px; }
/* Rows and cells are hidden with [hidden]; tables override display, so restore it. */
tr[hidden], td[hidden], th[hidden] { display: none !important; }

/* ---------- 27. Side shell: full width, with a sidebar for everything --------
   Staff and student pages have no top header: the sidebar (templates/_side_shell.html)
   holds the brand, the navigation and the account controls. From lg up it is pinned to
   the left edge at full height; below lg it is Bootstrap's off-canvas menu, opened from
   .side-topbar. */
.side-shell {
  display: grid; grid-template-columns: 264px minmax(0, 1fr);
  min-height: 100vh; align-items: start;
}
/* The page fills the width beside the sidebar instead of centring in --page-max. */
.side-shell__main.page {
  max-width: none; margin: 0; min-width: 0;
  padding: clamp(20px, 3vw, 36px) clamp(16px, 3vw, 40px) 80px;
}
.side-topbar { display: none; }

.side-nav__inner {
  display: flex; flex-direction: column; gap: 20px;
  width: 100%; height: 100%; padding: 18px 14px 16px;
  background: var(--surface); border-right: 1px solid var(--line);
}
.side-nav__brand { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 2px 6px; }
.side-nav__body {
  display: flex; flex-direction: column; gap: 14px;
  flex: 1 1 auto; min-height: 0; overflow-y: auto; margin: 0 -4px; padding: 0 4px;
}
.side-nav__section { display: flex; flex-direction: column; gap: 2px; }
.side-nav__heading {
  padding: 0 10px 4px; font-size: .7rem; font-weight: 650; color: var(--ink-400);
  text-transform: uppercase; letter-spacing: .06em;
}
.side-nav__link {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px;
  border-radius: var(--r-xs); color: var(--ink-600); font-size: .9rem; font-weight: 550;
  transition: background .14s ease, color .14s ease;
}
.side-nav__link:hover { background: var(--surface-3); color: var(--ink-900); text-decoration: none; }
.side-nav__link.is-active { background: var(--brand-050); color: var(--brand-700); }
.side-nav__link .bi { font-size: 1rem; opacity: .85; }
.side-nav__run {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-1);
}
.side-nav__run strong { color: var(--ink-900); font-size: .95rem; }
.side-nav__run a { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; font-size: .82rem; font-weight: 550; }
.side-nav__account {
  display: flex; flex-direction: column; gap: 4px;
  padding-top: 14px; border-top: 1px solid var(--line);
}
.side-nav__user { display: flex; align-items: center; gap: 10px; padding: 0 4px 4px 10px; }
.side-nav__who { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.25; }
.side-nav__who strong, .side-nav__who small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-nav__who strong { font-size: .88rem; color: var(--ink-900); }
.side-nav__who small { font-size: .75rem; color: var(--ink-400); }
.side-nav__signout:hover { color: var(--danger-700); }

@media (min-width: 992px) {
  .side-nav { position: sticky; top: 0; height: 100vh; }
  /* No header above staff pages, so sticky rails need no header-sized offset. */
  .side-shell .team-layout__rail { top: 24px; }
}
@media (max-width: 991.98px) {
  .side-shell { grid-template-columns: minmax(0, 1fr); }
  .side-topbar {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    position: sticky; top: 0; z-index: 1030; height: 58px; padding: 0 var(--page-inline);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: saturate(1.4) blur(10px); border-bottom: 1px solid var(--line);
  }
  .side-topbar .nav-toggle { display: grid; place-items: center; }
  /* Pages no longer repeat the course and run above their heading, so the top bar
     keeps showing them on phones (the header rule hid .brand small below 860px). */
  .side-topbar .brand small { display: block; }
  .side-nav.offcanvas-lg { --bs-offcanvas-width: min(300px, 86vw); --bs-offcanvas-bg: var(--surface); }
  .side-nav__inner { border-right: 0; }
  /* The header hid the run under the brand on phones; the menu has room for it, and a
     student in both SE and SPG needs to see which one they are in. */
  .side-nav__brand .brand small { display: block; }
  .side-shell__main.page { padding-inline: var(--page-inline); }
}

/* Beside the sidebar the page is ~330px narrower than the window, so layouts sized
   for the window collapse earlier here. */
@media (max-width: 1180px) {
  .side-shell .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 1240px) {
  .side-shell .team-layout { grid-template-columns: 1fr; }
  .side-shell .team-layout__rail { position: static; }
}

/* A stat that opens the list it counts. */
a.stat {
  display: block; color: inherit;
  transition: box-shadow .14s ease, transform .14s ease;
}
a.stat:hover { text-decoration: none; box-shadow: var(--shadow-sm); transform: translateY(-1px); }
a.stat:focus-visible { box-shadow: var(--ring); }
.stat__link {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 10px;
  font-size: .8rem; font-weight: 600; color: var(--brand-700);
}

/* Filters and statistics rails fold behind a button once they no longer fit beside
   the list, so the teams are not a full screen of scrolling away. */
.rail-toggle { display: none; }
.team-layout__rail.collapse:not(.show) { display: flex; }
@media (max-width: 1000px) {
  .rail-toggle { display: flex; }
  .team-layout__rail.collapse:not(.show) { display: none; }
}
@media (max-width: 1240px) {
  .side-shell .rail-toggle { display: flex; }
  .side-shell .team-layout__rail.collapse:not(.show) { display: none; }
}

/* On narrow screens a .table-stack row becomes a small card: first cell on its own
   line, the rest wrapping beneath it, the last (usually actions) pushed right. The
   breakpoint matches section 20's, below which other tables scroll sideways instead. */
@media (max-width: 780px) {
  /* Outranks section 20's scrolling-table rule (display: table, min-width: 640px). */
  .portal-page .table-responsive > table.table.table-stack,
  .table-stack, .table-stack tbody {
    display: block; width: 100%; min-width: 0; overflow: visible; white-space: normal;
  }
  .table-stack thead { display: none; }
  .table-stack tbody tr {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
    padding: 12px 14px; border-bottom: 1px solid var(--line-soft);
  }
  .table-stack tbody tr:last-child { border-bottom: 0; }
  .table-stack tbody td, .table-stack tbody th {
    display: block; min-width: 0; padding: 0; border: 0; overflow-wrap: anywhere;
  }
  .table-stack tbody td:first-child, .table-stack tbody th:first-child,
  .table-stack tbody .stack-full { flex: 1 1 100%; }
  .table-stack tbody td:last-child:not(:first-child) { margin-left: auto; }
  .table-responsive:has(> .table-stack) { overflow-x: visible; }
}

/* Student list: status tabs with counts, and a search box that shares the row. */
.segmented { max-width: 100%; overflow-x: auto; }
.segmented a { white-space: nowrap; }
.segmented .count { font-size: .75rem; font-weight: 650; color: var(--ink-400); }
.segmented a.is-active .count { color: var(--brand-700); }
.student-search { width: min(320px, 100%); }

/* Three short numbers stay on one row until they genuinely cannot fit; on phones two
   to a row, so the counts do not push the page's content off the first screen. */
.grid-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; }
@media (max-width: 640px) {
  .grid-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .grid-stats .stat { padding: 12px 14px; }
  .grid-stats .stat__label { font-size: .7rem; }
  .grid-stats .stat__value { font-size: 1.5rem; }
}

/* Label/value tables wrap rather than scroll: section 20 makes every table on a narrow
   screen a no-wrap scroller, which pushed the dashboard's link buttons off the card. */
@media (max-width: 780px) {
  .portal-page table.table.kv-table { display: table; white-space: normal; overflow: visible; }
  .kv-table .cluster { flex-wrap: wrap; }
}

/* List filters (staff and student): status tabs, search and selects on one row above the list
   (templates/_filters.html, filtered by static/js/table_filters.js). */
.filter-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-bottom: 8px; }
.filter-toolbar__search { flex: 1 1 240px; max-width: 360px; }
/* Sized to its text, so it needs back the room for the arrow that section 10's
   symmetric .form-select padding takes away. */
.filter-toolbar__select { width: auto; flex: 0 0 auto; padding-right: 2.25rem; }
.filter-toolbar__note { margin: 0 0 14px; font-size: .85rem; color: var(--ink-400); }
/* A control that belongs at the far end of the row, such as a view switch. On phones,
   where the row wraps, it leads instead: it decides which list the filters apply to. */
.filter-toolbar__end { margin-left: auto; }
.team-list { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 640px) {
  .filter-toolbar__search { max-width: none; }
  .filter-toolbar__select { flex: 1 1 auto; }
  .filter-toolbar__end { order: -1; margin-left: 0; }
}

/* A milestone past its deadline folds behind its header, collapsed by default
   (student submissions and submissions setup). The header is the toggle button. */
button.milestone-fold {
  width: 100%; text-align: left; border: 0; border-bottom: 1px solid var(--line-soft);
  background: transparent; color: var(--ink-900); font: inherit; font-weight: 600; cursor: pointer;
}
button.milestone-fold:hover { background: var(--surface-1); }
button.milestone-fold:focus-visible { box-shadow: var(--ring); }
button.milestone-fold.collapsed { border-bottom-color: transparent; border-radius: var(--bs-card-inner-border-radius); }
.milestone-fold__chevron { margin-left: auto; transition: transform .2s ease; }
.milestone-fold:not(.collapsed) .milestone-fold__chevron { transform: rotate(180deg); }

/* A classmate who is already in a team is listed but cannot be picked; say why. */
.student-option.disabled { opacity: .6; cursor: not-allowed; }
.student-option__note { margin-left: 6px; font-size: .8rem; color: var(--ink-400); }

/* ---------- 30. Team dashboard ------------------------------------------ */
/* Main column beside a rail of links and the next deadline. align-items: start keeps
   the rail at its own height instead of stretching its cards to the main column's. */
.dash-layout {
  display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 24px; align-items: start;
}
.dash-layout__main { display: grid; gap: 24px; min-width: 0; }
.dash-layout__rail { display: grid; gap: 20px; min-width: 0; }
/* Beside the sidebar the rail no longer fits at laptop widths. It then leads, two
   cards abreast, so the next deadline is not a screen of scrolling away. */
@media (max-width: 1180px) {
  .side-shell .dash-layout { grid-template-columns: minmax(0, 1fr); }
  .side-shell .dash-layout__rail {
    order: -1; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start;
  }
}
@media (max-width: 900px) {
  .dash-layout { grid-template-columns: minmax(0, 1fr); }
  .dash-layout__rail {
    order: -1; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start;
  }
}

/* Something that needs the student now, such as a live peer review. */
.attention-card { border-color: var(--warn-line); }
.attention-card .card-body {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px;
}
.attention-card__title { font-size: 1.1rem; margin: 0 0 2px; }

/* Milestones only, so narrower steps than the default keep five or six in view. */
.dash-timeline .timeline__step { flex-basis: 104px; }

/* One deliverable: its name wraps, its status stays whole at the row's end. */
.owed-row { display: flex; align-items: baseline; gap: 8px; }
.owed-row > .bi { flex: none; }
.owed-row__label { min-width: 0; overflow-wrap: anywhere; }
.filetree__status { margin-left: auto; padding-left: 8px; white-space: nowrap; }
.filetree__status--ok { color: var(--ok-700); }
.filetree__status--due { color: var(--warn-700); }
.dash-rules { margin: 0; padding-left: 16px; font-size: .86rem; color: var(--ink-600); }
.dash-rules li + li { margin-top: 4px; }

.card-header .count {
  font-size: .8rem; font-weight: 600; color: var(--ink-500);
  background: var(--surface-3); padding: 1px 8px; border-radius: var(--r-pill);
}
.chip--xs { font-size: .68rem; padding: 3px 7px; }
.stat__value--md { font-size: 1.4rem; }

/* A link laid out as one row: icon, two lines of text, and where it goes. */
.tile--row { display: flex; align-items: center; gap: 12px; padding: 14px; }
.tile--row .tile__icon { margin: 0; width: 34px; height: 34px; font-size: 1rem; flex: none; }
.tile__icon--plain { background: var(--surface-3); color: var(--ink-700); }
.tile__text { display: flex; flex-direction: column; min-width: 0; }
.tile__text strong { color: var(--ink-900); font-size: .9rem; font-weight: 600; }
.tile__text small { color: var(--ink-500); font-size: .78rem; overflow-wrap: anywhere; }
.tile__go { margin-left: auto; flex: none; color: var(--ink-400); }
/* A link the instructor has not set up yet: same footprint, visibly inert. */
.tile--muted { background: var(--surface-1); border-style: dashed; box-shadow: none; }
.tile--muted .tile__icon { background: var(--surface-3); color: var(--ink-400); }

/* A per-member artifact in the instructor grid: the team's count, opening onto each
   member's work. A native <details>, so it needs no script. */
.member-cell summary { list-style: none; cursor: pointer; }
.member-cell summary::-webkit-details-marker { display: none; }
.member-cell__list {
  list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px;
  min-width: 200px; font-size: .82rem; white-space: normal;
}
.member-cell__list li { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
