/*
 * Medly admin theme.
 *
 * Loaded after Django's base.css, so it works by overriding the admin's own CSS
 * custom properties rather than replacing its stylesheet. That is what keeps it
 * cheap and reversible: delete the <link> in base_site.html and the admin is
 * back to stock, with all 100-odd existing template overrides intact.
 *
 * Every colour below comes from Medly's design system in
 * apps/teacher-dashboard/src/app/globals.css (medly-web) — brand-black #27221E,
 * medly-blue #06B0FF, state-selected #DBF3FF/#06B0FF, medly-green #7CC500.
 * Nothing here is invented.
 */

html[data-theme="light"],
:root {
    --medly-ground: #f8f7f5;
    --medly-surface: #ffffff;
    --medly-ink: #27221e;
    --medly-ink-2: #6b635c;
    --medly-ink-3: #767068;
    --medly-line: #e7e3de;
    --medly-accent: #06b0ff;
    --medly-accent-ink: #00659a;
    --medly-accent-soft: #dbf3ff;
    --medly-green: #7cc500;
    /* A fill that carries light text in both themes — see --primary. */
    --medly-fill: #27221e;
    --medly-on-fill: #ffffff;
    /* Error text. Django has no token for this and the custom dashboards
       hardcoded #b91c1c, which is unreadable on the dark ground. */
    --medly-danger: #b3261e;
    /* Tints for status chips and ghost-button hovers. The staff consoles
       hardcoded the light pair (#fdeeee / #e6f3ec), which reads as a lit patch
       on the dark ground; --medly-danger doubles as the ink on the danger
       tint in both themes. */
    --medly-danger-soft: #fdeeee;
    --medly-good-soft: #e6f3ec;
    --medly-good-ink: #1e6b41;
    --medly-warn-soft: #fff8e1;
    --medly-warn-ink: #8a5300;
    --medly-radius: 10px;
    --medly-radius-sm: 8px;
    /* Caret for the column-filter dropdowns. A data URI can't read a custom
       property, so the ink colour is baked in per theme — the hex is
       --medly-ink-3 for this theme, kept in step by hand. */
    --medly-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23767068'/%3E%3C/svg%3E");

    /* Django's own tokens, repointed. */
    /* Django uses --primary and --secondary as *fills* that carry light text,
       not as text colours: widgets.css paints .selector-chosen-title with
       --secondary and .list-footer-display and calendar hover with --primary.
       Pointing them at --medly-ink worked in this theme and broke the dark one,
       where the ink is near-white — the "Chosen permissions" header came out
       #f5f2ee with muted text on it at 2.1:1. They now point at a fill that
       stays dark in both themes, with --medly-on-fill for anything drawn on it. */
    --primary: var(--medly-fill);
    --secondary: var(--medly-fill);
    --accent: var(--medly-accent);
    --primary-fg: #ffffff;

    --body-fg: var(--medly-ink);
    --body-bg: var(--medly-surface);
    --body-quiet-color: var(--medly-ink-3);
    --body-medium-color: var(--medly-ink-2);
    --body-loud-color: var(--medly-ink);

    --header-color: var(--medly-ink);
    --header-branding-color: var(--medly-ink);
    --header-bg: var(--medly-surface);
    --header-link-color: var(--medly-ink-2);

    --breadcrumbs-fg: var(--medly-ink-3);
    --breadcrumbs-link-fg: var(--medly-ink-2);
    --breadcrumbs-bg: var(--medly-ground);

    --link-fg: var(--medly-accent-ink);
    --link-hover-color: var(--medly-ink);
    --link-selected-fg: var(--medly-accent-ink);

    --hairline-color: var(--medly-line);
    --border-color: var(--medly-line);

    --darkened-bg: var(--medly-ground);
    --selected-bg: var(--medly-accent-soft);
    --selected-row: var(--medly-accent-soft);

    /* Text-bearing accent surfaces use --medly-accent-ink, not --medly-accent:
       white on #06B0FF is only 2.4:1. Medly blue is a large-element brand
       colour, so it carries fills and dots, never small text or its ground. */
    --button-bg: var(--medly-accent-ink);
    --button-fg: #ffffff;
    --button-hover-bg: #005b8a;
    --default-button-bg: #005b8a;
    --default-button-hover-bg: var(--medly-accent-ink);

    /* Django defaults these to --button-fg on --close-button-bg (#333 in dark),
       which pairs dark text with a dark grey pill. Pin them to the button
       colours so "Add lesson" stays legible in both themes. */
    --object-tools-bg: var(--medly-accent-ink);
    --object-tools-fg: #ffffff;
    --object-tools-hover-bg: #005b8a;

    /* Chip that carries the user's initial in the header. */
    --medly-on-accent: #ffffff;
    --medly-avatar-bg: var(--medly-accent-ink);

    /* ABC Diatype is licensed and already shipping in medly-web's
       teacher-dashboard. Until the woff2 is copied into this app's static
       files the documented fallback stack applies, which is why the family is
       named first and the stack behind it is the same one the dashboard uses. */
    --font-family-primary: "ABC Diatype", -apple-system, BlinkMacSystemFont,
        "Segoe UI", Roboto, sans-serif;
}

@media (prefers-color-scheme: dark) {
    :root {
        --medly-ground: #1b1714;
        --medly-surface: #241f1b;
        --medly-ink: #f5f2ee;
        --medly-ink-2: #b0a79f;
        --medly-ink-3: #978d85;
        --medly-line: #37302b;
        --medly-accent: #38c3ff;
        --medly-accent-ink: #7ad4ff;
        --medly-accent-soft: #10344a;
        --medly-fill: #3a332d;
        --medly-on-fill: #f5f2ee;
        --medly-danger: #ffb4ab;
        --medly-danger-soft: #3a1f1c;
        --medly-good-soft: #17321f;
        --medly-good-ink: #86e0a8;
        --medly-warn-soft: #3a2c14;
        --medly-warn-ink: #ffcf70;
        --medly-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23978d85'/%3E%3C/svg%3E");

        --primary-fg: #08161f;
        /* Inverted here: the dark-theme accent is light, so it takes dark text
           (#08161f on #38C3FF is 9.1:1). */
        --button-bg: var(--medly-accent);
        --button-fg: #08161f;
        --button-hover-bg: var(--medly-accent-ink);
        --default-button-bg: var(--medly-accent-ink);
        --default-button-hover-bg: var(--medly-accent);
        --object-tools-bg: var(--medly-accent);
        --object-tools-fg: #08161f;
        --object-tools-hover-bg: var(--medly-accent-ink);
        --medly-on-accent: #08161f;
        --medly-avatar-bg: var(--medly-accent);
    }
}

html[data-theme="dark"] {
    --medly-ground: #1b1714;
    --medly-surface: #241f1b;
    --medly-ink: #f5f2ee;
    --medly-ink-2: #b0a79f;
    --medly-ink-3: #978d85;
    --medly-line: #37302b;
    --medly-accent: #38c3ff;
    --medly-accent-ink: #7ad4ff;
    --medly-accent-soft: #10344a;
    --medly-fill: #3a332d;
    --medly-on-fill: #f5f2ee;
    --medly-danger: #ffb4ab;
    --medly-danger-soft: #3a1f1c;
    --medly-good-soft: #17321f;
    --medly-good-ink: #86e0a8;
    --medly-warn-soft: #3a2c14;
    --medly-warn-ink: #ffcf70;
    --medly-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23978d85'/%3E%3C/svg%3E");

    --primary-fg: #08161f;
    /* Inverted here: the dark-theme accent is light, so it takes dark text
       (#08161f on #38C3FF is 9.1:1). */
    --button-bg: var(--medly-accent);
    --button-fg: #08161f;
    --button-hover-bg: var(--medly-accent-ink);
    --default-button-bg: var(--medly-accent-ink);
    --default-button-hover-bg: var(--medly-accent);
    --object-tools-bg: var(--medly-accent);
    --object-tools-fg: #08161f;
    --object-tools-hover-bg: var(--medly-accent-ink);
    --medly-on-accent: #08161f;
    --medly-avatar-bg: var(--medly-accent);
}

body {
    letter-spacing: -0.01em;
    background: var(--medly-ground);
}

/* ------------------------------------------------------------------ header */

/* These rules used to live in an inline <style> block in base_site.html. They
   moved here when this stylesheet was introduced: the inline block rendered
   after the <link>, so at equal specificity it won every colour argument. One
   home for admin chrome CSS, and the cascade stops being load-order-sensitive.
   The env-banner block stays inline because it is templated on ENVIRONMENT. */

#header {
    border-bottom: 1px solid var(--medly-line);
    padding: 14px 24px;
}

/* Lay the branding row out inline so the logo, commit hash, API docs link and
   the Exams/Open switcher share one baseline with consistent spacing. */
#header #branding {
    display: flex;
    align-items: center;
    gap: 20px;
    flex-wrap: wrap;
}

#site-name {
    font-size: 1.125rem;
    font-weight: 500;
}

#site-name a {
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

/* logo-mark-white.svg has a hard-coded white fill — it was drawn for Django's
   dark header and disappears on this light one. Sitting it on an accent chip
   keeps the single existing asset legible in both themes, and still degrades to
   a plain white mark on the stock header if this stylesheet is removed. */
#site-name .medly-logo-mark {
    height: 26px;
    width: 26px;
    display: block;
    box-sizing: border-box;
    padding: 4px;
    background: var(--medly-accent);
    border-radius: 7px;
}

#site-name .medly-wordmark {
    font-weight: 600;
    letter-spacing: -0.02em;
}

/* Django sets #header a:link to --header-link-color; the wordmark should read
   as the page's title rather than as a link. */
#site-name a:link,
#site-name a:visited {
    color: var(--medly-ink);
}

/* One consistent treatment for the secondary top-bar items, so they read as a
   group rather than several differently-sized fragments. */
.admin-topbar-meta {
    display: flex;
    align-items: center;
    gap: 16px;
    font-size: 0.8125rem;
    line-height: 1;
    white-space: nowrap;
}

.admin-topbar-meta a,
.admin-topbar-commit {
    color: var(--medly-ink-2);
    text-decoration: none;
}

.admin-topbar-meta a:hover {
    text-decoration: underline;
}

.admin-topbar-commit {
    background: var(--medly-ground);
    border: 1px solid var(--medly-line);
    border-radius: 5px;
    padding: 2px 6px;
    font-size: 10.5px;
    font-variant-numeric: tabular-nums;
}

#user-tools {
    text-transform: none;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0;
}

#user-tools a,
#logout-form button {
    border-bottom: 0;
}

#user-tools a:hover {
    text-decoration: underline;
}

#user-tools .medly-profile-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    text-decoration: none;
    color: inherit;
}

#user-tools .medly-profile-link:hover .medly-profile-name {
    text-decoration: underline;
}

#user-tools .medly-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--medly-avatar-bg);
    color: var(--medly-on-accent);
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
    text-transform: uppercase;
    flex-shrink: 0;
}

.admin-site-switcher,
.admin-env-switcher {
    display: flex;
    align-items: center;
    gap: 8px;
}

.admin-site-switcher a[aria-current="page"],
.admin-env-switcher a[aria-current="page"] {
    color: var(--medly-accent-ink);
    font-weight: 700;
}

.admin-site-switcher-sep {
    opacity: 0.5;
}

.admin-env-switcher-label {
    opacity: 0.6;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: 0.6875rem;
}

/* The environment banner stays loud on purpose — it is a safety signal — but
   uses Medly's palette and a lighter weight than Django's shout. */
.env-banner {
    font-size: 11px;
    letter-spacing: 0.14em;
    padding: 6px 16px;
}

div.breadcrumbs {
    padding: 12px 24px;
    border-bottom: 1px solid var(--medly-line);
}

/* --------------------------------------------------- module / caption bars */

/* Django paints every module caption in solid --header-bg. Replacing those
   bands with quiet small-caps labels on a hairline is the single biggest
   reduction in how busy the admin looks. */
.module caption,
.inline-group h2 {
    background: transparent;
    color: var(--medly-ink-3);
    border-bottom: 1px solid var(--medly-line);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    padding: 10px 12px;
}

.module caption a.section,
.module caption a.section:link,
.module caption a.section:visited {
    color: var(--medly-ink-3);
}

.module {
    border: 1px solid var(--medly-line);
    border-radius: var(--medly-radius);
    overflow: hidden;
}

#content {
    padding: 22px 24px 28px;
}

/* Django caps the dashboard's main column at a literal `width: 600px` and then
   reserves another 300px on the right for #content-related, both from the era
   when the index was a narrow two-column list. On a 1440px window that left the
   domain grid 633px — three cramped columns beside a mostly-empty panel. The
   grid now spans the full width and Recent actions sits underneath it, where a
   rarely-read panel belongs. */
.dashboard #content {
    width: auto;
}

.dashboard #content.colMS {
    margin-right: 0;
}

.dashboard #content-related {
    float: none;
    clear: both;
    width: auto;
    max-width: 480px;
    margin: 20px 0 0;
}

/* Recent actions, laid out to the same metrics as the domain cards above it:
   they are two lists of links on one page and were using different numbers.
   Django gives the panel's h2 16px of padding *and* a 16px bottom margin, then
   the h3 another 16px, so ~64px of chrome stood between the title and the first
   entry, and the h3 sat at the 16px inset while the entries' text sat at 34px —
   Django pads each .actionlist li by 18px to hang its add/change/delete sprite
   in that gutter — so a bold "My actions" lined up with the sprites rather than
   with the text it introduces, which is what read as ragged.
   The h3 is now the same quiet uppercase subhead the sidebar and the cards use,
   the head padding matches the cards' 11px 14px, and the 0.5em list margin is
   the value the card lists and the sidebar already share, so all three lists on
   the dashboard sit at one inset. */
#content-related .module h2 {
    padding: 11px 14px;
    margin-bottom: 0;
    font-size: 1rem;
}

#content-related .module h3 {
    margin: 10px 0 2px;
    padding: 0 14px;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--medly-ink-3);
}

#content-related .actionlist {
    margin: 0 0 0 0.5em;
    padding: 4px 14px 10px 0;
}

#content-related .actionlist li {
    margin-bottom: 6px;
    /* The sprite lives in this gutter; 16px matches the card rows' bullet slot
       (9px marker + 7px gap) so entry text lands where card link text does. */
    padding-left: 16px;
}

#content-related .actionlist li:last-child {
    margin-bottom: 0;
}

/* The content-type line under each entry ("School"), which Django renders as
   .mini.quiet — pulled closer to its own row so entries group visually. */
#content-related .actionlist li .mini.quiet {
    display: block;
    margin-top: 1px;
    color: var(--medly-ink-3);
}

/* ------------------------------------------------- Django's filter sidebar */

/* Django already renders the filter contents in the right place. nav.js adds
   one 23px rail immediately before it and toggles only the panel, leaving every
   built-in and custom list_filter unchanged. This deliberately shares the left
   navigation rail's measurements and theme tokens rather than introducing a
   second treatment for the same interaction. */
#changelist
    .changelist-form-container.medly-filter-sidebar-enabled:has(
        #changelist-filter
    )
    > div {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
}

.toggle-filter-sidebar {
    z-index: 20;
    order: 1;
    flex: 0 0 23px;
    align-self: stretch;
    width: 23px;
    margin-left: 30px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-left: 1px solid var(--medly-line);
    background: var(--medly-ground);
    color: var(--medly-ink-3);
    font: inherit;
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
}

.toggle-filter-sidebar::before {
    content: "\00BB";
}

.toggle-filter-sidebar[aria-expanded="false"]::before {
    content: "\00AB";
}

.filter-sidebar-collapsed .toggle-filter-sidebar {
    margin-left: 0;
}

.toggle-filter-sidebar:hover {
    background: var(--medly-ground);
}

.toggle-filter-sidebar:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: -2px;
}

#changelist .medly-filter-sidebar-enabled #changelist-filter {
    order: 2;
    margin-left: 0;
}

/* changelists.css gives #changelist .actions both `width: 100%` and `padding:
   10px` on the default content-box, so the bar measures 20px wider than its
   parent and #changelist clips its right edge. Keep it inside the flexible main
   pane in both sidebar states. */
#changelist .actions {
    box-sizing: border-box;
}

/* Wide content should scroll inside its own box rather than pushing the page
   sideways. Several custom dashboards lay out tables far wider than the
   viewport, and without this the whole document scrolls, which moves the header
   and sidebar off-screen with it. */
#content-main .module,
#content-main .results {
    max-width: 100%;
}

#content-main .results,
#content-main .medly-scroll-x {
    overflow-x: auto;
}

/* ----------------------------------------------------------------- sidebar */

#nav-sidebar {
    background: var(--medly-ground);
    padding-bottom: 24px;
    /* Django's `.sticky` caps the sidebar with `max-height: 100vh`, which limits
       the content box — so this padding plus the 1px border made the element 25px
       taller than the cap, pushing its last rows permanently below the fold. With
       border-box the cap means what it says. Matters more now that the sections
       list every table rather than six and a disclosure. */
    box-sizing: border-box;
}

.toggle-nav-sidebar,
.toggle-filter-sidebar {
    background: var(--medly-ground);
    color: var(--medly-ink-3);
}

.toggle-nav-sidebar {
    border-right: 1px solid var(--medly-line);
}

#nav-sidebar .nav-filter-sticky {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--medly-ground);
    padding: 8px;
    border-bottom: 1px solid var(--medly-line);
}

#nav-filter {
    margin: 0;
    padding: 7px 10px;
    border: 1px solid var(--medly-line);
    border-radius: var(--medly-radius-sm);
    background: var(--medly-surface);
    font-size: 0.8125rem;
}

/* The grouped sections are plain lists, not tables, so Django's #nav-sidebar
   table rules don't apply and these carry the whole treatment. */
#nav-sidebar .medly-nav-section {
    border: 0;
    border-radius: 0;
    background: transparent;
    margin: 0;
    overflow: visible;
}

.medly-nav-section-toggle {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 8px;
    border: 0;
    border-radius: var(--medly-radius-sm);
    background: transparent;
    color: var(--medly-ink);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
}

.medly-nav-section-toggle:hover {
    background: var(--medly-surface);
}

.medly-nav-section-toggle:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: -2px;
}

.medly-nav-caret,
.medly-nav-icon {
    flex: 0 0 auto;
    color: var(--medly-ink-3);
}

.medly-nav-caret {
    width: 11px;
    height: 11px;
    transition: transform 0.12s ease;
}

.medly-nav-icon {
    width: 15px;
    height: 15px;
}

.medly-nav-section-toggle[aria-expanded="true"] .medly-nav-caret {
    transform: rotate(90deg);
}

.medly-nav-section-label {
    flex: 1;
}

.medly-nav-count {
    font-size: 0.6875rem;
    color: var(--medly-ink-3);
    font-variant-numeric: tabular-nums;
}

/* Indentation is deliberately shallow, and shallower than it first was. Each
   nested level (section > subhead > row > marker) used to add a step, so a table
   name began ~74px in against the section label's ~59px — the child text stepped
   *past* its own heading, which is what read as a staircase. The row labels now
   land at ~61px: level with the section label, with the rule and the marker
   sitting in the gutter to its left carrying the depth instead of an indent. */
.medly-nav-section-body {
    padding: 1px 0 8px 0;
    margin-left: 4px;
    border-left: 1px solid var(--medly-line);
}

.medly-nav-subhead {
    margin: 8px 0 3px;
    padding: 0 4px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--medly-ink-3);
}

/* Django's base.css styles `ul li` with a square marker at a specificity that
   beats a bare `list-style: none` on the <ul>, so the marker is cleared on the
   <li> too — otherwise every nav row shows a stray bullet next to its dot. */
.medly-nav-list,
.medly-nav-list li {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Django's `.module ul` sets margin-left: 1.5em at (0,1,1), which outranks the
   bare `.medly-nav-list` above (0,1,0) — so the section lists were indented 21px
   (1.5em at the sidebar's 14px) despite that `margin: 0`. Two classes here make
   it (0,2,0), which wins. Scoped to the section body rather than to the class, so
   the pinned/recents quick lists — which are outside `.module` and were already
   flush — keep their own alignment. */
.medly-nav-section-body .medly-nav-list {
    margin-left: 0.5em;
}

.medly-nav-item {
    display: flex;
    align-items: center;
    gap: 2px;
    padding-right: 4px;
    border-radius: var(--medly-radius-sm);
}

.medly-nav-item:hover {
    background: var(--medly-surface);
}

.medly-nav-item.current-model {
    background: var(--medly-accent-soft);
}

.medly-nav-item > a,
.medly-nav-label--plain {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 6px 2px 6px 4px;
    font-size: 0.8125rem;
    line-height: 1rem;
    color: var(--medly-ink-2);
    text-decoration: none;
    border-radius: var(--medly-radius-sm);
}

.medly-nav-item > a:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: -2px;
}

.medly-nav-item.current-model > a {
    color: var(--medly-accent-ink);
    font-weight: 600;
}

.medly-nav-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* One fixed-width marker slot shared by both row types, so a console's gauge
   and a table's dot put their labels on the same left edge. Sizing lives here;
   only the mark inside differs. */
.medly-nav-bullet {
    flex: 0 0 9px;
    width: 9px;
    height: 9px;
    color: var(--medly-ink-3);
}

.medly-nav-bullet--dot {
    /* Drawn inside the slot rather than being the slot, so the dot stays 5px
       while still occupying the same width as the icon. */
    background: radial-gradient(
        circle at center,
        var(--medly-line) 0 2.5px,
        transparent 2.5px
    );
}

.medly-nav-item.current-model .medly-nav-bullet--dot {
    background: radial-gradient(
        circle at center,
        var(--medly-accent) 0 2.5px,
        transparent 2.5px
    );
}

.medly-nav-item.current-model .medly-nav-bullet--console {
    color: var(--medly-accent);
}

.medly-nav-pin {
    border: 0;
    background: transparent;
    padding: 2px;
    line-height: 1;
    cursor: pointer;
    color: var(--medly-ink-3);
    opacity: 0;
    border-radius: 4px;
}

.medly-nav-pin svg {
    width: 12px;
    height: 12px;
    display: block;
}

.medly-nav-item:hover .medly-nav-pin,
.medly-nav-pin.is-pinned,
.medly-nav-pin:focus-visible {
    opacity: 1;
}

.medly-nav-pin.is-pinned {
    color: #b5720c;
}

.medly-nav-pin:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: -1px;
}

.medly-nav-quick {
    padding: 6px 0;
    border-bottom: 1px solid var(--medly-line);
    margin-bottom: 3px;
}

.medly-nav-quick-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    padding: 4px 8px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--medly-ink-3);
}

.medly-nav-quick-head svg {
    width: 11px;
    height: 11px;
    flex: 0 0 11px;
}

/* -------------------------------------------------------------- index page */

.medly-home {
    width: auto;
}

.medly-home-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 14px;
}

.medly-home-card {
    display: flex;
    flex-direction: column;
    background: var(--medly-surface);
    margin-bottom: 0;
}

.medly-home-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding: 11px 14px;
    background: transparent;
    color: var(--medly-ink);
    border-bottom: 1px solid var(--medly-line);
    font-size: 0.875rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.medly-home-card-head svg {
    width: 15px;
    height: 15px;
    flex: 0 0 15px;
    color: var(--medly-accent);
}

.medly-home-card-head span:first-of-type {
    flex: 1;
}

.medly-home-card-count {
    font-size: 0.6875rem;
    font-weight: 400;
    color: var(--medly-ink-3);
}

.medly-home-card-links {
    list-style: none;
    margin: 0;
    padding: 4px 0;
}

/* Django's `.module ul { margin-left: 1.5em }` is (0,1,1) and outranks the
   `margin: 0` above (0,1,0), so these lists sat 21px in — which is also why the
   rows looked over-indented against their card heading. Two classes to clear it.
   Same trap, same fix, as .medly-nav-section-body .medly-nav-list. */
.medly-home-card .medly-home-card-links {
    margin-left: 0.5em;
}

.medly-home-card-links li {
    list-style: none;
    margin: 0;
    padding: 0;
}

.medly-home-card-links a {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 6px 14px;
    font-size: 0.8125rem;
    color: var(--medly-ink-2);
    text-decoration: none;
}

.medly-home-card-links a:hover {
    background: var(--medly-ground);
    color: var(--medly-accent-ink);
}

.medly-home-card-links .medly-nav-bullet--console,
.medly-home-card-links .medly-nav-bullet--dot {
    color: var(--medly-ink-3);
}

.medly-home-card-links a:hover .medly-nav-bullet--console {
    color: var(--medly-accent-ink);
}

/* Separates the tables from the consoles above them, matching the sidebar's
   CONSOLES/TABLES subheads so the two views read the same way. */
.medly-home-card-subhead {
    margin: 6px 0 2px;
    padding: 0 14px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--medly-ink-3);
}

/* Cards now hold two lists, so the grid needs taller minimum columns to avoid
   an over-tall single column on medium screens. */
.medly-home-grid {
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.medly-home-card-foot {
    margin: auto 0 0;
    padding: 9px 14px;
    border-top: 1px solid var(--medly-line);
    font-size: 0.6875rem;
    color: var(--medly-ink-3);
}

.medly-home-strip {
    border: 1px solid var(--medly-line);
    border-radius: var(--medly-radius);
    background: var(--medly-surface);
    margin-bottom: 16px;
    overflow: hidden;
}

.medly-home-strip-head {
    margin: 0;
    padding: 9px 14px;
    background: var(--medly-ground);
    border-bottom: 1px solid var(--medly-line);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--medly-ink-3);
}

.medly-home-strip-items {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 12px 14px;
}

.medly-home-strip-items a {
    display: inline-flex;
    align-items: center;
    padding: 4px 11px;
    border: 1px solid var(--medly-line);
    border-radius: 999px;
    background: var(--medly-ground);
    font-size: 0.8125rem;
    color: var(--medly-ink-2);
    text-decoration: none;
}

.medly-home-strip-items a:hover {
    background: var(--medly-surface);
    border-color: var(--medly-accent);
    color: var(--medly-accent-ink);
}

/* ------------------------------------------------- per-column filters */

/* Moved here from an inline <style> in
   admin/curriculum/change_list_with_batch.html when the filters became
   site-wide — one home, and the tokens below are this theme's. */

#result_list thead th {
    vertical-align: bottom;
}

.col-filter-input,
.col-filter-select,
.col-filter-null {
    display: block;
    width: 100%;
    margin-top: 2px;
    padding: 4px 6px;
    height: 26px;
    font-size: 12px;
    font-weight: 400;
    border: 1px solid var(--medly-line);
    border-radius: var(--medly-radius-sm);
    background: var(--medly-surface);
    color: var(--medly-ink);
    box-sizing: border-box;
}

/* The native control's chevron is far heavier than these 26px inputs, so the
   dropdown would read as a different kind of widget sitting next to them. */
.col-filter-select {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    padding-right: 20px;
    background-image: var(--medly-caret);
    background-repeat: no-repeat;
    background-position: right 6px center;
}

.col-filter-input:focus,
.col-filter-select:focus,
.col-filter-null:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: -1px;
    border-color: var(--medly-accent);
}

/* An applied filter has to be obvious at a glance — a column can be filtered
   while its control is scrolled out of view. */
.col-filter-input.has-value,
.col-filter-select.has-value,
.col-filter-null.has-value {
    border-color: var(--medly-accent);
    /* background-color, not the shorthand: that would reset the select's caret
       background-image and leave an applied dropdown with no chevron. */
    background-color: var(--medly-accent-soft);
    color: var(--medly-accent-ink);
    font-weight: 600;
}

.col-filter-null {
    width: auto;
    min-width: 26px;
    flex: none;
    margin-top: 0;
    padding: 0 5px;
    cursor: pointer;
    text-align: center;
    color: var(--medly-ink-3);
}

.col-filter-null:hover {
    border-color: var(--medly-accent);
    color: var(--medly-ink);
}

.col-filter-group {
    display: flex;
    gap: 2px;
    margin-top: 2px;
}

.col-filter-group .col-filter-input {
    flex: 1;
    min-width: 0;
    margin-top: 0;
}

/* Keeps unfilterable columns' headers aligned with filterable ones. */
.col-filter-spacer {
    display: block;
    height: 26px;
    margin-top: 2px;
}

/* Escape hatch shown beside the result counter when a column filter matched
   nothing, since a zero-result changelist has no header row to host controls. */
.col-filter-clear {
    margin-left: 8px;
    font-size: 12px;
    white-space: nowrap;
}

/* ------------------------------- groups and permissions (filter_horizontal) */

/* The two-list picker on Groups and on a user's Groups/Permissions. Django
   paints its headers with --secondary and its footer with --primary, both of
   which this theme redirects to --medly-fill, so the text on them has to come
   from --medly-on-fill rather than from --header-link-color (which is sized for
   the header bar, not for a fill). Without this the labels sat at 2.1:1. */

.selector-chosen-title,
.selector-chosen-title label,
.selector-chosen-title .helptext,
.selector-chosen .list-footer-display,
.selector-chosen .list-footer-display__clear {
    color: var(--medly-on-fill);
}

/* The rest is presentation: Django's picker predates the admin's own rounding
   and border tokens, so it reads as a transplant next to the themed fieldsets. */
.selector {
    gap: 12px;
}

.selector .selector-available,
.selector .selector-chosen {
    border-radius: var(--medly-radius-sm);
    overflow: hidden;
    border: 1px solid var(--medly-line);
    background: var(--medly-surface);
}

.selector-available-title,
.selector-chosen-title {
    border: 0;
    border-radius: 0;
    border-bottom: 1px solid var(--medly-line);
}

.selector select {
    border: 0;
    border-radius: 0;
    background: var(--medly-surface);
}

/* Filter box above the available list. */
.selector .selector-filter {
    border: 0;
    border-bottom: 1px solid var(--medly-line);
    background: var(--medly-ground);
    padding: 6px 8px;
}

.selector .selector-filter input {
    border: 1px solid var(--medly-line);
    border-radius: var(--medly-radius-sm);
    background: var(--medly-surface);
    color: var(--medly-ink);
    padding: 4px 8px;
}

/* Choose-all / remove-all are <button>s, so they already carry the admin's
   button fill. They take --button-fg, not the accent ink: on the dark theme,
   where the fill *is* the accent, accent-on-accent measured 1.22:1. */
.selector-chosen .selector-clearall,
.selector-available .selector-chooseall {
    color: var(--button-fg);
}

/* Django ships these as background-image sprites tinted for a light UI; they
   vanish against a dark fill without inverting. */
@media (prefers-color-scheme: dark) {
    .selector-add,
    .selector-remove,
    .selector-chooseall,
    .selector-clearall {
        filter: invert(1) hue-rotate(180deg);
    }
}

html[data-theme="dark"] .selector-add,
html[data-theme="dark"] .selector-remove,
html[data-theme="dark"] .selector-chooseall,
html[data-theme="dark"] .selector-clearall {
    filter: invert(1) hue-rotate(180deg);
}

/* The media query above fires on OS preference alone, so forcing the admin to
   Light on a dark-mode OS left these inverted on a light UI. The token blocks
   don't need this guard — `html[data-theme="light"], :root` outranks the `:root`
   inside the media query — but a `filter` has no light-theme counterpart to win
   over, so it needs stating. Written as a light-theme reset rather than by
   scoping the query to `[data-theme="auto"]`, so OS-dark still inverts on any
   page that renders without Django's theme attribute at all. */
html[data-theme="light"] .selector-add,
html[data-theme="light"] .selector-remove,
html[data-theme="light"] .selector-chooseall,
html[data-theme="light"] .selector-clearall {
    filter: none;
}

/* ------------------------------------------------------- sorted column header */

/* Django tints the sorted column's header with --selected-bg (this theme's
   accent-soft) but leaves its link on the muted ink, which lands at 4.01:1 —
   just under AA. The header is the one column you are definitely reading. */
#result_list thead th.sorted a,
#result_list thead th.sorted,
#result_list thead th.sorted .text a {
    color: var(--medly-ink);
}

/* ------------------------------------------------------------- link buttons */

/* Django underlines every anchor (base.css:128). That rule's :not() list carries
   `#content-main.app-list a`, and :not() takes the specificity of its most
   specific argument, so it lands at (1,1,2) — higher than any class rule a
   template can write, which is why link-buttons across the admin render
   underlined even though each template sets `text-decoration: none` itself.
   Fixed once here. `:is()` keeps the list flat and contributes its most specific
   argument, (0,1,1); the trailing `:is(:link, :visited)` adds the class-level
   unit needed to clear (1,1,2), landing this at (1,2,1).
   Add a selector when a new link-button appears — or put `role="button"` on the
   anchor, which is Django's own escape hatch from the same rule. */
#content
    :is(
        a.button,
        a.force-sync-btn,
        .rct-followup a,
        .rct-class-actions a,
        .rct-school-actions a,
        .staff-actions a,
        .clra-add-btn
    ):is(:link, :visited) {
    text-decoration: none;
}

/* --------------------------------------------------------------- delete link */

/* Django pairs .deletelink with --button-fg, which this theme inverts to near
   black in dark mode — dark text on Django's red at 2.9:1. The delete button is
   always red, so its text is always white. */
a.deletelink,
.deletelink-box a,
a.deletelink:link,
a.deletelink:visited {
    color: #ffffff;
}

/* --------------------------------------------------- close / cancel links */

/* Same trap as .deletelink, and the one the "Close" button on a read-only
   change form hit: Django's --close-button-bg is grey in both themes (#747474
   light, #333333 dark) but its label is painted with --button-fg, which this
   theme inverts to near black — #08161f on #333333 is 1.45:1, an invisible
   button. An always-dark fill takes an always-white label.
   Covers Django's three surfaces: the submit-row Close, the delete-confirmation
   Cancel, and the date picker's Cancel bar. The :link/:visited duplicates clear
   Django's own `.submit-row a.closelink` and `.delete-confirmation form
   .cancel-link`, which sit at or above the specificity of a bare class. Hover
   needs no counterpart: --close-button-hover-bg is darker still in both. */
.delete-confirmation form .cancel-link,
.delete-confirmation form .cancel-link:link,
.delete-confirmation form .cancel-link:visited,
.calendar-cancel,
.calendar-cancel a,
.calendar-cancel a:link,
.calendar-cancel a:visited {
    color: #ffffff;
}

/* .closelink gets the fill as well as the label, because Django only paints it
   inside a .submit-row: the four RCT confirmation pages put the class on a bare
   anchor, so their Cancel rendered as a naked link while the six RCT form pages
   that do wrap it got the pill. These are Django's own values for the pill, so
   the submit-row copies render identically — height and line-height still come
   from forms.css. (1,2,1) also clears the admin's underline-everything rule. */
#content a.closelink:is(:link, :visited) {
    display: inline-block;
    padding: 10px 15px;
    border-radius: 4px;
    background: var(--close-button-bg);
    color: #ffffff;
    text-decoration: none;
}

#content a.closelink:is(:hover, :focus, :active) {
    background: var(--close-button-hover-bg);
    color: #ffffff;
    text-decoration: none;
}

/* ------------------------------------------------ bulk-create results table */

/* Shared by the bulk-create wizard's results step and BulkCreateJobAdmin's
   read-only "Results" field, which used to inline the same greys itself and so
   rendered near-white text on #ffffff rows in dark mode. One home, themed
   tokens, and the admin field renders classes instead of style attributes. */
.results-table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 20px;
    font-size: 0.8125rem;
}

.results-table th,
.results-table td {
    border: 1px solid var(--hairline-color);
    padding: 8px 12px;
    text-align: left;
    font-family: var(--font-family-monospace);
}

.results-table th {
    background: var(--darkened-bg);
    color: var(--body-fg);
    font-family: inherit;
}

.results-table tbody tr {
    background: var(--body-bg);
}

.results-table tbody tr:nth-child(even) {
    background: var(--darkened-bg);
}

/* The row tints have to outrank the zebra stripe, hence tbody + tr.class. */
.results-table tbody tr.error-row {
    background: var(--medly-danger-soft);
}

.results-table tbody tr.warning-row {
    background: var(--medly-warn-soft);
}

.results-table td.cell-error {
    color: var(--medly-danger);
}

.results-table td.cell-warning {
    color: var(--medly-warn-ink);
}

/* The admin field is inside a fieldset row, so the table needs its own
   horizontal scroll rather than widening the form. */
.results-table-wrap {
    overflow-x: auto;
    max-width: 100%;
}

/* -------------------------------------------------------- command palette */

.medly-palette-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 12px;
    border: 1px solid var(--medly-line);
    border-radius: 999px;
    background: var(--medly-ground);
    color: var(--medly-ink-2);
    font: inherit;
    font-size: 0.8125rem;
    cursor: pointer;
}

.medly-palette-trigger:hover {
    background: var(--medly-surface);
    border-color: var(--medly-accent);
    color: var(--medly-ink);
}

.medly-palette-trigger:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: 1px;
}

.medly-palette-trigger kbd {
    border: 0;
    background: var(--medly-line);
    border-radius: 4px;
    padding: 1px 5px;
    font-size: 10.5px;
    color: var(--medly-ink-2);
}

.medly-palette-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: grid;
    align-items: start;
    justify-items: center;
    padding: 12vh 20px 20px;
    background: rgba(39, 34, 30, 0.45);
}

.medly-palette-overlay[hidden] {
    display: none;
}

body.medly-palette-open {
    overflow: hidden;
}

.medly-palette-dialog {
    width: 100%;
    max-width: 620px;
    background: var(--medly-surface);
    border: 1px solid var(--medly-line);
    border-radius: var(--medly-radius);
    box-shadow: 0 24px 60px -20px rgba(39, 34, 30, 0.45);
    overflow: hidden;
}

.medly-palette-input-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 13px 15px;
    border-bottom: 1px solid var(--medly-line);
}

.medly-palette-input-row svg {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
    color: var(--medly-ink-3);
}

#medly-palette-input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--medly-ink);
    font: inherit;
    font-size: 15px;
    outline: none;
}

.medly-palette-results {
    max-height: 336px;
    overflow-y: auto;
    padding: 6px;
}

.medly-palette-group {
    margin: 9px 0 4px;
    padding: 0 9px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--medly-ink-3);
}

.medly-palette-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 9px;
    border: 0;
    border-radius: var(--medly-radius-sm);
    background: transparent;
    font: inherit;
    font-size: 0.875rem;
    color: var(--medly-ink);
    text-align: left;
    cursor: pointer;
}

.medly-palette-row.is-selected {
    background: var(--medly-accent-soft);
}

.medly-palette-row:focus-visible {
    outline: 2px solid var(--medly-accent);
    outline-offset: -2px;
}

.medly-palette-kind {
    flex: 0 0 auto;
    padding: 2px 5px;
    border-radius: 4px;
    background: var(--medly-line);
    color: var(--medly-ink-3);
    font-size: 9.5px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.medly-palette-kind--console {
    background: var(--medly-accent-soft);
    color: var(--medly-accent-ink);
}

/* Theme tints: the hardcoded pair was a lit chip on the dark ground. The
   palette overlay is hidden until opened, which is why the audit never saw it. */
.medly-palette-kind--action {
    background: var(--medly-warn-soft, #fdebd7);
    color: var(--medly-warn-ink, #9a5c0b);
}

.medly-palette-kind--jump,
.medly-palette-kind--ai {
    background: var(--medly-accent-soft);
    color: var(--medly-accent-ink);
}

/* The name gets the room and gives it up last; the path yields first. All three
   parts used to be `flex: 0 1 auto`, so they shrank in proportion to their own
   content — which meant a long query made the URL the longest string in the row
   and it took ~45% of the width while the name it belongs to was ellipsised.
   Measured on `user:robert@medlyai.com` in a 608px row: name 167px (clipped),
   path 271px (not clipped). */
.medly-palette-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.medly-palette-name mark {
    background: transparent;
    color: var(--medly-accent-ink);
    font-weight: 700;
}

.medly-palette-context {
    font-size: 0.75rem;
    color: var(--medly-ink-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.medly-palette-path {
    /* Shrinks far faster than the name and is capped outright, so it can hint at
       the destination without ever being the reason the label is unreadable.
       `margin-left: auto` still pins it right when there is slack. */
    flex: 0 1000 auto;
    min-width: 0;
    max-width: 30%;
    margin-left: auto;
    overflow: hidden;
    text-overflow: ellipsis;
    font-family: var(--font-family-monospace);
    font-size: 10.5px;
    color: var(--medly-ink-3);
    white-space: nowrap;
}

.medly-palette-footer {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    padding: 9px 14px;
    border-top: 1px solid var(--medly-line);
    background: var(--medly-ground);
    font-size: 0.75rem;
    color: var(--medly-ink-3);
}

/* Pushed to the right so the three key hints stay grouped together. */
.medly-palette-hint {
    margin-left: auto;
}

.medly-palette-hint code {
    font-size: 0.75rem;
    color: var(--medly-ink-2);
}

.medly-palette-empty {
    margin: 0;
    padding: 26px 14px;
    text-align: center;
    font-size: 0.875rem;
    color: var(--medly-ink-3);
}

@media (prefers-reduced-motion: reduce) {
    .medly-nav-caret {
        transition: none;
    }
}
