/* =====================================================================
   Cover The Gaps — CTG UI
   The shared design system behind every logged-in page.

   WHY THIS FILE EXISTS
   --------------------
   Four pages were redesigned one at a time (student dashboard, teacher
   dashboard, homework list, homework detail) and each one carried its own
   private copy of the same tokens under its own page scope: .dash-page,
   .hw-page, .hd-page. Same indigo, same Plus Jakarta Sans, same 16px
   radius, four times over. Every page redesigned after them would have
   been a fifth copy.

   This file is that token set, extracted once, plus the primitives the
   four pages had in common. The four originals are left alone — they
   still ship their own CSS and are the reference for what "current"
   looks like.

   HOW TO USE IT
   -------------
   1. In the page template:

        {% block extra_head %}{% include "includes/ctg_ui.html" %}{% endblock %}

      (a <link> must not go in {% block style %} — base.html wraps that
      block in a <style> element and the link would load as broken CSS.)

   2. Wrap the page body in the scope class and the width container:

        <div class="ctg">
          <div class="ctg-wrap"> … </div>
        </div>

   Everything below is scoped under .ctg, so a page that does not opt in
   is untouched. Inside .ctg the Bootstrap primitives a page already uses
   — .card, .btn-primary, .table, .badge, .form-control, .nav-tabs,
   .modal, .alert, .pagination — are restyled in this language. An old
   page therefore modernises by adding the wrapper, and gets refined by
   moving its markup onto the CTG primitives (.stat, .sec, .badge-soft,
   .ctg-empty …) where they say more than the Bootstrap equivalent.

   THE ONE RULE
   ------------
   Do not add page-specific rules here. Anything only one page needs
   belongs in that page's own {% block style %}. This file is the part
   that must look the same everywhere.
   ===================================================================== */

/* ==========================================================
   1. Tokens
   ========================================================== */
.ctg,
.ctg-scope {
  --ctg-font: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;

  --bg: #f4f5f8;
  --surface: #fff;
  --surface-2: #f8f9fc;
  --surface-3: #f1f3f8;

  --border: #e6e8ef;
  --border-2: #d9dce6;

  --text: #1a2030;
  --text-2: #5b6577;
  --text-3: #8b94a6;

  --primary: #4f46e5;
  --primary-700: #4338ca;
  --primary-50: #eef0fe;
  --primary-100: #e0e2fb;

  --green: #0a8f53;  --green-bg: #e6f6ee;
  --amber: #b4690e;  --amber-bg: #fbf0dd;
  --rose: #d61f49;   --rose-bg: #fdeaee;
  --sky: #0e7490;    --sky-bg: #e0f2f7;
  --purple: #7c3aed; --purple-bg: #f1ebfe;

  --r-sm: 8px;
  --r: 12px;
  --r-lg: 16px;
  --r-xl: 20px;

  --shadow-sm: 0 1px 2px rgba(20, 28, 48, .05), 0 1px 1px rgba(20, 28, 48, .04);
  --shadow: 0 2px 8px rgba(20, 28, 48, .06), 0 1px 2px rgba(20, 28, 48, .05);
  --shadow-md: 0 8px 28px rgba(20, 28, 48, .10), 0 2px 6px rgba(20, 28, 48, .06);
  --shadow-pop: 0 18px 50px rgba(20, 28, 48, .22);

  --ease: cubic-bezier(.22, .61, .36, 1);

  /* Default accent. Any block can re-point --accent at one of the hues
     above and the primitives inside it follow — that is how one stat card
     is amber and its neighbour green without either of them naming a
     colour twice. */
  --accent: var(--primary);
  --accent-bg: var(--primary-50);

  font-family: var(--ctg-font);
  color: var(--text);
}

/* The page ground. Scoped by :has so a page that never opts in keeps the
   Bootstrap white it was built against. */
body:has(.ctg) { background: var(--bg, #f4f5f8); }

/* Some pages own their whole look already and want only the tokens — putting
   `.ctg` on them would drag the component layer in and restyle controls their
   own CSS is responsible for. Those use `ctg-scope` for the values and add
   `ctg-ground` when they also want the app's background. Deliberately not
   keyed off `.ctg-scope` itself: a modal carries that class too, and a modal
   must not repaint the page behind it. */
body:has(.ctg-ground) { background: var(--bg, #f4f5f8); }

/* ==========================================================
   2. Base
   ========================================================== */
.ctg *, .ctg *::before, .ctg *::after { box-sizing: border-box; }
.ctg a { color: inherit; text-decoration: none; }
/* Only bare links pick up the hover colour. A link that carries a class is a
   component — .btn, .stat__link, .chip — and owns its own hover, which a
   blanket rule here would win against and repaint indigo. */
.ctg a:not([class]):hover { color: var(--primary); }
.ctg button, .ctg input, .ctg select, .ctg textarea { font-family: inherit; }
.ctg img, .ctg video { max-width: 100%; }
.ctg svg[data-lucide] { width: 18px; height: 18px; stroke-width: 2; }
.ctg hr { border: 0; border-top: 1px solid var(--border); margin: 22px 0; opacity: 1; }

.ctg :focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 4px;
}

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

/* ==========================================================
   3. Layout
   ========================================================== */
.ctg-wrap { max-width: 1180px; margin: 0 auto; padding: 26px 20px 80px; }
.ctg-wrap--narrow { max-width: 820px; }
.ctg-wrap--wide { max-width: 1420px; }
@media (max-width: 560px) { .ctg-wrap { padding: 18px 14px 60px; } }

/* Page header: title, one line of context, actions on the right. */
.ctg .page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-bottom: 26px;
}
.ctg .page-head__main { min-width: 0; }
.ctg .page-head h1 {
  margin: 0; font-size: 28px; font-weight: 800;
  letter-spacing: -.025em; line-height: 1.2; color: var(--text);
}
.ctg .page-head .sub {
  margin: 7px 0 0; color: var(--text-2); font-size: 15px;
  max-width: 68ch; line-height: 1.5;
}
.ctg .page-head__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
@media (max-width: 560px) {
  .ctg .page-head h1 { font-size: 23px; }
  .ctg .page-head__actions { width: 100%; }
}

/* Back link above a detail page title. */
.ctg .backlink {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 650; color: var(--text-3);
  margin-bottom: 10px; transition: color .14s var(--ease);
}
.ctg .backlink:hover { color: var(--primary); }
.ctg .backlink i, .ctg .backlink svg { width: 14px; height: 14px; font-size: 12px; }

/* Grids. */
.ctg .ctg-grid-2, .ctg .ctg-grid-3, .ctg .ctg-grid-4 { display: grid; gap: 18px; }
.ctg .ctg-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ctg .ctg-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ctg .ctg-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1020px) { .ctg .ctg-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 860px)  { .ctg .ctg-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  {
  .ctg .ctg-grid-2, .ctg .ctg-grid-3, .ctg .ctg-grid-4 { grid-template-columns: 1fr; }
}

/* Main column + sidebar. */
.ctg .ctg-split { display: grid; grid-template-columns: minmax(0, 1fr) 348px; gap: 24px; align-items: start; }
.ctg .ctg-split--wide-side { grid-template-columns: minmax(0, 1fr) 400px; }
@media (max-width: 980px) { .ctg .ctg-split, .ctg .ctg-split--wide-side { grid-template-columns: 1fr; } }

/* ==========================================================
   4. Buttons
   Both vocabularies land on the same shape: the CTG modifier
   (.btn--primary) and the Bootstrap class (.btn-primary) an old
   template already carries.
   ========================================================== */
.ctg .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 40px; padding: 0 15px; border-radius: 10px;
  border: 1px solid var(--border-2); background: var(--surface); color: var(--text);
  font-family: var(--ctg-font); font-size: 14px; font-weight: 650; line-height: 1;
  cursor: pointer; white-space: nowrap; text-decoration: none;
  transition: background .15s var(--ease), border-color .15s var(--ease),
              color .15s var(--ease), transform .15s var(--ease), box-shadow .15s var(--ease);
}
.ctg .btn:hover { background: var(--surface-3); color: var(--text); }
.ctg .btn:active { transform: translateY(1px); }
.ctg .btn:disabled, .ctg .btn.disabled { opacity: .55; pointer-events: none; }
.ctg .btn svg { width: 17px; height: 17px; }
.ctg .btn i { font-size: 14px; }

.ctg .btn--primary, .ctg .btn-primary {
  background: var(--primary); border-color: var(--primary); color: #fff;
  box-shadow: 0 1px 2px rgba(79, 70, 229, .35);
}
.ctg .btn--primary:hover, .ctg .btn-primary:hover {
  background: var(--primary-700); border-color: var(--primary-700); color: #fff;
}
.ctg .btn--ghost, .ctg .btn-outline-primary, .ctg .btn-outline-secondary,
.ctg .btn-secondary, .ctg .btn-light, .ctg .btn-outline-dark {
  background: var(--surface); border-color: var(--border-2); color: var(--text);
}
.ctg .btn--ghost:hover, .ctg .btn-outline-primary:hover, .ctg .btn-outline-secondary:hover,
.ctg .btn-secondary:hover, .ctg .btn-light:hover, .ctg .btn-outline-dark:hover {
  background: var(--surface-3); border-color: var(--border-2); color: var(--text);
}
/* Ghost that leans on the accent — the "secondary but still the point" button. */
.ctg .btn--ghost-primary {
  background: var(--primary-50); border-color: var(--primary-100); color: var(--primary-700);
}
.ctg .btn--ghost-primary:hover { background: var(--primary); border-color: var(--primary); color: #fff; }

.ctg .btn--success, .ctg .btn-success {
  background: var(--green); border-color: var(--green); color: #fff;
}
.ctg .btn--success:hover, .ctg .btn-success:hover { filter: brightness(.94); color: #fff; }
.ctg .btn--danger, .ctg .btn-danger {
  background: var(--rose); border-color: var(--rose); color: #fff;
}
.ctg .btn--danger:hover, .ctg .btn-danger:hover { filter: brightness(.94); color: #fff; }
.ctg .btn--danger-soft, .ctg .btn-outline-danger {
  background: var(--rose-bg); border-color: transparent; color: var(--rose);
}
.ctg .btn--danger-soft:hover, .ctg .btn-outline-danger:hover {
  background: var(--rose); border-color: var(--rose); color: #fff;
}
.ctg .btn--warning, .ctg .btn-warning {
  background: var(--amber-bg); border-color: transparent; color: var(--amber);
}
.ctg .btn--warning:hover, .ctg .btn-warning:hover { background: var(--amber); color: #fff; }
/* The remaining Bootstrap outline variants. Without these they fall through to
   the neutral .btn and lose the meaning their colour was carrying — a green
   "Skills" button turning the same grey as "Delete" is a real regression. */
.ctg .btn--success-soft, .ctg .btn-outline-success {
  background: var(--green-bg); border-color: transparent; color: var(--green);
}
.ctg .btn--success-soft:hover, .ctg .btn-outline-success:hover {
  background: var(--green); border-color: var(--green); color: #fff;
}
.ctg .btn--info-soft, .ctg .btn-outline-info, .ctg .btn-info {
  background: var(--sky-bg); border-color: transparent; color: var(--sky);
}
.ctg .btn--info-soft:hover, .ctg .btn-outline-info:hover, .ctg .btn-info:hover {
  background: var(--sky); border-color: var(--sky); color: #fff;
}
.ctg .btn-outline-warning {
  background: var(--amber-bg); border-color: transparent; color: var(--amber);
}
.ctg .btn-outline-warning:hover { background: var(--amber); border-color: var(--amber); color: #fff; }
/* A link styled as a button keeps reading as a link. */
.ctg .btn-link {
  background: transparent; border-color: transparent; color: var(--primary);
  text-decoration: none; height: auto; padding: 0;
}
.ctg .btn-link:hover { background: transparent; color: var(--primary-700); text-decoration: underline; }
.ctg .btn--dark, .ctg .btn-dark { background: #1a2030; border-color: #1a2030; color: #fff; }
.ctg .btn--dark:hover, .ctg .btn-dark:hover { background: #11151f; color: #fff; }

.ctg .btn--sm, .ctg .btn-sm { height: 34px; padding: 0 12px; font-size: 13px; border-radius: 9px; }
.ctg .btn--sm svg, .ctg .btn-sm svg { width: 15px; height: 15px; }
.ctg .btn--lg, .ctg .btn-lg { height: 46px; padding: 0 20px; font-size: 15px; border-radius: 12px; }
.ctg .btn--pill, .ctg .btn--rpill, .ctg .rounded-pill.btn { border-radius: 999px; }
.ctg .btn--block, .ctg .btn.w-100 { width: 100%; }
/* Square icon-only button. */
.ctg .btn--icon { width: 40px; padding: 0; flex: none; }
.ctg .btn--icon.btn--sm { width: 34px; }
/* The one loud button on a page — used for the primary create action. */
.ctg .btn--hero {
  height: 44px; padding: 0 20px; border: 0; border-radius: 12px; color: #fff;
  font-size: 14.5px; font-weight: 700;
  background: linear-gradient(135deg, #4f46e5, #7c74f3);
  box-shadow: 0 3px 14px rgba(79, 70, 229, .32);
}
.ctg .btn--hero:hover { filter: brightness(1.06); color: #fff; }

.ctg .btn-group > .btn { border-radius: 0; }
.ctg .btn-group > .btn:first-child { border-top-left-radius: 10px; border-bottom-left-radius: 10px; }
.ctg .btn-group > .btn:last-child { border-top-right-radius: 10px; border-bottom-right-radius: 10px; }

/* Row of buttons that should wrap cleanly on a phone. */
.ctg .btnrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ==========================================================
   5. Cards
   .ctg-card is the primitive; Bootstrap's .card is pulled onto it so
   existing markup lands in the right place without being rewritten.
   ========================================================== */
.ctg .ctg-card, .ctg .card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}
.ctg .card { margin-bottom: 0; }
.ctg .ctg-card--hover:hover, .ctg .card--hover:hover {
  transform: translateY(-3px); border-color: var(--border-2); box-shadow: var(--shadow-md);
}
.ctg .ctg-card__head, .ctg .card-header {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px 0; background: transparent; border: 0;
}
.ctg .ctg-card__title, .ctg .card-title {
  margin: 0; font-size: 16px; font-weight: 750; letter-spacing: -.01em;
  display: flex; align-items: center; gap: 10px; color: var(--text);
}
.ctg .ctg-card__actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.ctg .ctg-card__body, .ctg .card-body { padding: 16px 20px 20px; }
.ctg .ctg-card__foot, .ctg .card-footer {
  padding: 14px 20px; background: var(--surface-2);
  border-top: 1px solid var(--border); border-radius: 0 0 var(--r-lg) var(--r-lg);
}
.ctg .card-subtitle, .ctg .card-text { color: var(--text-2); font-size: 14px; }

/* The small square icon that sits at the head of a card or a section. */
.ctg .ctg-card__icon, .ctg .ti {
  width: 32px; height: 32px; border-radius: 9px; flex: none;
  display: grid; place-items: center; font-size: 15px;
  background: var(--surface-3); color: var(--text-2);
}
.ctg .ctg-card__icon svg, .ctg .ti svg { width: 17px; height: 17px; }
.ctg .ti--primary { background: var(--primary-50); color: var(--primary); }
.ctg .ti--green   { background: var(--green-bg);  color: var(--green); }
.ctg .ti--amber   { background: var(--amber-bg);  color: var(--amber); }
.ctg .ti--rose    { background: var(--rose-bg);   color: var(--rose); }
.ctg .ti--sky     { background: var(--sky-bg);    color: var(--sky); }
.ctg .ti--purple  { background: var(--purple-bg); color: var(--purple); }

/* .panel is the surface you act inside: a form, a settings group, a player.
   .card is for a thing in a list of things; .sec is spacing with a heading and
   no surface at all. Pages kept hand-rolling this exact block, so it lives here
   now. Combine freely: `class="sec panel"` is a titled block on a surface. */
.ctg .panel {
  padding: 22px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
}
.ctg .panel__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 16px;
}
/* The heading of a panel sits closer to its content than a bare section's does,
   because the surface is already doing the grouping. */
.ctg .panel > .sec__title { margin-bottom: 14px; }
.ctg .panel__intro { margin: -8px 0 18px; font-size: 13.5px; line-height: 1.6; color: var(--text-3); }
.ctg .panel > .field:last-child, .ctg .panel > form > .field:last-child { margin-bottom: 0; }
@media (max-width: 600px) { .ctg .panel { padding: 18px 16px; } }

/* ==========================================================
   6. Section head
   ========================================================== */
.ctg .sec { margin-bottom: 34px; }
.ctg .sec:last-child { margin-bottom: 0; }
.ctg .sec__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin-bottom: 15px; flex-wrap: wrap;
}
.ctg .sec__title {
  display: flex; align-items: center; gap: 10px; margin: 0;
  font-size: 18px; font-weight: 780; letter-spacing: -.015em; color: var(--text);
}
.ctg .sec__sub { margin: 4px 0 0; font-size: 13.5px; color: var(--text-3); }
.ctg .sec__count {
  font-size: 12px; font-weight: 800; color: var(--text-3);
  background: var(--surface-3); border-radius: 999px; padding: 2px 9px;
}
.ctg .sec__link {
  display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 14px;
  border-radius: 999px; font-size: 13px; font-weight: 700; color: var(--text-2);
  background: var(--surface); border: 1px solid var(--border-2); transition: .12s var(--ease);
}
.ctg .sec__link:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-50); }

/* ==========================================================
   7. Stat cards
   ========================================================== */
.ctg .statgrid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; margin-bottom: 34px; }
.ctg .statgrid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ctg .statgrid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 920px) { .ctg .statgrid, .ctg .statgrid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .ctg .statgrid, .ctg .statgrid--3, .ctg .statgrid--2 { grid-template-columns: 1fr; } }

.ctg .stat {
  position: relative; isolation: isolate; display: flex; flex-direction: column;
  padding: 17px 18px 13px; border-radius: 18px; border: 1px solid var(--border); text-align: left;
  background: linear-gradient(180deg, #fff 0%, #fcfcfd 100%);
  box-shadow: 0 1px 2px rgba(20, 28, 48, .045), 0 5px 16px -10px rgba(20, 28, 48, .14);
  transition: transform .34s cubic-bezier(.2, .7, .15, 1), box-shadow .34s cubic-bezier(.2, .7, .15, 1), border-color .34s;
}
.ctg .stat:hover {
  transform: translateY(-4px); border-color: var(--border-2);
  box-shadow: 0 2px 4px rgba(20, 28, 48, .05), 0 20px 36px -22px rgba(20, 28, 48, .26);
}
.ctg .stat__top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ctg .stat__icon {
  width: 46px; height: 46px; border-radius: 14px; flex: none;
  display: grid; place-items: center; font-size: 19px; color: var(--accent);
  background: linear-gradient(155deg, color-mix(in srgb, var(--accent) 15%, #fff), color-mix(in srgb, var(--accent) 6%, #fff));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85),
              inset 0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent),
              0 5px 12px -8px color-mix(in srgb, var(--accent) 42%, transparent);
  transition: transform .34s cubic-bezier(.2, .7, .15, 1);
}
.ctg .stat:hover .stat__icon { transform: translateY(-2px); }
.ctg .stat__icon svg { width: 21px; height: 21px; }
.ctg .stat__body { margin-top: 13px; }
.ctg .stat__label {
  margin: 0 0 6px; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; color: var(--text-3);
}
.ctg .stat__numrow { display: flex; align-items: baseline; gap: 9px; }
.ctg .stat__num {
  font-size: 34px; font-weight: 800; letter-spacing: -.04em; line-height: 1; color: #11141d;
  font-variant-numeric: tabular-nums lining-nums; font-feature-settings: "tnum" 1, "lnum" 1;
}
.ctg .stat__num--sm { font-size: 22px; }
.ctg .stat__chip {
  display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 10px;
  border-radius: 999px; font-size: 11.5px; font-weight: 800; transform: translateY(-2px);
}
.ctg .stat__chip--green { background: var(--green-bg); color: var(--green); }
/* .stat is a flex column, so anything pill-shaped inside it stretches to the
   card's full width and stops reading as a pill. Pull them back to their
   content. */
.ctg .stat > .stat__chip, .ctg .stat > .badge-soft, .ctg .stat > .badge,
.ctg .stat > .stat__link { align-self: flex-start; }
.ctg .stat__chip--rose  { background: var(--rose-bg);  color: var(--rose); }
.ctg .stat__chip--amber { background: var(--amber-bg); color: var(--amber); }
.ctg .stat__link {
  position: relative; display: flex; align-items: center; gap: 6px;
  margin-top: 13px; padding-top: 12px;
  font-size: 12.5px; font-weight: 750; color: var(--accent); transition: .16s var(--ease);
}
.ctg .stat__link::before {
  content: ""; position: absolute; top: 0; left: -2px; right: -2px; height: 1px;
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--text-3) 26%, transparent) 18%,
    color-mix(in srgb, var(--text-3) 26%, transparent) 82%, transparent);
}
.ctg .stat__link svg, .ctg .stat__link i {
  width: 26px; height: 26px; margin-left: auto; padding: 6px; border-radius: 50%;
  box-sizing: border-box; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 11%, transparent);
  display: inline-flex; align-items: center; justify-content: center; font-size: 12px;
  transition: background .22s var(--ease), color .22s var(--ease), transform .22s cubic-bezier(.2, .7, .15, 1);
}
.ctg .stat:hover .stat__link svg, .ctg .stat:hover .stat__link i {
  background: var(--accent); color: #fff; transform: translateX(2px);
}
/* Accent routing: the icon modifier colours the whole card. Both variables
   move together, so a block that uses --accent-bg (a .rowitem icon, a tile)
   never ends up amber on an indigo wash. */
.ctg .stat:has(.stat__icon--primary), .ctg .accent-primary { --accent: var(--primary); --accent-bg: var(--primary-50); }
.ctg .stat:has(.stat__icon--green),   .ctg .accent-green   { --accent: var(--green);   --accent-bg: var(--green-bg); }
.ctg .stat:has(.stat__icon--amber),   .ctg .accent-amber   { --accent: var(--amber);   --accent-bg: var(--amber-bg); }
.ctg .stat:has(.stat__icon--rose),    .ctg .accent-rose    { --accent: var(--rose);    --accent-bg: var(--rose-bg); }
.ctg .stat:has(.stat__icon--sky),     .ctg .accent-sky     { --accent: var(--sky);     --accent-bg: var(--sky-bg); }
.ctg .stat:has(.stat__icon--purple),  .ctg .accent-purple  { --accent: var(--purple);  --accent-bg: var(--purple-bg); }

/* ==========================================================
   8. Tables
   ========================================================== */
.ctg .table-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm); overflow: hidden;
}
.ctg .table-responsive { overflow-x: auto; }
.ctg table.ctg-table, .ctg .table-card table, .ctg table.table {
  width: 100%; border-collapse: collapse; margin: 0; color: var(--text);
}
.ctg .ctg-table thead th, .ctg .table-card thead th, .ctg table.table thead th {
  text-align: left; background: var(--surface-2); color: var(--text-3);
  font-weight: 700; text-transform: uppercase; font-size: 11px; letter-spacing: .06em;
  border-bottom: 1px solid var(--border); border-top: 0; padding: 12px 18px; white-space: nowrap;
}
.ctg .ctg-table tbody td, .ctg .table-card tbody td, .ctg table.table tbody td {
  padding: 13px 18px; vertical-align: middle;
  border-bottom: 1px solid var(--surface-3); border-top: 0; color: var(--text); font-size: 14px;
}
.ctg .ctg-table tbody tr:last-child td,
.ctg .table-card tbody tr:last-child td,
.ctg table.table tbody tr:last-child td { border-bottom: 0; }
.ctg .ctg-table tbody tr, .ctg .table-card tbody tr, .ctg table.table tbody tr {
  transition: background .12s var(--ease);
}
.ctg .ctg-table tbody tr:hover, .ctg .table-card tbody tr:hover,
.ctg table.table tbody tr:hover { background: var(--surface-2); }
.ctg th.end, .ctg td.end { text-align: right; }
.ctg .cell-strong { font-weight: 700; color: var(--text); }
.ctg .cell-sub { font-size: 12.5px; color: var(--text-3); margin-top: 1px; }
.ctg .cell-sub.trunc { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctg .cell-person { display: flex; align-items: center; gap: 11px; }
.ctg .cell-muted { color: var(--text-3); }
.ctg .tbl-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* ==========================================================
   9. Avatars
   ========================================================== */
.ctg .avatar-circle {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: grid; place-items: center; overflow: hidden;
  font-weight: 750; font-size: 14px; text-transform: uppercase;
  background: var(--primary-50); color: var(--primary-700);
}
.ctg .avatar-circle img { width: 100%; height: 100%; object-fit: cover; }
.ctg .avatar-circle--sm { width: 30px; height: 30px; font-size: 12px; }
.ctg .avatar-circle--lg { width: 56px; height: 56px; font-size: 20px; }
.ctg .avatar-circle.a1 { background: #e7ecfe; color: #3f43c8; }
.ctg .avatar-circle.a2 { background: #e2f3ee; color: #0a8f53; }
.ctg .avatar-circle.a3 { background: #fdece0; color: #b4690e; }

/* ==========================================================
   10. Badges, pills, scores
   ========================================================== */
.ctg .badge-soft, .ctg .badge {
  display: inline-flex; align-items: center; gap: 6px;
  height: 24px; padding: 0 11px; border-radius: 999px;
  font-size: 12.5px; font-weight: 700; white-space: nowrap;
  background: var(--surface-3); color: var(--text-2);
}
.ctg .badge-soft .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.ctg .badge-soft--success, .ctg .badge.bg-success, .ctg .badge.bg-success-subtle,
.ctg .badge.text-bg-success { background: var(--green-bg) !important; color: var(--green) !important; }
.ctg .badge-soft--warning, .ctg .badge.bg-warning, .ctg .badge.bg-warning-subtle,
.ctg .badge.text-bg-warning { background: var(--amber-bg) !important; color: var(--amber) !important; }
.ctg .badge-soft--danger, .ctg .badge.bg-danger, .ctg .badge.bg-danger-subtle,
.ctg .badge.text-bg-danger { background: var(--rose-bg) !important; color: var(--rose) !important; }
.ctg .badge-soft--info, .ctg .badge.bg-info, .ctg .badge.bg-info-subtle,
.ctg .badge.text-bg-info { background: var(--sky-bg) !important; color: var(--sky) !important; }
.ctg .badge-soft--primary, .ctg .badge.bg-primary, .ctg .badge.text-bg-primary {
  background: var(--primary-50) !important; color: var(--primary-700) !important;
}
.ctg .badge-soft--neutral, .ctg .badge.bg-secondary, .ctg .badge.bg-light,
.ctg .badge.text-bg-secondary { background: var(--surface-3) !important; color: var(--text-2) !important; }
.ctg .badge-soft--purple, .ctg .badge.bg-purple { background: var(--purple-bg) !important; color: var(--purple) !important; }
/* Vue templates pick a badge class in JavaScript, so the whole Bootstrap set
   has to land somewhere sensible or a computed `bg-dark` comes out unstyled. */
.ctg .badge.bg-dark, .ctg .badge.text-bg-dark { background: #e7e9f0 !important; color: #2a3142 !important; }

/* A numeric result: 92%, 4/5, B2. Reads as a value, not a label. */
.ctg .score {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 48px; height: 26px; padding: 0 11px; border-radius: 8px;
  font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.ctg .score--hi  { background: var(--green-bg); color: var(--green); border: 1px solid #bfe6d2; }
.ctg .score--mid { background: var(--amber-bg); color: var(--amber); border: 1px solid #f0dcc0; }
.ctg .score--lo  { background: var(--rose-bg);  color: var(--rose);  border: 1px solid #f3c2cd; }

/* Filter / tag chips. */
.ctg .chiprow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ctg .chip {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 13px;
  border-radius: 999px; border: 1px solid var(--border-2); background: var(--surface);
  color: var(--text-2); font-size: 13px; font-weight: 650; cursor: pointer;
  transition: .13s var(--ease); white-space: nowrap;
}
.ctg .chip:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-50); }
.ctg .chip.is-active, .ctg .chip[aria-pressed="true"] {
  background: var(--primary); border-color: var(--primary); color: #fff;
}
.ctg .chip--static { cursor: default; }
.ctg .chip--static:hover { border-color: var(--border-2); color: var(--text-2); background: var(--surface); }

/* ==========================================================
   11. Progress
   ========================================================== */
.ctg .ctg-bar, .ctg .progress {
  height: 8px; border-radius: 999px; background: var(--surface-3);
  overflow: hidden; box-shadow: none;
}
.ctg .ctg-bar__fill, .ctg .progress-bar {
  height: 100%; border-radius: 999px; background: var(--primary);
  transition: width .9s var(--ease);
}
.ctg .ctg-bar__fill--green, .ctg .progress-bar.bg-success {
  background: linear-gradient(90deg, #0a8f53, #12b76a) !important;
}
.ctg .ctg-bar__fill--amber, .ctg .progress-bar.bg-warning { background: var(--amber) !important; }
.ctg .ctg-bar__fill--rose,  .ctg .progress-bar.bg-danger  { background: var(--rose) !important; }

/* ==========================================================
   12. Empty state
   An empty screen is an invitation to act, so it carries a button.
   ========================================================== */
.ctg .ctg-empty {
  text-align: center; padding: 56px 24px;
  background: var(--surface); border: 1px dashed var(--border-2); border-radius: var(--r-lg);
}
.ctg .ctg-empty--bare { background: transparent; border: 0; }
.ctg .ctg-empty__icon {
  width: 60px; height: 60px; border-radius: 18px; margin: 0 auto 16px;
  display: grid; place-items: center; font-size: 26px;
  background: var(--surface-3); color: var(--text-3);
}
.ctg .ctg-empty__icon svg { width: 26px; height: 26px; }
.ctg .ctg-empty h3, .ctg .ctg-empty h2 {
  margin: 0 0 6px; font-size: 17px; font-weight: 780; letter-spacing: -.01em; color: var(--text);
}
.ctg .ctg-empty p {
  margin: 0 auto 18px; max-width: 46ch; font-size: 14px;
  color: var(--text-2); line-height: 1.55;
}
.ctg .ctg-empty p:last-child { margin-bottom: 0; }

/* Compact empty row inside a table card. */
.ctg .tbl-empty { text-align: center; padding: 46px 20px; color: var(--text-3); }
.ctg .tbl-empty__icon {
  width: 50px; height: 50px; border-radius: 14px; margin: 0 auto 12px;
  display: grid; place-items: center; font-size: 24px;
  background: var(--surface-3); color: var(--text-3);
}
.ctg .tbl-empty p { margin: 0; font-weight: 600; color: var(--text-2); }

/* ==========================================================
   13. Forms
   ========================================================== */
.ctg .form-label, .ctg .ctg-label {
  display: block; margin-bottom: 6px;
  font-size: 13px; font-weight: 700; color: var(--text-2); letter-spacing: -.005em;
}
.ctg .form-text, .ctg .ctg-help { font-size: 12.5px; color: var(--text-3); margin-top: 5px; }
/* Django renders `{{ form.field }}` as a bare widget with no Bootstrap class,
   so the element selectors carry the styling for every hand-rolled form in the
   app. Listed by type rather than as a blanket `input`, to leave checkboxes,
   radios, ranges and file inputs to their own rules. */
.ctg input[type="text"], .ctg input[type="email"], .ctg input[type="password"],
.ctg input[type="search"], .ctg input[type="url"], .ctg input[type="tel"],
.ctg input[type="number"], .ctg input[type="date"], .ctg input[type="time"],
.ctg input[type="datetime-local"], .ctg input[type="month"], .ctg input[type="week"],
.ctg select, .ctg textarea,
.ctg .form-control, .ctg .form-select, .ctg textarea.form-control, .ctg .ctg-input {
  min-height: 42px; padding: 10px 13px;
  border: 1px solid var(--border-2); border-radius: 10px;
  /* background-COLOR, not the shorthand: .form-select is appearance:none with
     its chevron painted as a background-image, and the shorthand erases it,
     leaving a select that looks exactly like a text box. */
  background-color: var(--surface); color: var(--text);
  font-family: var(--ctg-font); font-size: 14px; line-height: 1.45;
  box-shadow: none; transition: border-color .14s var(--ease), box-shadow .14s var(--ease);
}
.ctg input:focus, .ctg select:focus, .ctg textarea:focus,
.ctg .form-control:focus, .ctg .form-select:focus, .ctg .ctg-input:focus {
  border-color: var(--primary); outline: 0;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent);
}
/* Checkboxes and radios opted out of the sizing above; give them the accent. */
.ctg input[type="checkbox"], .ctg input[type="radio"] { accent-color: var(--primary); }
/* Django's own error list. */
.ctg ul.errorlist { list-style: none; padding: 0; margin: 6px 0 0; color: var(--rose); font-size: 12.5px; }
.ctg .helptext { display: block; margin-top: 5px; font-size: 12.5px; color: var(--text-3); }
/* Full width is a property of a form row, not of every input on the page.
   Naming it here rather than on the element selectors above is what keeps an
   inline input — a cloze blank inside a sentence, a width-constrained answer
   box — the size its own page asked for. That rule, when it lived on the
   element selector, turned every gap in a fill-in-the-blank drill into a
   page-wide box. */
.ctg .field input:not([type="checkbox"]):not([type="radio"]):not([type="color"]),
.ctg .field select, .ctg .field textarea,
.ctg .form-control, .ctg .form-select, .ctg .ctg-input { width: 100%; }

/* A colour well is a swatch, not a field. Stretched to the row it stops being
   a control and becomes a stripe of paint, so it is excluded above and sized
   here — including .form-control-color, which Bootstrap gives the .form-control
   class and which would otherwise be caught by the same rule. */
.ctg input[type="color"], .ctg .form-control-color {
  width: 48px; height: 38px; padding: 3px; flex: none; cursor: pointer;
}

/* …except inside an input-group, whose children share one row. Written with
   the same :not() pair and placed after the rule above, because :not()
   arguments count toward specificity: without this an .input-group inside a
   .field wrapped its add-on onto a line of its own. */
.ctg .input-group > input:not([type="checkbox"]):not([type="radio"]),
.ctg .input-group > select, .ctg .input-group > textarea,
.ctg .input-group > .form-control, .ctg .input-group > .form-select {
  flex: 1 1 auto; width: 1%; min-width: 0;
}

.ctg .form-control::placeholder { color: var(--text-3); }
.ctg .form-control:disabled, .ctg .form-select:disabled { background: var(--surface-2); color: var(--text-3); }
.ctg .form-control-sm, .ctg .form-select-sm { min-height: 36px; padding: 7px 11px; font-size: 13px; border-radius: 9px; }
.ctg textarea.form-control { min-height: 110px; resize: vertical; }
.ctg .input-group { display: flex; flex-wrap: wrap; align-items: stretch; }
/* The element selectors above give every input width:100%, which is right on
   its own but breaks an .input-group: its children have to share one row.
   Bootstrap's own rule, restated here because ours is more specific. */
.ctg .input-group > .form-control,
.ctg .input-group > .form-select,
.ctg .input-group > input,
.ctg .input-group > select,
.ctg .input-group > textarea { position: relative; flex: 1 1 auto; width: 1%; min-width: 0; }
.ctg .input-group > .input-group-text,
.ctg .input-group > .btn { flex: 0 0 auto; }
.ctg .input-group > .form-control:not(:last-child),
.ctg .input-group > .form-select:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.ctg .input-group > .btn:not(:first-child),
.ctg .input-group > .input-group-text:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }
.ctg .input-group-text {
  display: inline-flex; align-items: center; padding: 0 13px;
  background: var(--surface-2); border: 1px solid var(--border-2);
  color: var(--text-3); font-size: 13px; border-radius: 10px;
}
.ctg .form-check { padding-left: 1.7em; }
.ctg .form-check-input {
  width: 1.05em; height: 1.05em; margin-left: -1.7em; margin-top: .2em;
  border: 1px solid var(--border-2); background-color: var(--surface);
}
.ctg .form-check-input:checked { background-color: var(--primary); border-color: var(--primary); }
.ctg .form-check-input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent); }
.ctg .form-check-label { font-size: 14px; color: var(--text); }
.ctg .form-switch .form-check-input { width: 2.2em; border-radius: 999px; }
.ctg .invalid-feedback, .ctg .errorlist, .ctg .text-danger { color: var(--rose); font-size: 12.5px; }
.ctg .errorlist { list-style: none; padding: 0; margin: 6px 0 0; }

/* A form laid out as rows of label + control. */
.ctg .field { margin-bottom: 18px; }
.ctg .field:last-child { margin-bottom: 0; }
.ctg .fieldrow { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
@media (max-width: 620px) { .ctg .fieldrow { grid-template-columns: 1fr; } }

/* Toolbar above a list: search on the left, filters and actions on the right. */
.ctg .toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px 16px; margin-bottom: 20px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-sm);
}
.ctg .toolbar__search { flex: 1 1 240px; min-width: 0; position: relative; }
.ctg .toolbar__search .form-control { padding-left: 38px; }
.ctg .toolbar__search .icon {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--text-3); font-size: 14px; pointer-events: none;
}
.ctg .toolbar__spacer { margin-left: auto; }

/* ==========================================================
   14. Tabs
   ========================================================== */
.ctg .nav-tabs, .ctg .ctg-tabs {
  display: flex; gap: 4px; border: 0; border-bottom: 1px solid var(--border);
  margin-bottom: 20px; padding: 0; list-style: none;
  /* A row of tabs scrolls sideways when there are many; naming only the x axis
     leaves the y axis in `auto`, which paints a stray vertical scrollbar the
     moment a tab is a pixel taller than its box. */
  overflow-x: auto; overflow-y: hidden;
}
.ctg .nav-tabs .nav-link, .ctg .ctg-tabs a, .ctg .ctg-tabs button {
  border: 0; background: transparent; border-radius: 0; padding: 10px 14px;
  font-size: 14px; font-weight: 700; color: var(--text-3);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  white-space: nowrap; cursor: pointer; transition: .14s var(--ease);
}
.ctg .nav-tabs .nav-link:hover, .ctg .ctg-tabs a:hover, .ctg .ctg-tabs button:hover {
  color: var(--text); border-color: var(--border-2);
}
.ctg .nav-tabs .nav-link.active, .ctg .ctg-tabs .is-active {
  color: var(--primary); background: transparent; border-bottom-color: var(--primary);
}
.ctg .nav-pills { gap: 8px; border: 0; }
.ctg .nav-pills .nav-link {
  border-radius: 999px; padding: 0 14px; height: 34px;
  display: inline-flex; align-items: center; border: 1px solid var(--border-2);
  background: var(--surface); color: var(--text-2); font-size: 13px; font-weight: 700;
}
.ctg .nav-pills .nav-link.active { background: var(--primary); border-color: var(--primary); color: #fff; }

/* ==========================================================
   15. Alerts, dropdowns, modals, pagination, list groups
   ========================================================== */
.ctg .alert {
  border: 1px solid var(--border); border-radius: var(--r);
  padding: 13px 16px; font-size: 14px; color: var(--text);
  background: var(--surface-2); box-shadow: none;
}
.ctg .alert-success { background: var(--green-bg); border-color: transparent; color: var(--green); }
.ctg .alert-warning { background: var(--amber-bg); border-color: transparent; color: var(--amber); }
.ctg .alert-danger  { background: var(--rose-bg);  border-color: transparent; color: var(--rose); }
.ctg .alert-info    { background: var(--sky-bg);   border-color: transparent; color: var(--sky); }
.ctg .alert-primary { background: var(--primary-50); border-color: transparent; color: var(--primary-700); }

.ctg .dropdown-menu {
  border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow-pop); padding: 7px; min-width: 220px;
}
.ctg .dropdown-item {
  display: flex; align-items: center; gap: 10px; padding: 9px 11px;
  border-radius: 9px; font-size: 14px; font-weight: 600; color: var(--text);
}
.ctg .dropdown-item:hover, .ctg .dropdown-item:focus { background: var(--surface-2); color: var(--text); }
.ctg .dropdown-item i, .ctg .dropdown-item svg {
  width: 28px; height: 28px; border-radius: 8px; flex: none;
  display: inline-grid; place-items: center; font-size: 13px;
  background: var(--surface-3); color: var(--text-2);
}
.ctg .dropdown-header {
  font-size: 11px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-3); padding: 8px 11px 4px;
}
.ctg .dropdown-divider { border-color: var(--border); }

/* Bootstrap moves .modal to the end of <body>, outside .ctg — put
   .ctg-scope on the modal element itself so the tokens reach it. */
.ctg-scope.modal .modal-content, .ctg .modal-content {
  border: 0; border-radius: var(--r-xl); box-shadow: var(--shadow-pop); overflow: hidden;
}
.ctg-scope.modal .modal-header, .ctg .modal-header {
  border: 0; padding: 20px 22px 14px; align-items: center; gap: 12px;
}
.ctg-scope.modal .modal-title, .ctg .modal-title {
  font-size: 18px; font-weight: 800; letter-spacing: -.01em; margin: 0; color: var(--text);
}
.ctg-scope.modal .modal-body, .ctg .modal-body { padding: 4px 22px 8px; }
.ctg-scope.modal .modal-footer, .ctg .modal-footer {
  border-top: 1px solid var(--border); padding: 16px 22px 20px; gap: 10px;
}

.ctg .pagination { display: flex; gap: 6px; list-style: none; padding: 0; margin: 26px 0 0; justify-content: center; }
.ctg .page-link {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; height: 36px; padding: 0 11px; border-radius: 10px;
  border: 1px solid var(--border-2); background: var(--surface);
  color: var(--text-2); font-size: 13.5px; font-weight: 700;
}
.ctg .page-link:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-50); }
.ctg .page-item.active .page-link { background: var(--primary); border-color: var(--primary); color: #fff; }
.ctg .page-item.disabled .page-link { opacity: .45; pointer-events: none; }

/* Bootstrap's accordion, used for FAQs and for collapsible groups. Its JS only
   cares about data-bs-toggle / data-bs-target / data-bs-parent and the
   .collapse class, so all of this is cosmetic. */
.ctg .accordion { display: flex; flex-direction: column; gap: 8px; }
.ctg .accordion-item {
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface);
  box-shadow: var(--shadow-sm); overflow: hidden;
}
.ctg .accordion-header { margin: 0; }
.ctg .accordion-button {
  padding: 14px 16px; background: var(--surface); border: 0; box-shadow: none;
  font-family: var(--ctg-font); font-size: 14.5px; font-weight: 700; color: var(--text);
}
.ctg .accordion-button:not(.collapsed) { background: var(--surface-2); color: var(--primary-700); box-shadow: none; }
.ctg .accordion-button:focus { box-shadow: none; border-color: transparent; }
.ctg .accordion-button::after { width: 15px; height: 15px; background-size: 15px; }
.ctg .accordion-body { padding: 4px 16px 16px; font-size: 14px; line-height: 1.6; color: var(--text-2); }

.ctg .list-group { border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--border); }
.ctg .list-group-item {
  border: 0; border-bottom: 1px solid var(--surface-3);
  padding: 13px 16px; font-size: 14px; color: var(--text); background: var(--surface);
}
.ctg .list-group-item:last-child { border-bottom: 0; }
.ctg .list-group-item-action:hover { background: var(--surface-2); }

/* ==========================================================
   16. Rows — the list item used everywhere a card grid is too heavy
   ========================================================== */
.ctg .rowlist { display: flex; flex-direction: column; gap: 10px; }
.ctg .rowitem {
  display: flex; align-items: center; gap: 14px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-sm);
  padding: 14px 16px; transition: .14s var(--ease);
}
.ctg .rowitem:hover { border-color: var(--primary); box-shadow: var(--shadow); }
.ctg .rowitem__icon {
  width: 40px; height: 40px; border-radius: 11px; flex: none;
  display: grid; place-items: center; font-size: 18px;
  background: var(--accent-bg); color: var(--accent);
}
.ctg .rowitem__icon svg { width: 19px; height: 19px; }
.ctg .rowitem__main { flex: 1; min-width: 0; }
.ctg .rowitem__title { font-weight: 700; font-size: 14.5px; color: var(--text); }
.ctg .rowitem__meta {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-top: 3px; font-size: 12.5px; color: var(--text-3);
}
.ctg .rowitem__meta .dot-sep { width: 3px; height: 3px; border-radius: 50%; background: var(--text-3); flex: none; }
.ctg .rowitem__actions { flex: none; display: flex; align-items: center; gap: 8px; }
@media (max-width: 620px) {
  .ctg .rowitem { flex-wrap: wrap; }
  .ctg .rowitem__actions { width: 100%; justify-content: flex-start; }
}

/* ==========================================================
   17. Action tiles — the grid of "things you can do next"
   ========================================================== */
.ctg .tilegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
.ctg .tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 11px; text-align: center; padding: 20px 12px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); color: var(--text); transition: .14s var(--ease);
}
.ctg .tile:hover {
  border-color: var(--primary); background: var(--primary-50);
  transform: translateY(-2px); box-shadow: var(--shadow-sm); color: var(--text);
}
.ctg .tile__ico {
  width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center;
  background: var(--surface-2); font-size: 21px; color: var(--accent); transition: .14s var(--ease);
}
.ctg .tile:hover .tile__ico { background: #fff; }
.ctg .tile__ico svg { width: 22px; height: 22px; }
.ctg .tile__ico.c-primary { color: var(--primary); }
.ctg .tile__ico.c-green   { color: var(--green); }
.ctg .tile__ico.c-amber   { color: var(--amber); }
.ctg .tile__ico.c-rose    { color: var(--rose); }
.ctg .tile__ico.c-sky     { color: var(--sky); }
.ctg .tile__ico.c-purple  { color: var(--purple); }
.ctg .tile__t { font-size: 13px; font-weight: 700; line-height: 1.25; }

/* ==========================================================
   18. Prose — teacher-authored HTML we do not control
   ========================================================== */
.ctg .ctg-prose { font-size: 15px; line-height: 1.7; color: var(--text); }
.ctg .ctg-prose > :first-child { margin-top: 0; }
.ctg .ctg-prose > :last-child { margin-bottom: 0; }
.ctg .ctg-prose h1, .ctg .ctg-prose h2, .ctg .ctg-prose h3,
.ctg .ctg-prose h4, .ctg .ctg-prose h5, .ctg .ctg-prose h6 {
  margin: 26px 0 10px; font-weight: 780; letter-spacing: -.015em; line-height: 1.3; color: var(--text);
}
.ctg .ctg-prose h1 { font-size: 23px; }
.ctg .ctg-prose h2 { font-size: 20px; }
.ctg .ctg-prose h3 { font-size: 17px; }
.ctg .ctg-prose h4, .ctg .ctg-prose h5, .ctg .ctg-prose h6 { font-size: 15px; }
.ctg .ctg-prose p { margin: 0 0 14px; }
.ctg .ctg-prose ul, .ctg .ctg-prose ol { margin: 0 0 14px; padding-left: 22px; }
.ctg .ctg-prose li + li { margin-top: 5px; }
.ctg .ctg-prose blockquote {
  margin: 0 0 14px; padding: 10px 16px;
  border-left: 3px solid var(--primary-100); background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; color: var(--text-2);
}
.ctg .ctg-prose img, .ctg .ctg-prose video { max-width: 100%; border-radius: var(--r); }
.ctg .ctg-prose table { width: 100%; border-collapse: collapse; margin: 0 0 14px; font-size: 14px; }
.ctg .ctg-prose th, .ctg .ctg-prose td { border: 1px solid var(--border); padding: 8px 11px; text-align: left; }
.ctg .ctg-prose th { background: var(--surface-2); font-weight: 700; }
.ctg .ctg-prose a { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
.ctg .ctg-prose code {
  background: var(--surface-3); padding: 2px 6px; border-radius: 6px;
  font-size: .9em; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ==========================================================
   19. Utilities
   Deliberately few — reach for a primitive before a utility.
   ========================================================== */
.ctg .ctg-muted { color: var(--text-2); }
.ctg .ctg-dim { color: var(--text-3); }
.ctg .ctg-tnum { font-variant-numeric: tabular-nums lining-nums; }
.ctg .ctg-trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctg .ctg-clamp-2 {
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}
.ctg .ctg-clamp-3 {
  display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3;
  -webkit-box-orient: vertical; overflow: hidden;
}
.ctg .ctg-divider { height: 1px; background: var(--border); margin: 20px 0; }

/* Bootstrap text/background helpers that old markup carries, retuned so a
   half-converted page never shows two greys or two greens side by side. */
.ctg .text-muted { color: var(--text-3) !important; }
.ctg .text-primary { color: var(--primary) !important; }
.ctg .text-success { color: var(--green) !important; }
.ctg .text-warning { color: var(--amber) !important; }
.ctg .text-danger  { color: var(--rose) !important; }
.ctg .text-info    { color: var(--sky) !important; }
.ctg .bg-light { background: var(--surface-2) !important; }
.ctg .bg-white { background: var(--surface) !important; }
.ctg .border { border-color: var(--border) !important; }
.ctg .shadow-sm { box-shadow: var(--shadow-sm) !important; }
.ctg .shadow { box-shadow: var(--shadow) !important; }
.ctg .rounded, .ctg .rounded-3 { border-radius: var(--r) !important; }
