/* ─── The look ────────────────────────────────────────
   The site's second visual language (Omar, 2026-10-11), kept whole in this one
   file and loaded LAST on every page that links it, so that it can be taken
   off again in one move: remove the <link> to look.css from the pages (or
   empty this file) and the site is exactly as it was on 2026-10-11, commit
   c52ce5a, tagged `before-new-look`. Nothing else was changed to make it:
   no markup, no script, no other stylesheet.

   That is also why rules here repeat selectors from main.css, admin.css,
   dashboard.css and quiz.css instead of those files being edited. Once the
   look has been lived with and kept, fold each block into the file it
   overrides and delete this one.

   What it costs: this file, once per device (it is served with a year's
   cache like every stamped asset). No script, no request to the server
   beyond that, nothing per student or per page view.

   Not linked on purpose: privacy.html, tg/.

   What it is, in one line each:
     1  quiet surfaces and one accent colour
     2  one colour of card; the kind of thing is a small tinted mark
     3  depth from a soft shadow, not an outline
     4  the top bar is a material the page scrolls under
     5  things answer the press, not the hover
     6  the folder's name is the page's title
     7  the list view is one grouped list
     8  lighter controls
     9  watch progress in the brand's blue
    10  a neutral dark mode
    11  no print smaller than can be read
    12  a wash of the brand's light blue behind every page
    13  login, account setup and registration: the form on the page itself
    14  windows arrive: a little larger out of a dimmed, softened page
    15  on a phone, the pledge and a file come up from the bottom as sheets
    16  nothing to press is smaller under a finger than 44 across
    17  the shape of what is coming in place of a spinner
    18  less motion and less transparency for those who ask for it
    19  one ring for whatever the keyboard is on
    20  a touch leaves no blue behind: only the press is felt

   Two things of 2026-10-11 are NOT in this file and stay if it is removed,
   because they are not a matter of looks: the pages ask for the font
   directly instead of through main.css (earlier first paint), and the lesson
   pages set grid-or-list before loading instead of after (so 17 can draw the
   right shape; nothing else can tell the difference). */

/* ── 1 · Colour ──────────────────────────────────────── */
:root {
    --color-primary:        #1863B0;
    --color-primary-light:  #E8F1FB;
    --color-primary-dark:   #0F4C8C;
    --color-primary-hover:  #1F70C4;

    --color-bg:             #F5F8FC;
    --color-card:           #FFFFFF;
    --color-border:         rgba(60, 60, 67, 0.12);
    --color-border-input:   rgba(60, 60, 67, 0.22);

    --color-text:           #1D1D1F;
    --color-text-muted:     #6E6E73;
    --color-text-hint:      #8E8E93;

    --border-radius:        12px;
    --border-radius-lg:     16px;

    /* This file's own. */
    --look-shadow:          0 1px 2px rgba(0, 0, 0, 0.04), 0 6px 20px rgba(0, 0, 0, 0.06);
    --look-shadow-press:    0 1px 2px rgba(0, 0, 0, 0.06);
    --look-bar:             rgba(246, 249, 253, 0.74);
    --look-bar-rest:        rgba(224, 237, 250, 0.72);
    --look-bar-rest-clear:  rgba(224, 237, 250, 0);
    --look-fill:            rgba(118, 118, 128, 0.12);
    --look-hairline:        rgba(60, 60, 67, 0.12);
    --look-ease:            cubic-bezier(0.32, 0.72, 0, 1);
    --look-wash:            #DCEBFA;
    --look-wash-clear:      rgba(220, 235, 250, 0);

    --tint-video:           #1863B0;
    --tint-file:            #D9453B;
    --tint-quiz:            #6B46C1;
    --tint-image:           #2E9E5B;
}

/* ── 10 · Dark mode ──────────────────────────────────── */
:root[data-theme="dark"] {
    --color-primary:        #5AA9F0;
    --color-primary-light:  rgba(90, 169, 240, 0.16);
    --color-primary-dark:   #8CC4F7;
    --color-primary-hover:  #74B8F4;

    --color-bg:             #000000;
    --color-card:           #1C1C1E;
    --color-border:         rgba(84, 84, 88, 0.6);
    --color-border-input:   rgba(84, 84, 88, 0.9);

    --color-text:           #F5F5F7;
    --color-text-muted:     #A1A1A6;
    --color-text-hint:      #6E6E73;

    /* A shadow cannot be seen on black: a card is told from the page by a
       faint edge instead. */
    --look-shadow:          0 0 0 1px rgba(255, 255, 255, 0.07);
    --look-shadow-press:    0 0 0 1px rgba(255, 255, 255, 0.07);
    --look-bar:             rgba(12, 16, 22, 0.74);
    --look-bar-rest:        rgba(14, 30, 48, 0.72);
    --look-bar-rest-clear:  rgba(14, 30, 48, 0);
    --look-fill:            rgba(118, 118, 128, 0.24);
    --look-hairline:        rgba(84, 84, 88, 0.6);
    --look-wash:            #10253B;
    --look-wash-clear:      rgba(16, 37, 59, 0);

    --tint-video:           #5AA9F0;
    --tint-file:            #FF6961;
    --tint-quiz:            #B9A3F5;
    --tint-image:           #5BD08A;
}

/* ── 12 · The wash ───────────────────────────────────────
   The site was light blue from its first day. This keeps that and makes it
   one soft light from the top of the screen, fading into the page's own
   near-white (Omar, 2026-10-11, of the login design he chose: "retains the
   og site's light blue identity but in a newer approach").

   It is a layer of its own, fixed behind the page, and not a
   `background-attachment: fixed` on the body: that one is repainted on every
   scroll and is not honoured on an iPhone at all. A fixed layer is painted
   once and only moved. It fades to the same blue made clear, not to
   `transparent`, which would pass through grey on the way. */
body::before {
    content:        "";
    position:       fixed;
    inset:          0;
    z-index:        -1;
    pointer-events: none;
    background:     radial-gradient(130% 55% at 50% 0%, var(--look-wash) 0%, var(--look-wash-clear) 62%);
}

/* ── 4 · The top bar ─────────────────────────────────────
   The bar must not sit on the wash as a strip of its own colour: a white,
   outlined bar across the top cut the light off at its source (Omar,
   2026-10-11: "fixed in colour and kinda diminishes the effect"). So at the
   top of a page the bar has no colour and no line at all, and the wash runs
   through it. It becomes a material, with its hair's line, only once the
   page has been scrolled and there is something under it to stand apart
   from. That is done by the scroll position driving a CSS animation: no
   script, nothing listening.

   A browser that cannot tie an animation to scrolling (an iPhone or iPad
   before iOS 26, Firefox) gets the first rule alone: a bar tinted the wash's
   own blue, thinning to nothing at its lower edge, with no line. On the wash
   it has no edge to be seen by, and over a card it is still a tinted glass.

   The blur is plain: it used to raise the colour under it as well
   (saturate), which on a blue wash painted the bar a stronger blue than the
   page. .admin-topbar is the same bar on mom's phone. Where she has asked
   her device for less transparency, both are solid again. */
.navbar,
.admin-topbar {
    background-color:        transparent;
    background-image:        linear-gradient(to bottom, var(--look-bar-rest) 0%, var(--look-bar-rest) 45%, var(--look-bar-rest-clear) 100%);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter:         blur(20px);
    border-bottom:           0.5px solid transparent;
}

@keyframes look-bar-settles {
    to {
        background-color:    var(--look-bar);
        border-bottom-color: var(--look-hairline);
    }
}

@supports (animation-timeline: scroll()) {
    .navbar,
    .admin-topbar {
        background-image:   none;
        animation:          look-bar-settles linear both;
        animation-timeline: scroll(root block);
        animation-range:    0 72px;
    }
}

@media (prefers-reduced-transparency: reduce) {
    .navbar,
    .admin-topbar {
        background-color:        var(--color-card);
        background-image:        none;
        border-bottom-color:     var(--look-hairline);
        -webkit-backdrop-filter: none;
        backdrop-filter:         none;
        animation:               none;
    }
}

.navbar__link {
    border-radius: 999px;
}

.navbar__link.active {
    background-color: var(--look-fill);
    color:            var(--color-text);
}

/* ── 5 and 8 · Buttons ───────────────────────────────────
   A button gives a little under the finger the moment it is pressed. The
   lift on hover is gone: most students are on a phone or an iPad, where
   nothing hovers. The small sizes (.btn-sm, .btn-xs) keep their height:
   they sit in table rows and toolbars that were measured for it. */
.btn {
    font-weight: 600;
    transition:  transform 120ms ease-out, background-color 160ms ease, color 160ms ease, opacity 160ms ease;
}

.btn:not(.btn-sm):not(.btn-xs) {
    min-height: 44px;
}

.btn:active {
    transform: scale(0.97);
}

.btn-outline {
    border-color:     transparent;
    background-color: var(--look-fill);
}

.btn-outline:hover {
    background-color: var(--color-primary-light);
}

.theme-toggle {
    border:           0;
    background-color: var(--look-fill);
}

.course-type-btn {
    border-radius: 14px;
    transition:    transform 120ms ease-out, background-color 160ms ease;
}

.course-type-btn:hover  { transform: none; }
.course-type-btn:active { transform: scale(0.98); }

/* The way back out of a folder reads as "back", not as one more box. */
.drive-actions #backBtn {
    background-color: transparent;
    padding-inline:   0.25rem;
    min-height:       36px;
    font-weight:      500;
}

/* Grid or list: a switch with a thumb, not two boxes. */
.view-toggle {
    background-color: var(--look-fill);
    border-radius:    10px;
    padding:          2px;
    gap:              2px;
}

.view-toggle__icon,
.view-toggle__icon:first-child,
.view-toggle__icon:last-child {
    border:        0;
    border-radius: 8px;
    width:         2.3rem;
    height:        2rem;
}

.view-toggle__icon.active {
    background-color: var(--color-card);
    color:            var(--color-text);
    box-shadow:       0 1px 3px rgba(0, 0, 0, 0.14);
}

/* ── 3 · Cards ───────────────────────────────────────── */
.course-card,
.drive-card,
.card,
.stat-card,
.quick-action-card,
.admin-course-card {
    border:        0;
    border-radius: 16px;
    box-shadow:    var(--look-shadow);
}

.course-card,
.drive-card {
    transition: transform 140ms ease-out, box-shadow 160ms ease;
}

.course-card:hover,
.drive-card:hover {
    transform:  none;
    box-shadow: var(--look-shadow);
}

.course-card:active,
.drive-card:active {
    transform:  scale(0.975);
    box-shadow: var(--look-shadow-press);
}


/* A course: its name where reading starts, and a mark that it opens. */
.course-card {
    justify-content: space-between;
    min-height:      96px;
    padding:         1.4rem 1.25rem;
}

.course-card__name {
    margin:     0;
    font-size:  1.05rem;
    text-align: start;
}

.course-card::after {
    content:     "\2039";
    flex-shrink: 0;
    color:       var(--color-text-hint);
    font-size:   1.6rem;
    line-height: 1;
}

/* ── 2 · One colour of card ──────────────────────────────
   The four solid bars (blue folder, red PDF, purple quiz, green picture)
   become one white card each, and what kind of thing it is, a small tinted
   square around the icon it already had. !important only where the old
   colour is itself a class of the same weight in a file loaded earlier. */
.drive-card__card-header {
    background: transparent !important;
    color:      var(--color-text);
    font-size:  0.875rem;
    height:     auto;
    min-height: 44px;
    gap:        0.6rem;
    padding:    0.5rem 0.75rem;
}

/* A name may take two lines here as well (a folder's, three): the letters
   are larger than they were, and the card no wider. */
.drive-card__card-header span:not(.drive-sched-badge) {
    white-space:        normal;
    line-height:        1.45;
    display:            -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.drive-card__card-header--folder-full span:not(.drive-sched-badge) {
    -webkit-line-clamp: 3;
}

.drive-card__card-header svg {
    box-sizing:    content-box;
    width:         16px;
    height:        16px;
    padding:       6px;
    border-radius: 8px;
}

.drive-card__card-header--folder svg { color: var(--tint-video); background: color-mix(in srgb, var(--tint-video) 14%, transparent); }
.drive-card__card-header--pdf    svg { color: var(--tint-file);  background: color-mix(in srgb, var(--tint-file)  14%, transparent); }
.drive-card__card-header--quiz   svg { color: var(--tint-quiz);  background: color-mix(in srgb, var(--tint-quiz)  14%, transparent); }
.drive-card__card-header--image  svg { color: var(--tint-image); background: color-mix(in srgb, var(--tint-image) 14%, transparent); }

.drive-card--folder {
    min-height: 64px;
}

.drive-card__card-body--folder,
.quiz-card__body {
    background: transparent;
    border-top: 0.5px solid var(--look-hairline);
}

.drive-card__card-body--folder svg {
    stroke: var(--tint-file);
}

.quiz-card__status--new,
.quiz-card__pill--new {
    color: var(--tint-quiz);
}

/* ── 9 and 11 · A lesson's card ──────────────────────── */
.vcard__thumb {
    border-radius: 16px 16px 0 0;
}

.vcard__meta {
    padding: 0.7rem 0.8rem 0.8rem;
}

.vcard__name {
    font-size:   0.95rem;
    font-weight: 700;
}

.vcard__status {
    font-size: 0.8rem;
}

.vcard__duration {
    font-size:     0.75rem;
    border-radius: 6px;
    padding:       0.22rem 0.4rem;
}

/* Blue, the brand's: red on a lesson reads as something gone wrong. */
.vcard__prog {
    height: 4px;
}

.vcard__prog i {
    background: var(--color-primary);
}

.quiz-card__count {
    font-size: 0.85rem;
}

/* ── 7 · The list view ───────────────────────────────────
   One rounded panel with a hair's line between its rows, in place of a
   card per row. */
.drive-list {
    gap:              0;
    background-color: var(--color-card);
    border-radius:    16px;
    box-shadow:       var(--look-shadow);
    overflow:         hidden;
}

.drive-list:empty {
    display: none;
}

.drive-list.drive-folders-section {
    margin-bottom: 1.25rem;
}

.drive-list-item {
    border:           0;
    border-radius:    0;
    background-color: transparent;
    min-height:       56px;
    padding:          0.6rem 1rem;
    transition:       background-color 120ms ease;
}

.drive-list-item:hover {
    transform:  none;
    box-shadow: none;
}

.drive-list-item + .drive-list-item,
.drive-list-item + .drive-list-item:hover {
    box-shadow: inset 0 0.5px 0 var(--look-hairline);
}

.drive-list-item:active {
    background-color: var(--look-fill);
}

.drive-list-item__icon {
    width:         2.1rem;
    height:        2.1rem;
    border-radius: 9px;
}

.drive-list-item__icon--folder,
.drive-list-item__icon--video,
.drive-list-item--folder:hover .drive-list-item__icon--folder {
    color:            var(--tint-video);
    background-color: color-mix(in srgb, var(--tint-video) 14%, transparent);
}

.drive-list-item__icon--pdf   { color: var(--tint-file);  background-color: color-mix(in srgb, var(--tint-file)  14%, transparent); }
.drive-list-item__icon--quiz  { color: var(--tint-quiz);  background-color: color-mix(in srgb, var(--tint-quiz)  14%, transparent); }
.drive-list-item__icon--image { color: var(--tint-image); background-color: color-mix(in srgb, var(--tint-image) 14%, transparent); }

.drive-list-item__arrow {
    color:     var(--color-text-hint);
    font-size: 0.9rem;
}

/* Mom's selection and drag marks are outlines of their own (admin.css) and
   must stay what they are. */
.drive-card.selected,
.drive-list-item.selected {
    box-shadow: 0 0 0 3px rgba(24, 95, 165, 0.15);
}
/* ── 6 · Where she is ────────────────────────────────────
   The folders above stay a small line of links; the one on screen takes a
   line of its own, as the page's title. The arrow that led to it goes: a
   title needs none. (:has is not in every old browser; there the arrow
   simply stays.) */
.drive-breadcrumb {
    font-size:     0.9rem;
    row-gap:       0.15rem;
    margin-bottom: 1rem;
}

.drive-crumb {
    color: var(--color-primary);
}

.drive-crumb.active {
    flex-basis:  100%;
    font-size:   1.6rem;
    font-weight: 700;
    line-height: 1.35;
    color:       var(--color-text);
}

.drive-crumb-sep:has(+ .drive-crumb.active) {
    display: none;
}

@media (max-width: 768px), (max-height: 500px) and (orientation: landscape) {
    .drive-crumb.active {
        font-size: 1.35rem;
    }
}

/* ── Those who ask their device for less motion ──────── */
@media (prefers-reduced-motion: reduce) {
    .btn:active,
    .course-card:active,
    .drive-card:active,
    .course-type-btn:active {
        transform: none;
    }
}

/* ── 13 · Login, account setup, registration ─────────────
   The form stands on the page itself, on the wash, with no card round it
   (the one of four designs Omar chose, 2026-10-11). The line under the logo
   is the page's title. These rules only meet the three pages that have an
   .auth-card or a .register-card. */
.card.auth-card {
    max-width:  380px;
    padding:    0 1.5rem;
    border:     0;
    box-shadow: none;
    background: transparent;
}

.auth-card__logo {
    height:        62px;
    margin-bottom: 1.4rem;
}

.auth-card__header {
    margin-bottom: 2rem;
}

.auth-card__header .text-muted {
    font-size:   1.55rem;
    font-weight: 700;
    line-height: 1.4;
    color:       var(--color-text);
}

.auth-card .divider {
    display: none;
}

.auth-card .form-label {
    font-size: 0.85rem;
    color:     var(--color-text-muted);
}

.auth-card .form-input {
    height:           52px;
    border-radius:    14px;
    border-color:     transparent;
    background-color: var(--look-fill);
}

.auth-card .form-input:focus {
    border-color:     var(--color-primary);
    background-color: var(--color-card);
}

.auth-card .btn-lg {
    min-height:    52px;
    margin-top:    0.75rem;
    border-radius: 999px;
}

/* These pages have no top bar, yet kept the room for one above the form
   (.page-center, main.css): up to 90 of a laptop's 650. They take the whole
   screen and stand in its middle. (:has is not in every old browser; there
   the room simply stays.) */
.page-center:has(> .auth-card) {
    min-height:  100vh;
    min-height:  100dvh;
    padding:     1rem 0;
}

/* A laptop's screen is wide but not tall, and what is left of it under the
   browser's own bars is about 650, less again once the page is enlarged to
   fill its width: the account-setup form, the longest of these, ran off the
   bottom with its button out of sight. On a wide screen that is not a tall
   one, everything here is a size smaller. */
@media (min-width: 769px) and (max-height: 1000px) {
    .auth-card__logo               { height: 44px; margin-bottom: 0.8rem; }
    .auth-card__header             { margin-bottom: 1rem; }
    .auth-card__header .text-muted { font-size: 1.25rem; }
    .auth-card .form-group         { margin-bottom: 0.8rem; }
    .auth-card .form-input         { height: 44px; }
    .auth-card .btn-lg             { min-height: 46px; margin-top: 0.4rem; }
    .auth-card .setup-greeting     { margin-bottom: 0.25rem; }
    .auth-card .setup-intro        { margin-bottom: 0.8rem; font-size: 0.85rem; }
    .auth-card .phone-chip         { margin-bottom: 0.9rem; padding: 0.5rem 1rem; }
}
/* Account setup: the number she registered with, on a field's grey. */
.auth-card .phone-chip {
    border-radius:    14px;
    background-color: var(--look-fill);
}

/* Registration: the three choices are the site's cards. */
.register-option {
    border:     0;
    box-shadow: var(--look-shadow);
    transition: transform 140ms ease-out, box-shadow 160ms ease;
}

.register-option:hover {
    transform:  none;
    box-shadow: var(--look-shadow);
}

.register-option:active {
    transform:  scale(0.98);
    box-shadow: var(--look-shadow-press);
}

@media (prefers-reduced-motion: reduce) {
    .register-option:active {
        transform: none;
    }
}

/* ── 14 · Windows arrive ─────────────────────────────────
   A window used to slide up 12px on a plain fade. It now comes forward out
   of a page that dims and goes soft behind it, on one curve for every window
   (fast away, long gentle settle: the curve iOS draws its sheets on).

   The open state is still a transform, an identity one, never `none`: the
   quiz editor measures the card being dragged from the window, which only
   holds while the window has a transform (quiz.css, .quiz-sort-ghost).

   The page behind is not softened under the video player: a blur layer under
   a playing protected video is the one place it could cost a weak phone
   frames, and the player's own black frame is what is looked at there. */
.modal-overlay {
    transition: opacity 220ms ease, visibility 220ms ease;
}

.modal-overlay:not(#playerModal) {
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter:         blur(6px);
}

.modal {
    border:     0;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.24), var(--look-shadow);
    transform:  translateY(8px) scale(0.97);
    transition: transform 340ms var(--look-ease);
}

.modal-overlay.open .modal {
    transform: translateY(0) scale(1);
}

/* ── 15 · Sheets on a phone ──────────────────────────────
   The three windows a student only reads or looks at: the pledge, a PDF, a
   picture. They come up from the bottom edge, where the thumb is, as wide as
   the screen. Windows with something to type in are left in the middle of
   the screen on purpose: a sheet at the bottom edge is where the keyboard
   comes up. There is no handle drawn on them: a handle promises a drag, and
   these close by their own buttons. */
@media (max-width: 768px) {
    #pledgeModal,
    #pdfModal,
    #imageModal {
        align-items: flex-end;
        padding:     0;
    }

    #pledgeModal .modal,
    #pdfModal .modal,
    #imageModal .modal {
        width:          100%;
        max-width:      100%;
        max-height:     92vh;
        max-height:     92dvh;
        margin:         0;
        border-radius:  22px 22px 0 0;
        padding-bottom: env(safe-area-inset-bottom);
        transform:      translateY(100%);
    }

    #pledgeModal.open .modal,
    #pdfModal.open .modal,
    #imageModal.open .modal {
        transform: translateY(0);
    }

    #imageModal .modal img {
        max-width: 100% !important;
        margin:    0 auto;
    }
}

/* ── 16 · Under a finger ─────────────────────────────────
   The close mark of a window was 30 across, the theme switch 36, a small
   button 34. On a touch screen each now answers over at least 44 by 44,
   without being drawn any larger: the extra is an unseen margin of its own,
   so no row, toolbar or table was re-measured. The close mark alone is also
   drawn a little larger, 36. */
.modal__close {
    width:            36px;
    height:           36px;
    border-radius:    50%;
    background-color: var(--look-fill);
}

@media (pointer: coarse) {
    .btn-sm,
    .btn-xs,
    .modal__close,
    .theme-toggle,
    .hamburger,
    .view-toggle,
    .nav-sidebar__close,
    .admin-sidebar__close,
    .quiz-modal__x {
        position: relative;
    }

    .btn-sm::before,
    .btn-xs::before,
    .modal__close::before,
    .theme-toggle::before,
    .hamburger::before,
    .view-toggle::before,
    .nav-sidebar__close::before,
    .admin-sidebar__close::before,
    .quiz-modal__x::before,
    .player-shrink::before {
        content:    "";
        position:   absolute;
        top:        50%;
        left:       50%;
        width:      max(100%, 44px);
        height:     max(100%, 44px);
        transform:  translate(-50%, -50%);
    }
}

/* ── 17 · The shape of what is coming ────────────────────
   While a folder or the list of courses is being fetched, the page shows
   grey shapes where the cards will be, in place of a spinner in an empty
   page: the same wait reads as shorter, and nothing jumps when the cards
   arrive. Only the three loading places that stand before a grid of cards;
   every other spinner (tables, results) is as it was.

   It is one box and its shadows: a shadow with no blur is a copy of the box,
   corners and all, so one element draws two rows of cards. Their size comes
   from the place's own width (cqw), which is why this is offered only to
   browsers that have it and :has (2022 on); the rest keep the spinner. The
   column counts are the grids' own (admin.css, dashboard.css). */
@keyframes look-waiting {
    from { opacity: 1; }
    to   { opacity: 0.5; }
}

@supports (width: 1cqw) and selector(:has(a)) {
    .loading-overlay:has(~ .drive-grid),
    .loading-overlay:has(~ .drive-list),
    .loading-overlay:has(~ .course-list-grid) {
        --sk-cols:      5;
        --sk-gap:       1rem;
        --sk-w:         calc((100cqw - (var(--sk-cols) - 1) * var(--sk-gap)) / var(--sk-cols));
        --sk-h:         calc(var(--sk-w) * 0.82);
        --sk-dx:        calc(var(--sk-w) + var(--sk-gap));
        --sk-dy:        calc(var(--sk-h) + var(--sk-gap));
        display:        block;
        position:       relative;
        container-type: inline-size;
        height:         calc(2 * var(--sk-h) + var(--sk-gap));
        padding:        0;
        overflow:       hidden;
        animation:      look-waiting 900ms ease-in-out infinite alternate;
    }

    .loading-overlay:has(~ .drive-grid) .spinner,
    .loading-overlay:has(~ .drive-list) .spinner,
    .loading-overlay:has(~ .course-list-grid) .spinner {
        display: none;
    }

    .loading-overlay:has(~ .drive-grid)::before,
    .loading-overlay:has(~ .drive-list)::before,
    .loading-overlay:has(~ .course-list-grid)::before {
        content:          "";
        position:         absolute;
        top:              0;
        left:             0;
        width:            var(--sk-w);
        height:           var(--sk-h);
        border-radius:    16px;
        background-color: var(--look-fill);
        box-shadow:       calc(1 * var(--sk-dx)) 0 0 var(--look-fill),
                          calc(2 * var(--sk-dx)) 0 0 var(--look-fill),
                          calc(3 * var(--sk-dx)) 0 0 var(--look-fill),
                          calc(4 * var(--sk-dx)) 0 0 var(--look-fill),
                          0                      var(--sk-dy) 0 var(--look-fill),
                          calc(1 * var(--sk-dx)) var(--sk-dy) 0 var(--look-fill),
                          calc(2 * var(--sk-dx)) var(--sk-dy) 0 var(--look-fill),
                          calc(3 * var(--sk-dx)) var(--sk-dy) 0 var(--look-fill),
                          calc(4 * var(--sk-dx)) var(--sk-dy) 0 var(--look-fill);
    }

    /* The list view: one panel, as the list itself is. */
    .loading-overlay:has(~ #driveGrid.drive-list) {
        height: 300px;
    }

    .loading-overlay:has(~ #driveGrid.drive-list)::before {
        width:      100%;
        height:     100%;
        box-shadow: none;
    }

    /* The list of courses: three wide cards across, 96 tall. */
    .loading-overlay:has(~ .course-list-grid) {
        --sk-cols: 3;
        --sk-gap:  1.25rem;
        --sk-h:    96px;
    }

    @media (max-width: 1024px) {
        .loading-overlay:has(~ .drive-grid) { --sk-cols: 4; }
    }

    @media (max-width: 768px), (max-height: 500px) and (orientation: landscape) {
        .loading-overlay:has(~ .drive-grid)        { --sk-cols: 3; }
        .loading-overlay:has(~ .course-list-grid)  { --sk-cols: 2; }
    }

    @media (max-width: 480px) {
        .loading-overlay:has(~ .drive-grid)        { --sk-cols: 2; }
        .loading-overlay:has(~ .course-list-grid)  { --sk-cols: 1; }
    }
}

/* ── 18 · For those who ask for less ─────────────────────
   Less motion: windows and sheets no longer travel, they fade in where they
   stand (the fade is the overlay's, and stays); the waiting shapes hold
   still; a link to a place on the page jumps there. Less transparency: the
   page behind a window is dimmed more and not softened. The top bar and the
   pressed buttons answer the same two settings further up. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    .modal,
    .modal-overlay.open .modal,
    #pledgeModal .modal,
    #pdfModal .modal,
    #imageModal .modal {
        transform:  translateY(0) scale(1);
        transition: none;
    }

    .loading-overlay {
        animation: none !important;
    }
}

@media (prefers-reduced-transparency: reduce) {
    .modal-overlay,
    .modal-overlay:not(#playerModal) {
        background-color:        rgba(0, 0, 0, 0.6);
        -webkit-backdrop-filter: none;
        backdrop-filter:         none;
    }
}

/* ── 19 · Where the keyboard is ──────────────────────────
   One ring, in the site's blue, round whatever the keyboard has reached,
   and only for the keyboard: a click or a tap draws none. :where carries no
   weight, so any ring a component draws for itself still wins. Fields keep
   their own (a border and a glow, main.css). */
:where(a, button, summary, [tabindex]:not([tabindex="-1"])):focus-visible {
    outline:        2px solid var(--color-primary);
    outline-offset: 2px;
}

:where(.btn, .course-type-btn):focus-visible {
    outline-offset: 3px;
}

/* ── 20 · A touch leaves no blue behind ──────────────────
   On a touch screen a tap drew blue twice, and neither was the button's own
   doing (Omar, 2026-10-13: "ruins the feel of the buttons and makes it feel
   static").

   One was the browser's: a flash of its own blue over anything that can be
   tapped. It is switched off for the whole page (the property is inherited).

   The other was this site's hover colours. A finger does not hover, so a
   phone puts the hover state on whatever was last tapped and LEAVES it there
   until something else is tapped: the menu button, a link in the bar or a
   grey button stayed light blue after the finger had gone. On a screen that
   cannot hover, each of those is given back the colours it has at rest. What
   answers a touch is the press itself (5), and the small controls that had
   only a hover colour get a press of their own here.

   Rules a mouse still gets are untouched: all of this is inside
   (hover: none). */
html {
    -webkit-tap-highlight-color: transparent;
}

@media (hover: none) {
    .btn-primary:hover          { background-color: var(--color-primary);       border-color: var(--color-primary); }
    .btn-secondary:hover        { background-color: var(--color-primary-light); border-color: var(--color-primary-light); }
    .btn-outline:hover          { background-color: var(--look-fill); }
    .btn-danger:hover           { background-color: var(--color-danger-light);  border-color: var(--color-danger-light); }
    .btn.quiz-btn:hover         { background-color: var(--color-primary); }
    .course-type-btn:hover      { background-color: var(--color-primary); }

    .quiz-modal .btn.quiz-btn:hover:not(:disabled) { background-color: var(--quiz-solid); border-color: var(--quiz-solid); }
    .quiz-modal .btn-outline:hover                 { background-color: transparent; }
    .quiz-q__ref:hover                             { background: none; }
    .quiz-modal__x:hover                           { background: rgba(255, 255, 255, 0.16); }

    .navbar__link:not(.active):hover,
    .nav-sidebar__link:not(.active):hover,
    .admin-sidebar__link:not(.active):hover {
        background-color: transparent;
        color:            var(--color-text-muted);
    }

    .theme-toggle:hover         { background-color: var(--look-fill); color: var(--color-text-muted); }
    .hamburger:hover            { background-color: transparent; }
    .modal__close:hover         { background-color: var(--look-fill); color: var(--color-text-muted); }
    .nav-sidebar__close:hover,
    .admin-sidebar__close:hover { background-color: transparent; }
    .modal-nav:hover            { background: rgba(0, 0, 0, 0.45); }
    .input-toggle:hover         { color: var(--color-text-muted); }

    .table tbody tr:hover       { background-color: transparent; }
    .quick-action-card:hover    { background-color: var(--color-bg); }
    .card-clickable:hover       { transform: none; }

    .drive-list-item--folder:hover .drive-list-item__arrow { color: var(--color-text-hint); transform: none; }
    .register-option:hover .register-option__arrow          { transform: none; }

    /* The press, for what had a hover colour and nothing else. */
    .hamburger,
    .theme-toggle,
    .modal__close,
    .navbar__link,
    .nav-sidebar__link,
    .admin-sidebar__link {
        transition: transform 120ms ease-out, background-color 160ms ease, color 160ms ease;
    }

    .hamburger:active,
    .theme-toggle:active,
    .modal__close:active {
        transform: scale(0.9);
    }

    .navbar__link:active,
    .nav-sidebar__link:active,
    .admin-sidebar__link:active {
        background-color: var(--look-fill);
    }
}

@media (hover: none) and (prefers-reduced-motion: reduce) {
    .hamburger:active,
    .theme-toggle:active,
    .modal__close:active {
        transform: none;
    }
}