/**
 * App-specific additions on top of theme.css (Adminator's own CSS, kept as
 * an untouched copy). Adminator's own tables only ever hold short content
 * (names, dates, badges), so .data-table th/td are deliberately
 * white-space:nowrap there. This app's tables hold real free text (audit
 * actions, email subjects, record names) that can run much longer — without
 * this, one long value forces its column, and the whole table, wider than
 * its card.
 *
 * Each column already gets an explicit width from components/table.blade.php
 * (10% by default, or whatever a screen overrides it to). This just makes a
 * value that doesn't fit that width wrap onto extra lines instead of forcing
 * the column past it.
 */
.data-table th,
.data-table td {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/**
 * components/permission-matrix.blade.php (Step 7.5) — the Add/Edit role
 * screens' checkbox table. A fixed heading-column width keeps every row's
 * module name lined up regardless of how long its own name is.
 *
 * Bootstrap's own .table-light (previously used here) hardcodes a light
 * background and black text via --bs-table-* variables, which never look
 * at this app's data-theme attribute — it stayed light even in dark mode.
 * --primary-soft is theme.css's own light/dark-aware soft-blue tint
 * (#eff6ff light / #0f2847 dark), so this now switches correctly.
 *
 * `th.permission-matrix-heading`, not just `.permission-matrix-heading` —
 * Bootstrap's own `.table>:not(caption)>*>*` rule (which sets every cell's
 * background) has the exact same specificity as a bare class selector, and
 * bootstrap.min.css loads before this file, so a same-specificity rule
 * here still loses to source order alone. Adding the element selector
 * matches Bootstrap's specificity exactly, so this file's later position
 * in <head> is what wins the tie.
 */
th.permission-matrix-heading {
    width: 220px;
    white-space: nowrap;
    background-color: var(--primary-soft);
}

/**
 * form/{input,select,textarea}.blade.php now always render their
 * #{id}-error div, even with nothing in it, so an AJAX-submitted form
 * (app-shell.js) always has somewhere to write an error into. theme.css's
 * own .field-error is display:flex, which — unlike a div that simply isn't
 * in the DOM at all — still takes up a line's height with no text in it.
 * This restores "no error means no gap", the same as before that change.
 */
.field-error:empty {
    display: none;
}

/**
 * Step 8 (Course offerings) — theme.css only ships an outline button style
 * for primary/success/danger. Any other .btn--outline-* class just renders
 * with no border and no color at all, since the rule doesn't exist —
 * "Back to draft" and "Copy" looked like plain text because of this, not
 * because of anything specific to how they were marked up.
 *
 * These two extend the exact same outline pattern theme.css already uses
 * (transparent background, border-color and text in the theme color, hover
 * fills with a light/dark-aware tint) for the two colors offerings needs
 * beyond what theme.css ships: info (matches the Draft status badge, used
 * by "Back to draft") and a medium-dark grey for "Copy" — not a status
 * transition, so it deliberately doesn't borrow any status badge's color.
 * --t-muted/--bg-hover are theme.css's own light/dark-aware muted-text and
 * hover-background tokens, not new colors invented here.
 */
.btn--outline-info {
    background: transparent;
    border-color: var(--info);
    color: var(--info);
}

.btn--outline-info:hover {
    background: var(--info-soft);
}

.btn--outline-grey {
    background: transparent;
    border-color: var(--t-muted);
    color: var(--t-muted);
}

.btn--outline-grey:hover {
    background: var(--bg-hover);
}

.hero-title .badge {
    font-size: 14px;
    padding: 4px 10px;
}

/* A removable pill — the course search page's active-filter chips. (It
   originally belonged to the chip-select widget too; that widget is now a
   Tom Select multi-select, styled with the other searchable dropdowns.) */
.chip {
    align-items: center;
    background: var(--bg-muted);
    border-radius: 999px;
    color: var(--t-base);
    display: inline-flex;
    font-size: 12px;
    font-weight: 500;
    gap: 4px;
    line-height: 1.4;
    padding: 4px 4px 4px 10px;
}

/**
 * Step 12's student profile tabs — a wizard-style stepper (icon circle,
 * connecting line, label underneath), replacing the earlier plain
 * Bootstrap nav-tabs look on direct request.
 *
 * The connecting line is a single ::before per item (skipped on the last),
 * starting at this item's own horizontal centre and spanning its full
 * width — since every item is an equal-width flex column, "100% of this
 * item's width, starting at its centre" lands exactly on the centre of the
 * *next* item, without measuring anything in JS. The circle sits on top
 * (its own solid background, higher z-index) and naturally masks the short
 * stretch of line that would otherwise run directly behind it.
 *
 * Horizontal scroll, not wrap, is kept from the previous version for the
 * same reason it was added there: between roughly 720px and 1100px the
 * sidebar is in its icon-only collapsed state, narrow enough that three
 * full-size steps don't fit on one line, and a wrapped flex item can end up
 * sized down to something that no longer reads as a control at all.
 */
#profile-tabs {
    display: flex;
    flex-wrap: nowrap;
    list-style: none;
    margin: 0 0 4px;
    padding: 4px 4px 0;
}

/* No min-width on purpose — with exactly three equal-flex items, the row
   always divides the container's own width three ways and never overflows
   it, at any screen size, so there's nothing here for #profile-tabs to
   ever need to scroll. A fixed minimum (140px) was tried first and caused
   exactly that on a narrow phone (three columns wanting 420px in a ~340px
   viewport) — found live, reported with a screenshot showing the resulting
   horizontal scrollbar. min-width: 0 overrides a flex item's own default
   floor (its content's min-content size), which the short one/two-word
   labels here don't actually need. */
.profile-stepper__item {
    flex: 1;
    min-width: 0;
    position: relative;
}

.profile-stepper__item:not(:last-child)::before {
    background: var(--border);
    content: '';
    height: 2px;
    left: 50%;
    position: absolute;
    top: 24px;
    width: 100%;
    z-index: 0;
}

/* Same layout, smaller everything — a 48px circle plus its own padding is
   generous on a phone-width screen, and shrinking it (rather than just
   letting flex compress the columns around a fixed circle size) keeps the
   whole step looking deliberately sized for the screen, not squeezed. The
   connecting line's own `top` is re-set to match the smaller circle's new
   half-height. */
@media (max-width: 575.98px) {
    .profile-stepper__item:not(:last-child)::before {
        top: 18px;
    }

    .profile-stepper__circle {
        height: 36px;
        width: 36px;
    }

    /* Direct child only — .profile-stepper__status's own icon (the tick/
       alert/lock mark) is a grandchild of .profile-stepper__circle, not a
       child, so it's already excluded and stays its own small size. A CSS
       width/height always wins over the icon's own inline SVG attributes
       (SVG presentation attributes sit below CSS in the cascade), so this
       shrinks the circle's main icon with no Blade change needed. */
    .profile-stepper__circle > svg {
        height: 16px;
        width: 16px;
    }

    .profile-stepper__status {
        height: 15px;
        width: 15px;
    }

    .profile-stepper__label {
        font-size: 11.5px;
    }
}

.profile-stepper__btn {
    align-items: center;
    background: transparent;
    border: 0;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 0 8px 4px;
    position: relative;
    width: 100%;
    z-index: 1;
}

.profile-stepper__btn:disabled {
    cursor: not-allowed;
}

.profile-stepper__btn--static {
    cursor: default;
    pointer-events: none;
}

.profile-stepper__btn:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.profile-stepper__circle {
    align-items: center;
    background: var(--bg-card);
    border: 2px solid var(--border);
    border-radius: 50%;
    color: var(--t-light);
    display: flex;
    height: 48px;
    justify-content: center;
    position: relative;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
    width: 48px;
}

.profile-stepper__btn:hover:not(:disabled):not(.active) .profile-stepper__circle {
    border-color: var(--primary);
    color: var(--primary);
}

/* The active step is the one piece of state a student must never have to
   guess — a solid fill, not a border tint alone (WCAG 2.1 AA — CLAUDE.md's
   own accessibility rule, "colour is never the only way something is
   shown"); the label below picks up the same colour too. */
.profile-stepper__btn.active .profile-stepper__circle {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

.profile-stepper__btn.active .profile-stepper__label {
    color: var(--primary);
}

.profile-stepper__btn:disabled .profile-stepper__circle {
    color: var(--t-light);
    opacity: .6;
}

.profile-stepper__label {
    color: var(--t-muted);
    font-size: 13px;
    font-weight: 600;
    text-align: center;
}

/* Data-completion status — independent of which step is active, so it
   stays visible on every step at once, not just the open one. A small
   badge on the circle's own edge, coloured AND shaped differently (a tick
   vs. an exclamation mark, never colour alone) so it reads correctly even
   without colour vision. */
.profile-stepper__status {
    align-items: center;
    border: 2px solid var(--bg-card);
    border-radius: 50%;
    bottom: -2px;
    display: flex;
    height: 18px;
    justify-content: center;
    position: absolute;
    right: -2px;
    width: 18px;
}

.profile-stepper__status--complete {
    background: var(--success);
    color: #fff;
}

.profile-stepper__status--incomplete {
    background: var(--warning);
    color: #fff;
}

.profile-stepper__status--locked {
    background: var(--t-light);
    color: #fff;
}

#profile-tabs-content {
    /* padding-top: 20px; */
    margin:20px 20px 50px;
}


@media (max-width: 989.98px) {
    #profile-tabs-content {
        margin-top: 0;
        margin:0px 0px 20px;
    }
}


/**
 * Tab 1's own photo card — avatar circle, camera badge, an "Upload Photo"
 * button that opens the native file picker via a plain <label for="...">
 * (no JS needed for that part) and auto-submits its own sibling <form> the
 * moment a file is chosen (student-profile.js).
 *
 * A plain two-item flex row — the photo card (fixed 260px) beside the
 * field list, back to one single field grid on direct request (no
 * sub-sections). align-items: start (not the flex default, stretch) keeps
 * the shorter photo column at its own natural height instead of being
 * stretched to match the taller field list — stretch would otherwise
 * show as dead space at the bottom of the photo card, since nothing in
 * it fills that extra height.
 */
/* justify-content: center only matters once this wraps — on a wide
   enough screen the field list's own flex-grow already fills every bit
   of space left after the 260px photo card, so there's nothing left for
   centering to shift. Once the row wraps (the field list moving to a row
   of its own, whatever width that actually happens at — not pinned to
   one breakpoint, since that depends on the field list's own content, not
   a fixed number), the photo card is left alone on its own row and was
   sitting flush against the left edge instead of centered — reported live
   with a screenshot. */
.profile-personal-layout {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 32px;
    align-items: start;
}

.profile-photo-card {
    display: flex;
    flex: 0 0 260px;
    flex-direction: column;
    align-items: center;
    text-align: center;
    margin-top: 80px;
}


.profile-personal-form {
    flex: 1 1 420px;
    min-width: 0;
}

/* inline-block so this shrinks to wrap its one child (.profile-photo-avatar)
   at that element's own size, instead of stretching to the flex column's
   full width — the camera badge below is positioned absolute against
   *this* wrapper, not the avatar circle itself, so it needs to share the
   avatar's exact footprint to land on its edge correctly. */
.profile-photo-avatar-wrap {
    display: inline-block;
    margin-bottom: 17px;
    position: relative;
}

.profile-photo-avatar {
    align-items: center;
    background: var(--bg-hover);
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--t-light);
    display: flex;
    height: 200px;
    justify-content: center;
    overflow: hidden;
    width: 200px;
}

.profile-photo-avatar img {
    height: 100%;
    object-fit: cover;
    width: 100%;
}

@media (max-width: 989.98px) {
    .profile-photo-card {
        margin-top: 0;
    }

    .profile-photo-avatar {
        width: 150px;
        height: 150px;
    }
}

/* A sibling of .profile-photo-avatar, not a child of it (see the wrap
   above) — .profile-photo-avatar's own overflow:hidden clips it to a
   circle for the uploaded photo, and a badge positioned near its edge
   would get clipped by that same circular boundary if it lived inside it
   too (a square corner reaches past a circle inscribed in it). Found
   live, reported with a screenshot showing the badge cut in half. */
.profile-photo-camera-badge {
    align-items: center;
    background: var(--primary);
    border: 3px solid var(--bg-card);
    border-radius: 50%;
    bottom: 4px;
    color: #fff;
    display: flex;
    height: 40px;
    justify-content: center;
    position: absolute;
    right: 4px;
    width: 40px;
}

.profile-photo-upload-btn {
    align-items: center;
    display: inline-flex;
    gap: 6px;
    margin-bottom: 8px;
}

.profile-photo-upload-btn.is-disabled {
    cursor: not-allowed;
    opacity: .65;
    pointer-events: none;
}

.profile-photo-help {
    color: var(--t-light);
    font-size: 12px;
    line-height: 1.4;
    margin: 0;
}

/**
 * Tab 2's education rows — the original one-row-per-qualification layout,
 * kept simple on direct request (a fuller card redesign was tried and
 * reverted — its section header/dropdown-menu/detail-grid didn't reflow
 * cleanly on a narrow phone), just with real styling and small icons
 * instead of the plain Bootstrap border/badge-free look it started as.
 */

#tab-education
{
    margin:0px 10px;
}


.edu-row {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-left: 3px solid var(--primary);
    border-radius: 10px;
    margin-bottom: 14px;
    padding: 16px;
}

.edu-row__body {
    align-items: flex-start;
    display: flex;
    gap: 12px;
}

.edu-row__icon {
    align-items: center;
    background: var(--primary-soft);
    border-radius: 50%;
    color: var(--primary);
    display: flex;
    flex-shrink: 0;
    height: 40px;
    justify-content: center;
    width: 40px;
}

.edu-row__text {
    flex: 1;
    min-width: 0;
}

.edu-row__title {
    color: var(--t-base);
    font-size: 15px;
    font-weight: 700;
    margin: 0 0 2px;
}

.edu-row__subtitle {
    color: var(--t-muted);
    font-size: 13px;
    margin: 0 0 6px;
}

.edu-row__meta {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 13px;
    gap: 8px;
    margin: 0;
}

.edu-row__meta-item {
    align-items: center;
    display: inline-flex;
    gap: 5px;
}

.edu-row__meta-item svg {
    color: var(--t-light);
    flex-shrink: 0;
}

.edu-row__meta-dot {
    color: var(--t-light);
}

.edu-row__badge {
    background: var(--success-soft);
    border-radius: 999px;
    color: var(--success);
    font-size: 12px;
    font-weight: 700;
    padding: 2px 9px;
}

.edu-row__actions {
    display: flex;
    flex-shrink: 0;
    gap: 6px;
}

.edu-row__action-btn {
    align-items: center;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 7px;
    color: var(--t-muted);
    cursor: pointer;
    display: flex;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.edu-row__action-btn:hover {
    background: var(--bg-hover);
    color: var(--primary);
}

.edu-row__action-btn--danger:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

.edu-add-btn {
    align-items: center;
    display: inline-flex;
    gap: 6px;
}

@media (max-width: 989.98px) {

    #tab-education
    {
        margin:0px;
    }

    .edu-row__title {
        font-size: 14px;
    }

    .edu-row__subtitle {
        font-size: 12px;
    }

    .edu-row__meta {
        font-size: 12px;
    }

}

@media (max-width: 575.98px) {
    .edu-row__actions {
        flex-direction: column;
    }
}

/**
 * <x-profile-completeness-list> (Step 12) — REG-07's "what's still missing"
 * checklist. A single-column <ul> stuffed inside a full-width <x-alert>
 * left most of the box empty (theme.css's .alert is a fixed icon|text|
 * action grid, never designed to hold a multi-group checklist). This is a
 * standalone panel instead, styled like the rest of the app's .card
 * (theme.css) rather than a coloured "alert" fill — a flat, tinted banner
 * reads as a warning notice, not as part of the normal page.
 *
 * .completeness-groups is a *fixed*-column grid (repeat(3, 1fr), not
 * repeat(auto-fit, minmax(...,1fr))) — auto-fit's own 1fr tracks stretch a
 * single remaining group to fill the entire row once its row-mates have
 * wrapped away, so a lone third group (or second, on a narrower screen)
 * ended up visibly wider than its siblings above it. A fixed column count
 * means every group is always exactly one column wide, whether it has
 * company on its row or not — the actual bug this replaced (found and
 * reported live: cards of inconsistent width/height between rows,
 * specifically below 1000px).
 */
.completeness-panel {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-card);
    margin-bottom: 24px;
    padding: 20px 22px;
}

.completeness-panel-head {
    align-items: flex-start;
    display: flex;
    gap: 12px;
    margin-bottom: 18px;
}

.completeness-panel-icon {
    align-items: center;
    background: var(--info-soft);
    border-radius: 999px;
    color: var(--info);
    display: flex;
    flex-shrink: 0;
    height: 36px;
    justify-content: center;
    width: 36px;
}

.completeness-panel-icon svg {
    height: 17px;
    width: 17px;
}

.completeness-panel-title {
    color: var(--t-base);
    font-size: 15px;
    font-weight: 700;
    margin: 0;
}

.completeness-panel-sub {
    color: var(--t-muted);
    font-size: 12.5px;
    margin: 2px 0 0;
}

.completeness-groups {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 1099.98px) {
    .completeness-groups {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 575.98px) {
    .completeness-groups {
        grid-template-columns: 1fr;
    }
}

/* Accent per group — same --primary/--purple/--warning KPI-card colours,
   referenced through one custom property so the icon badge and count pill
   below only have to be styled once each. */
.completeness-group {
    --completeness-accent: var(--primary);
    background: var(--bg-muted);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    padding: 13px 16px;
}

.completeness-group--purple {
    --completeness-accent: var(--purple);
}

.completeness-group--success {
    --completeness-accent: var(--success);
}

.completeness-group-title {
    align-items: center;
    color: #4e5d72;
    display: flex;
    font-size: 11.5px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: .03em;
    margin: 0 0 10px;
    text-transform: uppercase;
}

.completeness-group-icon {
    align-items: center;
    background: color-mix(in srgb, var(--completeness-accent) 15%, transparent);
    border-radius: 8px;
    color: var(--completeness-accent);
    display: flex;
    flex-shrink: 0;
    height: 26px;
    justify-content: center;
    width: 26px;
}

.completeness-group-icon svg {
    height: 14px;
    width: 14px;
}

.completeness-group-label {
    flex: 1;
}

.completeness-group-count {
    align-items: center;
    background: color-mix(in srgb, var(--completeness-accent) 15%, transparent);
    border-radius: 999px;
    color: var(--completeness-accent);
    display: inline-flex;
    font-size: 10.5px;
    font-weight: 700;
    height: 20px;
    justify-content: center;
    letter-spacing: 0;
    padding: 0 8px;
    text-transform: none;
    white-space: nowrap;
}

.completeness-item-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.completeness-item {
    align-items: center;
    color: #637086;
    display: flex;
    font-size: 12.5px;
    font-weight: 500;
    gap: 8px;
    padding: 5px 2px;
}

.completeness-item-icon {
    color: var(--warning);
    flex-shrink: 0;
}

.completeness-item span {
    flex: 1;
}

/**
 * Tab 3 (Documents, Step 12) — REG-10. A list, not a card grid — a grid
 * puts rows of differently-tall cards side by side with no clean way to
 * keep them level; a list item is just as tall as its own content, so
 * nothing needs to match a neighbour's height at all.
 *
 * One visible control per item, not two: the dropzone's own <label>
 * already opens the native file picker on click, so the real
 * <input type="file"> is visually hidden (sr-only — clipped to 1px, never
 * display:none/[hidden], which would drop it from the tab order and fail
 * CLAUDE.md's "keyboard focus is visible" rule) — the :has() rule below
 * gives the dropzone itself a visible focus ring whenever that hidden
 * input has focus, so keyboard users lose nothing. The same dropzone shows
 * either "Click to upload" or the current file's own name — one line, one
 * control, for both a first upload and a replacement.
 */


 #tab-documents
{
    margin:0px 10px;
}

@media (max-width: 989.98px) {

    #tab-documents
    {
        margin:0px;
    }
}


.doc-list {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
}

@media (max-width: 991.98px) {
    .doc-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575.98px) {
    .doc-list { grid-template-columns: minmax(0, 1fr); }
}

/* Left-border accent added to match Tab 2's education rows — green once
   uploaded, amber while still required, so which document types still
   need attention reads at a glance down the list, not just from the
   small badge in each row's own header. */
.doc-list-item {
    /* background: var(--bg-muted); */
    border: 1px solid var(--border);
    border-left: 3px solid var(--warning);
    border-radius: 12px;
    padding: 16px 18px;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.doc-list-item.is-complete {
    border-left-color: var(--success);
}

/* border-top/-right/-bottom-color individually, never the border-color
   shorthand — that would also overwrite the left accent above with this
   same hover colour, losing the amber/green meaning the moment a row is
   hovered. */
.doc-list-item:hover {
    border-top-color: var(--primary);
    border-right-color: var(--primary);
    border-bottom-color: var(--primary);
    box-shadow: var(--shadow-sm);
}

.doc-list-header {
    align-items: center;
    display: flex;
    gap: 10px;
    margin-bottom: 12px;
}

.doc-card-icon {
    align-items: center;
    border-radius: 10px;
    display: flex;
    flex-shrink: 0;
    height: 34px;
    justify-content: center;
    width: 34px;
}

.doc-card-icon svg {
    height: 17px;
    width: 17px;
}

.doc-card-icon.is-complete {
    background: var(--success-soft);
    color: var(--success);
}

.doc-card-icon.is-empty {
    background: var(--bg-muted);
    color: var(--t-light);
}

.doc-card-title {
    color: var(--t-base);
    flex: 1;
    font-size: 14px;
    font-weight: 600;
    margin: 0;
}

/* The row holding the file control and its action button(s) — the
   Replace/Upload form grows to fill the space, Remove (a second, genuinely
   separate form — see the partial's own comment on why) stays its own
   width at the end. Wraps on narrow screens instead of squeezing. */
.doc-list-body {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.doc-list-replace-form {
    align-items: flex-start;
    display: flex;
    flex: 1 1 280px;
    gap: 10px;
    min-width: 0;
}

.doc-list-replace-form .btn,
.doc-list-body > form:not(.doc-list-replace-form) .btn {
    flex-shrink: 0;
}

.doc-action-btn {
    align-items: center;
    display: inline-flex;
    gap: 6px;
}

/* The one dropzone — a real <label for="...">, not a decorative div, so
   clicking anywhere in it opens the native file picker (the standard
   accessible label-wraps-control pattern). A compact horizontal bar, not
   a tall square — it sits inline in the row next to its own button. */
.doc-upload {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
}

.doc-dropzone {
    align-items: center;
    background: var(--bg-card);
    border: 1.5px dashed var(--border);
    border-radius: 8px;
    cursor: pointer;
    display: flex;
    gap: 10px;
    padding: 9px 12px;
    text-align: left;
    transition: border-color .15s ease, background-color .15s ease;
}

/* Light theme only, on direct request — dark theme keeps the base rule's
   var(--bg-card) above untouched. */
[data-theme="light"] .doc-dropzone {
    background: #f6faff;
}

.doc-dropzone:hover {
    background: var(--primary-soft);
    border-color: var(--primary);
}

.doc-upload:has(.doc-upload-input:focus-visible) .doc-dropzone {
    border-color: var(--primary);
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.doc-dropzone-icon {
    color: var(--t-light);
    flex-shrink: 0;
    height: 18px;
    width: 18px;
}

.doc-dropzone:hover .doc-dropzone-icon {
    color: var(--primary);
}

.doc-dropzone-text-group {
    min-width: 0;
}

.doc-dropzone-text {
    color: var(--t-base);
    display: block;
    font-size: 13px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.doc-dropzone-hint {
    color: var(--t-muted);
    display: block;
    font-size: 11px;
}

.doc-current-file {
    align-items: center;
    display: flex;
    gap: 6px;
    margin-top: 5px;
}

/* The trash icon — a real <button form="..."> submitting a separate Remove
   form by id (see document-upload.blade.php's own docblock), styled small
   and quiet until hovered so it doesn't visually compete with "Current
   File" next to it. */
.doc-trash-btn,
.doc-icon-btn {
    align-items: center;
    background: transparent;
    color: var(--t-muted);
    border: 0;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    flex-shrink: 0;
    height: 34px;
    justify-content: center;
    padding: 0;
    transition: background-color .15s ease, color .15s ease;
    width: 34px;
}

.doc-trash-btn svg,
.doc-icon-btn svg {
    height: 17px;
    width: 17px;
}

.doc-icon-btn:hover,
.doc-icon-btn:focus-visible {
    background: var(--bg-muted);
    color: var(--primary);
}

.doc-locked-name {
    font-size: 13.5px;
    margin-right: auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.doc-trash-btn:hover,
.doc-trash-btn:focus-visible {
    background: var(--danger-soft);
    color: var(--danger);
}

/**
 * Step 13 — layouts/public.blade.php. A third page shell alongside
 * layouts/app.blade.php (authenticated, always shows the sidebar) and
 * layouts/guest.blade.php (a single centered auth card) — neither fits a
 * public, full-width browse page. Slim header, full-width content, footer.
 *
 * The header is the site's shared one (partials/site-header.blade.php,
 * styled by site-chrome.css), sticky at its compact height. The search
 * page's own filter bar sticks directly beneath it while scrolling through
 * results — --public-header-h is that header's height at each width, kept
 * in step with site-chrome.css's compact sizes, and what the filter bar's
 * sticky offset and course-search.js read.
 */
:root {
    --public-header-h: 76px;
}
@media (max-width: 1100px) {
    :root { --public-header-h: 84px; }
}
@media (max-width: 680px) {
    :root { --public-header-h: 68px; }
}

/* The page body must never scroll horizontally (CLAUDE.md) — a real safety
   net, not just the rule stated in prose: the search filter bar's own
   full-bleed breakout below is computed from 50vw, and 100vw/50vw can be a
   few pixels wider than the true visible width on a browser/OS that
   reserves space for a classic (non-overlay) scrollbar, which vw units
   don't account for. Set on body/html specifically, never on an inner
   wrapper div like .public-shell — overflow-x on an arbitrary element
   forces its own overflow-y to auto too (per spec), which would turn it
   into a second, nested scroll container and break position:sticky for
   anything inside it that's meant to stick to the page's own scroll. */
body {
    overflow-x: hidden;
}

.public-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.public-brand {
    align-items: center;
    color: var(--t-base);
    display: inline-flex;
    gap: 10px;
    text-decoration: none;
}

.public-brand .brand-logo {
    align-items: center;
    /* background: var(--primary-soft); */
    background:transparent;
    /* border-radius: 8px; */
    display: flex;
    height: 32px;
    justify-content: center;
    width: 110px;
}

/* The dashboard sidebar's Ignite SAP Xcelerate logo, in its own colours.
   Its grey and navy lettering would sink into the dark theme's sidebar,
   so there it sits on a white tile, as in the site footer. */
.public-brand .brand-logo.sidebar-logo {
    height: auto;
    margin-left: 10px;
    width: 100px;
}

.sidebar-logo img {
    display: block;
    height: auto;
    width: 100%;
}

:root[data-theme=dark] .sidebar-logo {
    background: #fff;
    border-radius: 6px;
    padding: 4px 6px;
}

.public-brand .brand-name {
    font-family: Inter Tight, Inter, sans-serif;
    font-size: 16px;
    font-weight: 700;
}

/* The header and footer line up with this width and side padding
   (site-chrome.css, .site-container--portal) — keep the two in step. */
.public-content {
    flex: 1;
    margin: 0 auto;
    max-width: 1680px;
    padding: 24px 5% 48px;
    width: 100%;
}

/**
 * search/index.blade.php — a colour banner, then a filter bar, then the
 * card grid. Mobile (<992px) keeps its own untouched shape throughout: a
 * "Filters" button that opens the existing Bootstrap offcanvas panel
 * (vertical field list, unchanged). Desktop reuses that exact same
 * offcanvas markup — .offcanvas-lg already reduces it to a plain,
 * normal-flow element above the lg breakpoint (Bootstrap's own
 * .offcanvas-lg .offcanvas-header{display:none} and .offcanvas-body rules,
 * bootstrap.min.css) — so the desktop rules below only ever add layout,
 * never fight Bootstrap's own offcanvas positioning.
 */
/* The campaign banner: 40% of the screen's height. Every size inside it is
   in cqh (a percentage of the banner's own height), so the logos, headline
   and student keep the artwork's proportions to one another at any width;
   only the left margin follows the width, so the text stays on the light
   part of the photo. Phones get a stacked layout. */
.search-banner {
    --banner-navy: #224680;
    --banner-orange: #f39122;
    align-items: center;
    /* Anchored left: when the banner is narrower than the photo, the crop
       keeps its light left side behind the text, not just the screens. */
    background: #fff url("../images/coursesBanner/background-no-strip.jpg") 20% bottom / cover no-repeat;
    border-bottom: 5px solid var(--banner-orange);
    border-radius: 20px;
    container-type: size;
    display: flex;
    /* About a third of the screen's height, but never taller than a quarter of
       its width: on a tall, narrow screen (a portrait tablet) a share of the height would make
       the banner nearly square and push the student out of it. */
    height: max(230px, min(34vh, 26vw));
    isolation: isolate;
    margin-bottom: 20px;
    overflow: hidden;
    position: relative;
}

.search-banner-content {
    align-self: flex-start;
    padding: 17.5cqh 0 8cqh 13.8cqw;
    position: relative;
    z-index: 1;
}

.search-banner-partners {
    display: block;
    height: auto;
    width: 110cqh;
}

.search-banner-row {
    align-items: center;
    display: flex;
    gap: 7.6cqh;
    margin-top: 12.8cqh;
}

/* Ignite SAP Xcelerate logo, drawn in type — em-based, so the whole
   lockup scales from its one font-size. */
.xcel-logo {
    display: inline-flex;
    flex: none;
    flex-direction: column;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 4.9cqh;
    line-height: 1;
}
.xcel-logo-top { align-items: stretch; display: flex; }
.xcel-logo-ignite {
    color: #4a4a4a;
    font-size: 2.15em;
    font-weight: 700;
    letter-spacing: -.04em;
    padding-right: .12em;
}
.xcel-logo-ignite span { color: var(--banner-orange); }
.xcel-logo-sap {
    background: var(--banner-navy);
    clip-path: polygon(22% 0, 100% 0, 100% 100%, 0 100%);
    color: #fff;
    font-size: 2.15em;
    font-weight: 700;
    letter-spacing: -.02em;
    margin-left: -.3em;
    padding: .02em .14em 0 .62em;
    position: relative;
}
/* The orange slash between "Ignite" and "SAP" */
.xcel-logo-sap::before {
    background: var(--banner-orange);
    bottom: 0;
    content: "";
    left: 0;
    position: absolute;
    top: 0;
    transform: skewX(-38deg);
    transform-origin: bottom left;
    width: .09em;
}
.xcel-logo-name {
    align-self: flex-end;
    color: var(--banner-navy);
    font-size: 2.35em;
    letter-spacing: -.03em;
    margin-top: .04em;
}
.xcel-logo-name span { color: var(--banner-orange); font-weight: 700; }
.xcel-logo-tag {
    align-self: flex-end;
    color: #4a4a4a;
    font-size: .5em;
    letter-spacing: .18em;
    margin-top: .5em;
    text-transform: uppercase;
    white-space: nowrap;
}
.xcel-logo-tag i { color: var(--banner-orange); font-style: normal; padding: 0 .3em; }

.search-banner-title {
    color: var(--banner-navy);
    font-family: Montserrat, "Inter Tight", Inter, sans-serif;
    flex: none;
    font-size: 11cqh;
    font-weight: 800;
    letter-spacing: -.005em;
    line-height: 1.12;
    margin: 0;
    white-space: nowrap;
    margin-top:-5px;
}
.search-banner-sap {
    display: inline-block;
    height: .9em;
    margin: 0 .06em;
    vertical-align: -.06em;
    width: auto;
    margin-right: -3px;
}

/* An institute's own campaign page: which campus these courses belong to */
.search-campaign-heading { margin: 10px 0 4px; }
.search-campaign-title {
    color: var(--t-base);
    font-size: clamp(22px, 2.2vw, 28px);
    font-weight: 700;
    line-height: 1.25;
    margin: 0;
}
.search-campaign-city {
    color: var(--t-muted);
    font-size: 15px;
    margin: 2px 0 0;
}

.search-back { margin: 4px 0 14px; }

.search-back-link {
    color: #224680;
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: color .15s ease;
}
/* The brand orange the site header's own links turn on hover. */
.search-back-link:hover,
.search-back-link:focus-visible { color: #dd7c0e; }

.search-banner-corner {
    background: #fff;
    border: 1px solid rgba(34, 70, 128, .25);
    border-radius: 999px;
    box-shadow: 0 4px 12px rgba(34, 70, 128, .1);
    color: var(--banner-navy);
    font-size: max(10.5px, 2.2cqh);
    font-weight: 600;
    left: 12px;
    padding: 4px 10px;
    position: absolute;
    text-decoration: none;
    top: 12px;
    z-index: 2;
}
.search-banner-corner:hover,
.search-banner-corner:focus-visible {
    background: var(--banner-navy);
    color: #fff;
}

.search-banner-person {
    bottom: 0;
    height: 92%;
    /* Mirrors the logos: the same gap from the right edge as they have
       from the left (the photo is 65cqh wide at this height). On a banner
       too narrow for that, it stands just after the headline instead, so
       the two never overlap. */
    left: max(calc(15.8cqw + 156cqh), calc(100cqw - 15.8cqw - 65cqh));
    position: absolute;
    width: auto;
    z-index: 1;
}

/* Phones: the artwork's proportions would make the text unreadably small,
   so the banner stacks — partner logos, program logo, headline — over the
   light left side of the photo, and the student steps aside. */
@media (max-width: 640px) {
    /* Not tied to the screen height here on purpose: fitted to it, the
       text would be too small to read on a phone. */
    .search-banner {
        background-position: 18% bottom;
        container-type: normal;
        height: auto;
        text-align: center;
    }
    .search-banner-content { align-self: auto; padding: 28px 20px 34px; width: 100%; }
    .search-banner-partners { margin: 0 auto; max-width: 330px; width: 88%; }
    .search-banner-row { flex-direction: column; gap: 18px; margin-top: 24px; }
    .xcel-logo { font-size: 12.5px; }
    .search-banner-title { font-size: 27px; white-space: normal; }
    /* A fixed size: the banner stops being a size container at this width,
       so cqh would measure the whole screen and make this far too large. */
    .search-banner-corner { font-size: 10.5px; left: 8px; top: 8px; }
    /* Room for the corner button above the logos, on a campaign page only. */
    .search-banner:has(.search-banner-corner) .search-banner-content { padding-top: 40px; }
    .search-banner-person { display: none; }
}

/* "You came from another institute" — shown on a course page whose
   institute isn't the one that referred the visitor. Amber rather than
   red: nothing is wrong, and applying here is entirely allowed. The icon
   and the wording both carry the meaning, so colour is never the only
   signal. */
.institute-notice {
    align-items: flex-start;
    background: var(--warning-soft, #fff8e6);
    border: 1px solid var(--warning, #f0b429);
    border-radius: 10px;
    display: flex;
    gap: 10px;
    margin-bottom: 16px;
    padding: 12px 14px;
}

.institute-notice__icon {
    color: var(--warning, #f0b429);
    flex: 0 0 auto;
    height: 18px;
    margin-top: 1px;
    width: 18px;
}

.institute-notice__text {
    color: var(--t-base);
    flex: 1 1 auto;
    font-size: 13px;
    line-height: 1.55;
    margin: 0;
}

.institute-notice__close {
    background: none;
    border: 0;
    color: var(--t-muted);
    cursor: pointer;
    flex: 0 0 auto;
    line-height: 0;
    padding: 2px;
}

.institute-notice__close:hover {
    color: var(--t-base);
}

.institute-notice__close svg {
    height: 15px;
    width: 15px;
}

/* Recent Visited Courses — the student dashboard's first card. Each row is
   one link (the whole row is clickable), all in the theme's primary blue so the
   list reads as one calm, consistent block. Institute
   and city are separate tags rather than one run-on line, and every tag
   carries an icon as well as its colour, so colour is never the only cue. */
.recent-courses {
    display: flex;
    flex-direction: column;
    gap: 10px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.recent-course {
    --rc-accent: var(--primary);
    --rc-soft: var(--primary-soft);
    align-items: center;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    color: inherit;
    display: flex;
    gap: 16px;
    padding: 14px 16px;
    position: relative;
    text-decoration: none;
    transition: border-color .18s, box-shadow .18s, transform .18s, background .18s;
}

.recent-course:hover {
    background: linear-gradient(90deg, var(--rc-soft) 0%, var(--bg-card) 55%);
    border-color: var(--rc-accent);
    box-shadow: var(--shadow-sm);
    color: inherit;
    transform: translateY(-1px);
}

.recent-course:focus-visible {
    border-color: var(--rc-accent);
    box-shadow: 0 0 0 3px var(--rc-soft);
    outline: 0;
}

.recent-course__icon {
    align-items: center;
    background: var(--rc-soft);
    border-radius: 12px;
    color: var(--rc-accent);
    display: inline-flex;
    flex: 0 0 auto;
    height: 46px;
    justify-content: center;
    width: 46px;
}

.recent-course__icon svg {
    height: 22px;
    width: 22px;
}

.recent-course__body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.recent-course__program {
    color: var(--rc-accent);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.recent-course__name {
    color: var(--t-base);
    font-family: Inter Tight, Inter, sans-serif;
    font-size: 15.5px;
    font-weight: 700;
    letter-spacing: -.01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.recent-course__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 4px;
}

.recent-course__tag {
    align-items: center;
    background: var(--bg-muted);
    border-radius: 999px;
    color: var(--t-muted);
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    gap: 5px;
    line-height: 1;
    padding: 5px 10px;
}

.recent-course__tag svg {
    flex: 0 0 auto;
    height: 12px;
    width: 12px;
}

.recent-course__tag--institute {
    background: var(--primary-soft);
    color: var(--primary);
}

.recent-course__cta {
    align-items: center;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--t-base);
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 12.5px;
    font-weight: 600;
    gap: 6px;
    padding: 7px 14px;
    transition: background .18s, border-color .18s, color .18s;
}

.recent-course__cta svg {
    height: 14px;
    transition: transform .18s;
    width: 14px;
}

.recent-course:hover .recent-course__cta,
.recent-course:focus-visible .recent-course__cta {
    background: var(--rc-accent);
    border-color: var(--rc-accent);
    color: #fff;
}

.recent-course:hover .recent-course__cta svg,
.recent-course:focus-visible .recent-course__cta svg {
    transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
    .recent-course,
    .recent-course__cta,
    .recent-course__cta svg {
        transition: none;
    }

    .recent-course:hover {
        transform: none;
    }
}

@media (max-width: 575.98px) {
    .recent-course {
        align-items: flex-start;
        flex-wrap: wrap;
    }

    /* A zero basis keeps the text beside the icon instead of wrapping onto
       its own line under flex-wrap — only the CTA should drop down. */
    .recent-course__body {
        flex: 1 1 0;
    }

    .recent-course__name {
        white-space: normal;
    }

    .recent-course__cta {
        justify-content: center;
        width: 100%;
    }
}

/* An institute's own campaign link, in the institutes table. The URL
   itself has to stay readable at a glance (a Power User reads it aloud to
   whoever runs their social media), so it wraps rather than truncating. */
.campaign-link {
    align-items: center;
    display: flex;
    gap: 8px;
}

.campaign-link__url {
    color: var(--primary);
    font-size: 12px;
    overflow-wrap: anywhere;
}

.campaign-link__copy {
    flex: 0 0 auto;
}

.search-layout {
    display: block;
}

/* Fixed to the left edge instead of sitting inline above the results —
   inline, it scrolled away with the page the moment the student started
   reading the card grid, so reopening the filters meant scrolling all
   the way back up. Pinned and vertically centred, it now stays reachable
   for the entire scroll, and the rest of the page scrolls freely under
   it. A circle, not .btn's own text-button padding — a wide pill with a
   label stuck to the edge, moving with every scroll, read as intrusive
   over the page content; the label survives for screen readers via
   .visually-hidden (Bootstrap) rather than being dropped. Desktop hides
   it entirely below (the sticky pill bar takes over instead) — this is
   the small-screen treatment only. */
/* Solid --primary, not the plain .btn--outline-grey card colour the
   class itself would otherwise give it — a bare white/dark-card circle
   read as an ordinary muted button, easy to miss floating over the page
   content. The same brand colour as "Apply now" makes it read as a real
   action control instead of blending into the background. */
.search-filters-toggle {
    align-items: center;
    background: var(--primary);
    border-color: var(--primary);
    border-radius: 50%;
    box-shadow: var(--shadow-lg);
    color: #fff;
    display: inline-flex;
    height: 35px;
    justify-content: center;
    left: 12px;
    padding: 0;
    position: fixed;
    top: 56%;
    transform: translateY(-50%);
    transition: background-color .18s ease, transform .18s ease;
    width: 35px;
    z-index: 20;
}

.search-filters-toggle:hover,
.search-filters-toggle:focus-visible {
    background: var(--primary-dark);
    color: #fff;
}

.search-filters-toggle svg {
    height: 18px;
    width: 18px;
}

/* No visual footprint of its own — see the desktop .is-stuck rules below
   for what actually watches this. */
#search-filters-sentinel {
    height: 1px;
}

.search-filters {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-card);
    padding: 20px;
}

.search-filters-body {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

@media (min-width: 992px) {
    .search-filters-toggle {
        display: none;
    }

    /* A horizontal, sticky pill bar instead of a sidebar — sticks
       directly below the fixed header (--public-header-h) so it's
       reachable at any scroll position through the results.

       Bled out to the true viewport edges with the standard
       "calc(50% - 50vw)" breakout — not a negative margin matching
       .public-content's own side padding: that only reaches the edge of
       .public-content's own max-width (1680px), so on any wider monitor a
       visible gap of page background was still showing on both sides
       (found live, from a screenshot). margin-left/right computed this way
       reach the real viewport edge regardless of any ancestor's max-width,
       and — unlike the equivalent left:50%;margin-left:-50vw version of
       this trick — never touches left/right, which a position:sticky
       element also uses for its own horizontal stick behaviour; plain
       margin arithmetic doesn't fight that.

       Transparent by resting state — Bootstrap's own .offcanvas-lg rule
       (bootstrap.min.css) already forces background-color:transparent
       !important at this breakpoint, so simply not fighting it gets the
       "blend into the page while scrolled past the banner" look for free.
       .is-stuck (toggled by an IntersectionObserver in course-search.js,
       watching #search-filters-sentinel immediately above this element)
       is the one deliberate !important override, the instant the bar
       actually starts sticking to the fixed header — it needs a solid
       background at that point or the cards scrolling underneath would
       show straight through the pill labels. */
    #search-filters-panel.search-filters {
        border: 0;
        border-bottom: 1px solid transparent;
        border-radius: 0;
        box-shadow: none;
        margin-bottom: 24px;
        /* The BAR (background/border/sticky) spans the true viewport edge
           to edge; the pills inside it stay centered at the page's own
           content width instead (.search-filters-body, below) — a full-
           bleed background with centered content, not full-bleed content. */
        margin-left: calc(50% - 50vw);
        margin-right: calc(50% - 50vw);
        padding: 0;
        position: sticky;
        top: var(--public-header-h);
        transition: background-color .25s ease, border-color .25s ease, box-shadow .25s ease;
        z-index: 40;
    }

    #search-filters-panel.search-filters.is-stuck {
        background: var(--bg-card) !important;
        border-bottom-color: var(--border);
        box-shadow: var(--shadow-sm);
    }

    #search-filters-panel .search-filters-body {
        align-items: flex-end;
        display: flex;
        /* Without this, the base (mobile) .search-filters-body rule's own
           flex-direction: column wins instead — found live, from a
           screenshot showing every pill stacked vertically and shoved to
           the right edge (align-items: flex-end acts on the CROSS axis,
           which flips from vertical to horizontal the moment direction
           does). */
        flex-direction: row;
        flex-wrap: wrap;
        gap: 12px;
        margin: 0 auto;
        max-width: 1680px;
        /* Matches .public-content's own side padding exactly (5%) so the
           pills line up with the banner and card grid below, not a
           different clamp() value that happened to look close but wasn't
           actually the same at every width. */
        padding: 12px 5%;
        row-gap: 12px;
        width: 100%;
    }

    /* Each field is its own small label-above-select stack, laid out in a
       row by the parent above. */
    #search-filters-panel .search-filters-body .field {
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    #search-filters-panel .search-filters-body .field-label {
        color: var(--t-muted);
        font-size: 11px;
        font-weight: 700;
        letter-spacing: .03em;
        margin: 0 2px;
        text-transform: uppercase;
        margin-left: 7px;
    }

    #search-filters-panel .search-filters-body .select {
        background-color: var(--bg-card);
        border: 1px solid var(--border);
        border-radius: 999px;
        font-weight: 600;
        min-width: 0;
        /* padding: 8px 30px 8px 16px; */
        width: auto;
    }

    #search-filters-panel .search-filters-body .select:hover {
        border-color: var(--t-muted);
    }

    #search-filters-panel .search-filters-body .select:focus-visible {
        border-color: var(--primary);
    }

    /* Tom Select (CLAUDE.md, "Searchable dropdowns") hides the native
       <select> the moment it initializes and inserts a *sibling*
       .ts-wrapper — none of the .select pill styling just above ever
       reaches that sibling, so this bar was falling back to app.css's
       generic Tom Select theme (built for an ordinary boxy form select,
       not a compact pill) and rendering oversized and square-cornered
       (found live, from a screenshot). Restyled to the exact same pill
       here, targeting Tom Select's own classes instead. --ts-pr-caret is
       overridden because the vendor CSS sets the control's own
       padding-right !important from it — a plain padding-right here would
       be silently ignored. */
    #search-filters-panel .search-filters-body .ts-wrapper {
        --ts-pr-caret: 1.75rem;
        --ts-pr-min: 1rem;
        align-self: flex-start;
        flex: 0 1 auto;
        /* A uniform, more generous width across all six filters plus
           sort — not each one shrink-wrapped to its own "Any" text, which
           read as cramped next to the reference design. */
        min-width: 160px;
        width: auto;
    }

    #search-filters-panel .search-filters-body .ts-wrapper .ts-control {
        background-color: var(--bg-card);
        border-radius: 999px;
        font-weight: 600;
        min-height: unset;
        padding-bottom: 8px;
        padding-left: 16px;
        padding-top: 8px;
        border:0px !important;
    }

    /* The vendor CSS gives this input flex: 1 1 auto (flex-grow: 1), so
       once an option is selected it stretches to fill whatever space is
       left in the pill, opening a big visible gap between the selected
       text and the clear/caret icons (found live, from a screenshot) —
       fine in an ordinary wide form field, wrong in a compact pill. No
       grow here; it only takes the small width it actually needs. */
    #search-filters-panel .search-filters-body .ts-wrapper .ts-control input {
        flex: 0 1 auto;
        min-width: 1rem;
    }

    #search-filters-panel .search-filters-body .ts-wrapper:hover .ts-control {
        border-color: var(--t-muted);
    }

    #search-filters-panel .search-filters-body .ts-wrapper.focus .ts-control {
        border-color: var(--primary);
        box-shadow: none;
    }

    #search-filters-panel .search-filters-body .ts-dropdown,
    #search-filters-panel .search-filters-body .ts-dropdown.form-control,
    #search-filters-panel .search-filters-body .ts-dropdown.form-select {
        font-size: 12px;
        min-width: 150px;
    }

    #search-filters-panel .field-error {
        display: none;
    }

}

.search-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

/* Reuses .chip's own pill shape — the whole chip is the
   button here, not a separate inner .chip-remove, since removing the
   filter is the chip's only purpose. */
.search-chips .chip {
    background: var(--primary-soft);
    border: 0;
    color: var(--primary);
    cursor: pointer;
    font-weight: 600;
    padding: 6px 8px 6px 12px;
    transition: background-color .15s ease;
}

.search-chips .chip:hover,
.search-chips .chip:focus-visible {
    background: var(--danger-soft);
    color: var(--danger);
}

.search-chips .chip svg {
    height: 12px;
    width: 12px;
}

.search-count {
    color: var(--t-muted);
    font-size: 13px;
    font-weight: 600;
    margin-bottom: 14px;
}

.course-grid-wrap {
    position: relative;
}

.course-grid {
    display: grid;
    gap: 18px;
    grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
    transition: opacity .15s ease;
}

.course-grid.is-loading {
    opacity: .5;
    pointer-events: none;
}

.skeleton-grid {
    inset: 0;
    position: absolute;
}

@keyframes skeleton-shimmer {
    0% {
        background-position: -200px 0;
    }

    100% {
        background-position: calc(200px + 100%) 0;
    }
}

.course-card-skeleton {
    background: linear-gradient(90deg, var(--bg-muted) 25%, var(--border-soft) 37%, var(--bg-muted) 63%);
    background-size: 400px 100%;
    animation: skeleton-shimmer 1.4s ease infinite;
    border-radius: 14px;
    height: 220px;
}

.search-empty {
    color: var(--t-muted);
    grid-column: 1 / -1;
    padding: 48px 20px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.search-empty svg {
    color: var(--t-muted);
    height: 60px;
    margin-bottom: 12px;
    width: 60px;
}

.search-empty-title {
    color: var(--t-base);
    font-weight: 700;
    margin-bottom: 4px;
}

.search-empty-sub {
    font-size: 13px;
    margin: 0;
}

.search-load-more {
    display: flex;
    justify-content: center;
    margin-top: 24px;
}

/**
 * <x-course-card> — the whole card is one <a>, one tab stop, hover raises
 * it with a shadow (the "obvious click target" the brief asks for).
 */
.course-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    color: inherit;
    display: flex;
    flex-direction: column;
    padding: 18px;
    text-decoration: none;
    transition: box-shadow .18s ease, transform .18s ease, border-color .18s ease;
}

/* One look for every card (direct instruction): a wash from the brand's
   light orange into a light grey, as on the public home page, with the
   brand orange for the eyebrow and the hover border. Dark mode keeps the
   same idea in the dark theme's own warm and neutral tones. */
.course-card {
    --card-warm: #fff3e4;
    --card-grey: #f4f5f7;
    --card-accent: #dd7c0e;
    /* Everything in the card that read the portal's blue — institute
       badge, chips, bullets, fee — takes the brand navy and a light grey
       instead, so the card holds to orange, navy and grey. */
    --primary: #224680;
    --primary-dark: #18345f;
    --primary-soft: #eceff3;
    --bg-muted: #eceff3;
    --card-border: #b2cefd;
    background: linear-gradient(135deg, var(--card-warm) 0%, var(--card-grey) 100%);
    border-color: var(--card-border);
}

:root[data-theme=dark] .course-card {
    --card-warm: #2a1d10;
    --card-grey: var(--bg-card);
    --card-accent: #f39122;
    --primary: #8fb0e3;
    --primary-dark: #b3c9ec;
    --primary-soft: #1f2a3d;
    --bg-muted: #1f2a3d;
    /* The light blue border would glare on the dark card. */
    --card-border: var(--border);
}

.course-card .course-card-eyebrow {
    color: var(--card-accent);
}

.course-card:hover,
.course-card:focus-visible {
    border-color: var(--card-accent);
    box-shadow: var(--shadow-card);
    transform: translateY(-2px);
}

.course-card-body {
    flex: 1;
}

.course-card-eyebrow {
    color: var(--primary);
    display: block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.course-card-title {
    color: var(--t-base);
    font-family: Inter Tight, Inter, sans-serif;
    font-size: 17px;
    font-weight: 700;
    line-height: 1.3;
    margin: 4px 0 10px;
}

/* The institute "provider" header — first thing on the card. A solid blue
   initials avatar (white on primary reads the same against all six card
   tints), the institute name in bold, and the city on its own line. A
   hairline under it separates "who offers this" from the course itself. */
.course-card-provider {
    align-items: center;
    border-bottom: 1px solid var(--border-soft);
    display: flex;
    gap: 10px;
    margin: -2px 0 12px;
    padding-bottom: 12px;
}

.course-card-provider-avatar {
    align-items: center;
    background: var(--primary);
    border-radius: 50%;
    box-shadow: 0 0 0 3px var(--primary-soft);
    color: #fff;
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 13px;
    font-weight: 700;
    height: 38px;
    justify-content: center;
    letter-spacing: .02em;
    width: 38px;
}

.course-card-provider-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.course-card-provider-name {
    color: var(--t-base);
    font-size: 14px;
    font-weight: 700;
    line-height: 1.3;
    /* Wraps rather than truncating — the institute is precisely the detail
       a candidate is scanning for, so it's never cut off with an ellipsis. */
    overflow-wrap: anywhere;
    transition: color .18s;
}

.course-card:hover .course-card-provider-name,
.course-card:focus-visible .course-card-provider-name {
    color: var(--primary);
}

.course-card-provider-city {
    align-items: center;
    color: var(--t-muted);
    display: inline-flex;
    font-size: 12px;
    gap: 4px;
}

.course-card-provider-city svg {
    flex-shrink: 0;
    height: 12px;
    width: 12px;
}

/* One chip per distinct fact, always exactly two chips (duration,
   schedule) — never a variable count, which is what previously made one
   card's rhythm sit differently from its neighbour's even at equal card
   height. */
.course-card-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 12px;
}

.course-card-fact {
    align-items: center;
    background: var(--bg-muted);
    border-radius: 999px;
    color: var(--t-base);
    display: inline-flex;
    font-size: 11.5px;
    font-weight: 600;
    gap: 5px;
    padding: 5px 10px;
}

.course-card-fact svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 12px;
    width: 12px;
}

.course-card-requirements-label {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    font-size: 10.5px;
    font-weight: 700;
    gap: 5px;
    letter-spacing: .03em;
    margin: 0 0 6px;
    text-transform: uppercase;
}

.course-card-requirements-label svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 13px;
    width: 13px;
}

/* One requirement per line, not one comma-joined sentence — four or five
   rules used to read as a single run-on line. No line-clamp here either:
   a longer requirement wraps onto a second line instead of ending in
   "…", and .course-grid's default grid stretch still equalises every
   card in the row to the tallest one, so the footer below still lands on
   the same edge across the row regardless of how many lines this list
   ends up being. */
.course-card-requirements {
    display: flex;
    flex-direction: column;
    gap: 4px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.course-card-requirements li {
    color: var(--t-muted);
    font-size: 12px;
    line-height: 1.5;
    padding-left: 14px;
    position: relative;
}

.course-card-requirements li::before {
    color: var(--primary);
    content: "\2022";
    left: 2px;
    position: absolute;
}

/* Not a real link — the whole card is already one <a> (a second,
   nested interactive element would be invalid HTML and fight it for
   focus/click), just styled like one so it reads as "there's more, keep
   going" rather than another bullet the same weight as the two above it. */
.course-card-requirements-more {
    color: var(--primary);
    font-weight: 600;
}

.course-card-requirements-more::before {
    content: "\2192";
}

.course-card-footer {
    align-items: center;
    border-top: 1px solid var(--border-soft);
    display: flex;
    flex-wrap: wrap;
    gap: 6px 12px;
    justify-content: space-between;
    margin-top: 14px;
    padding-top: 12px;
}

/* The price is the card's headline number — large and bold on the footer's
   right-hand side. "Free" turns green rather than the usual price colour so
   the two read differently at a glance; the word itself still says it, so
   colour is never the only signal. margin-left:auto keeps it right-aligned
   even when the closing date wraps onto its own line on a narrow card. */
.course-card-price {
    margin-left: auto;
    text-align: right;
}

.course-card-price .course-fee {
    color: var(--primary);
    font-size: 16px;
    font-weight: 800;
    letter-spacing: -0.01em;
    line-height: 1.1;
}

.course-card-price .course-fee.is-free {
    color: var(--success);
}

.course-card-closing {
    align-items: center;
    color: var(--t-muted);
    display: inline-flex;
    font-size: 12.5px;
    font-weight: 600;
    gap: 5px;
}

/* Never colour alone — is-urgent also swaps the text itself ("Closing
   soon —") and adds an icon, not just this colour. */
.course-card-closing.is-urgent {
    color: var(--danger);
}

.course-card-closing svg {
    height: 13px;
    width: 13px;
}

/**
 * search/show.blade.php — the detail page. Two columns on desktop (content
 * | sticky Apply rail); the Apply panel becomes a fixed bottom bar on
 * mobile so it "stays reachable without scrolling back up" per the brief.
 */
.back-link {
    align-items: center;
    color: var(--t-muted);
    display: inline-flex;
    font-size: 13px;
    font-weight: 600;
    gap: 6px;
    margin-bottom: 16px;
    text-decoration: none;
}

.back-link:hover {
    color: var(--primary);
}

.back-link svg {
    height: 14px;
    width: 14px;
}

.offering-hero .hero-sub {
    align-items: center;
    display: flex;
    gap: 6px;
}

.offering-hero .hero-sub svg {
    height: 14px;
    width: 14px;
}

.offering-layout {
    align-items: flex-start;
    display: grid;
    gap: 24px;
    grid-template-columns: 1fr 300px;
}

.offering-main {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.offering-schedule-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    list-style: none;
    margin: 0 0 16px;
    padding: 0;
}

.offering-schedule-list li {
    align-items: center;
    display: flex;
    font-size: 13.5px;
    gap: 10px;
}

.offering-duration {
    border-top: 1px solid var(--border-soft);
    color: var(--t-base);
    font-size: 13.5px;
    margin: 0;
    padding-top: 14px;
}

@media (min-width: 992px) {
    .offering-aside {
        /* The header is now fixed (Step 13's search page redesign), not
           part of normal document flow — a bare 24px would stick this
           panel underneath it instead of just below it. */
        position: sticky;
        top: calc(var(--public-header-h) + 24px);
    }
}

.apply-panel {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-card);
    padding: 20px;
}

.apply-panel-btn {
    display: flex;
    justify-content: center;
    margin-top: 15px;
    width: 100%;
}

.apply-panel-note {
    color: var(--t-muted);
    font-size: 12.5px;
    line-height: 1.5;
    margin: 10px 0 0;
}

/* The schedule disclaimer under the apply panel's schedule choices — a red
   callout so it's read, not skimmed. */
.apply-panel-important {
    background: var(--danger-soft);
    border: 1px solid var(--danger);
    border-left-width: 4px;
    border-radius: 8px;
    color: var(--danger);
    font-size: 12.5px;
    line-height: 1.55;
    margin: 12px 0 0;
    padding: 10px 12px;
}

.apply-panel-important p {
    margin: 0;
}

.apply-panel-important p + p {
    margin-top: 6px;
}

.apply-panel-important-title {
    align-items: center;
    display: flex;
    font-size: 13px;
    font-weight: 700;
    gap: 6px;
}

.apply-panel-important-title svg {
    flex: 0 0 auto;
    height: 15px;
    width: 15px;
}

.apply-panel-status {
    align-items: center;
    color: #5f5d5d;
    display: flex;
    font-size: 12px;
    gap: 8px;
    margin: 20px 0 4px;
}

.apply-panel-status svg {
    color: var(--success);
    flex-shrink: 0;
    height: 16px;
    width: 16px;
}

/* Unstyled before this: the <legend> and <label> here fell back to raw
   browser defaults (legend at its own font-size, radio+text as plain
   inline flow with no gap), which is what made this section look
   oversized and made a wrapped option's second line run back to the
   container edge instead of staying under its own text. */
.apply-panel-schedules {
    border: 0;
    margin: 14px 0 0;
    padding: 0;
}

.apply-panel-schedules .field-label {
    margin: 0 0 8px;
}

.apply-panel-schedule-option {
    align-items: flex-start;
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    font-size: 13px;
    gap: 8px;
    line-height: 1.4;
    margin-bottom: 8px;
    padding: 10px 12px;
    transition: border-color .15s ease;
}

.apply-panel-schedule-option:last-child {
    margin-bottom: 0;
}

.apply-panel-schedule-option:hover {
    border-color: var(--primary);
}

.apply-panel-schedule-option input[type="radio"] {
    flex-shrink: 0;
    margin-top: 3px;
}

.apply-panel-schedule-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.apply-panel-schedule-type {
    color: var(--t-base);
    font-weight: 600;
    font-size: 12px;
}

.apply-panel-schedule-time {
    color: var(--t-muted);
    font-size: 12px;
}

.apply-confirm-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    margin-top: 16px;
}

@media (max-width: 480px) {
    .apply-confirm-actions {
        flex-direction: column-reverse;
    }

    .apply-confirm-actions .btn {
        width: 100%;
    }
}

/* Was a bare <ul> of links — display:flex on the list drops the browser's
   own bullet markers (they're tied to block layout, not flex), so this
   rendered as plain stacked text with no visible "these are separate
   points" signal at all. An alert-circle icon per row stands in for the
   bullet, coloured to read as "still needs attention" (--warning), not
   "wrong"/--danger — this is a checklist of profile fields to fill in,
   not a list of failures. */
.apply-panel-missing {
    display: flex;
    border-top: 1px solid var(--border-soft);
    flex-direction: column;
    gap: 6px;
    font-size: 12.5px;
    list-style: none;
    margin: 10px 0;
    padding: 16px 0 0;
}

.apply-panel-missing a {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    gap: 8px;
    text-decoration: none;
}

.apply-panel-missing a:hover {
    color: var(--t-muted);
    /* text-decoration: underline; */
}

.apply-panel-missing svg {
    color: var(--warning);
    flex-shrink: 0;
    height: 15px;
    width: 15px;
}

/* Sticky-on-mobile Apply. Only the CTA button itself (.apply-panel-cta,
   wrapping .apply-panel-btn) is fixed to the bottom — reachable without
   scrolling back up, per the brief. Everything above it (schedule picker,
   its note, the benefits list) stays in normal page flow so it scrolls
   with the rest of the page instead of being squeezed into a capped,
   internally-scrolling bar. */
@media (max-width: 991.98px) {
    .apply-panel-cta {
        background: var(--bg-card);
        border-top: 1px solid var(--border);
        bottom: 0;
        box-shadow: 0 -4px 12px rgba(15, 23, 42, .08);
        left: 0;
        padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
        position: fixed;
        right: 0;
        z-index: 30;
    }

    .apply-panel-btn
    {
        margin-top: 8px;
    }

    /* Just enough for the fixed CTA bar so it never covers the last bit
       of real page content scrolled up behind it. */
    /* .offering-main {
        padding-bottom: 96px;
    } */
}

/**
 * <x-search.eligibility-preview> — its own card-equivalent styling
 * (border/shadow matching .card) rather than nesting inside a second
 * <x-card>, since it already owns its own heading.
 */
.eligibility-preview {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-card);
    padding: 20px 22px;
}

.eligibility-preview-banner {
    align-items: center;
    border-radius: 10px;
    display: flex;
    font-size: 13.5px;
    font-weight: 600;
    gap: 8px;
    margin: 10px 0 14px;
    padding: 10px 14px;
}

.eligibility-preview-banner.is-pass {
    background: var(--success-soft);
    color: var(--success);
}

.eligibility-preview-banner.is-fail {
    background: var(--danger-soft);
    color: var(--danger);
}

.eligibility-preview-banner svg {
    flex-shrink: 0;
    height: 18px;
    width: 18px;
}

.eligibility-preview-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    list-style: none;
    margin: 0;
    padding: 20px 0 0;
}

.eligibility-preview-list li {
    display: flex;
    gap: 10px;
}

.eligibility-preview-list svg {
    flex-shrink: 0;
    height: 16px;
    margin-top: 2px;
    width: 16px;
}

.eligibility-preview-list li.is-pass svg {
    color: var(--success);
}

.eligibility-preview-list li.is-fail svg {
    color: var(--danger);
}

.eligibility-preview-rule {
    color: var(--t-base);
    font-size: 13px;
    font-weight: 600;
    margin: 0;
}

.eligibility-preview-reason {
    color: var(--t-muted);
    font-size: 12.5px;
    margin: 2px 0 0;
}

@media (max-width: 767.98px) {
    /* .search-layout is display:block at every width now (the filter bar
       lives above the results, never beside them) — only .offering-layout
       (the detail page's content|apply-rail split) still needs this. */
    .offering-layout {
        grid-template-columns: 1fr;
    }
}

/* Visually hidden, not display:none/[hidden] — stays in the accessibility
   tree and in the tab order (see the class docblock above). */
.doc-upload-input {
    clip: rect(0, 0, 0, 0);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

/* Step 15 — <x-applications.detail>. A small-caps, icon-accented label for
   every subsection inside a card ("Schedule", "Personal details",
   "Entry requirements", ...), deliberately distinct from <x-card>'s own
   H2 title (larger, bold, dark) — the "no order of large and small
   headings" complaint was this: an H3 sized/weighted almost identically to
   its own H2 parent reads as one flat block, not a two-level hierarchy.
   The top border is what actually separates one subsection from the next
   inside a single card; :first-child drops it so the first subsection
   doesn't float a stray line under the card's own header. */
.detail-section-heading {
    align-items: center;
    /* border-top: 1px solid var(--border-soft); */
    color: var(--t-muted);
    display: flex;
    font-size: 12px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: .05em;
    margin: 5px 0 20px;
    padding-top: 18px;
    text-transform: uppercase;
}

.detail-section-heading:first-child {
    border-top: none;
    margin-top: 0;
    padding-top: 0;
}

.detail-section-heading svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 15px;
    width: 15px;
}

/* A calmer replacement for a bare Bootstrap dl.row — a divider per row
   reads as a real list of facts, not one undifferentiated paragraph. */
.detail-list {
    margin: 0;
    border-bottom: 1px solid var(--border-soft);
    padding-bottom: 14px;
}

.detail-list > div {
    border-bottom: 1px solid var(--border-soft);
    display: grid;
    gap: 4px 16px;
    grid-template-columns: 200px 1fr;
    padding: 10px 0;
}

.detail-list > div:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.detail-list dt {
    color: var(--t-muted);
    font-size: 13px;
    font-weight: 600;
}

.detail-list dd {
    color: var(--t-base);
    font-size: 13.5px;
    margin: 0;
}

@media (max-width: 575.98px) {
    .detail-list > div {
        grid-template-columns: 1fr;
    }
}

/* Each schedule slot as its own card, not a bare flex row floating on the
   card's own background — the applied-to slot gets a visibly different
   treatment (border + tinted background), not just a small trailing badge
   easy to miss when scanning several rows. */
.applicant-schedule-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    list-style: none;
    margin: 0 0 4px;
    padding: 0;
}

.applicant-schedule-card {
    background: var(--bg-muted);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    padding: 14px 16px;
}

.applicant-schedule-card.is-applied {
    background: var(--primary-soft);
    border-color: var(--primary);
}

.applicant-schedule-card-head {
    align-items: center;
    display: flex;
    gap: 8px;
    margin-bottom: 6px;
}

.applicant-schedule-card-icon {
    align-items: center;
    background: var(--bg-card);
    border-radius: 8px;
    color: var(--primary);
    display: flex;
    flex-shrink: 0;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.applicant-schedule-card-icon svg {
    height: 16px;
    width: 16px;
}

.applicant-schedule-card-title {
    color: var(--t-base);
    font-size: 13.5px;
    font-weight: 600;
}

.applicant-schedule-card-time {
    color: var(--t-muted);
    font-size: 12.5px;
    margin: 0 0 0 38px;
}

.applicant-schedule-card-applied {
    align-items: center;
    color: var(--primary-dark);
    display: flex;
    font-size: 12px;
    font-weight: 600;
    gap: 5px;
    margin: 8px 0 0 38px;
}

.applicant-schedule-card-applied svg {
    height: 13px;
    width: 13px;
}

/* Step 15 — every section on <x-applications.detail> (Course Details,
   Application Details, Applicant Details). The plain dt/dd list buried
   the one thing each card is actually about under a wall of
   identically-styled rows. A banner (<x-applications.detail-banner>)
   surfaces the headline fact at a glance; the stat tiles turn the
   remaining handful of facts into something scannable by colour, not
   just by reading each label. One shared shape, one gradient per
   section so the three cards read as distinct without looking like
   three different apps. */
.detail-banner {
    align-items: center;
    border: 1px solid var(--border-soft);
    border-radius: 14px;
    display: flex;
    gap: 16px;
    margin-bottom: 20px;
    padding: 20px 22px;
}

.detail-banner--course {
    background: linear-gradient(135deg, var(--primary-soft), var(--purple-soft));
}

.detail-banner--application {
    background: linear-gradient(135deg, var(--info-soft), var(--primary-soft));
}

.detail-banner--applicant {
    background: linear-gradient(135deg, var(--teal-soft), var(--success-soft));
}

.detail-banner-icon {
    align-items: center;
    background: var(--bg-card);
    border-radius: 14px;
    box-shadow: var(--shadow-card);
    color: var(--primary);
    display: flex;
    flex-shrink: 0;
    height: 52px;
    justify-content: center;
    width: 52px;
}

.detail-banner--application .detail-banner-icon {
    color: var(--info);
}

/* The applicant's own banner is the one section about a person, not a
   record — a circular, gradient-filled icon reads as an avatar stand-in
   without needing to compute initials from a name. */
.detail-banner--applicant .detail-banner-icon {
    background: linear-gradient(135deg, var(--teal), var(--success));
    border-radius: 50%;
    color: #fff;
}

.detail-banner-icon svg {
    height: 26px;
    width: 26px;
}

.detail-banner-body {
    min-width: 0;
}

.detail-banner-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 8px;
}

.detail-banner-title {
    align-items: center;
    color: var(--t-base);
    display: flex;
    flex-wrap: wrap;
    font-family: Inter Tight, Inter, sans-serif;
    font-size: 19px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: -.02em;
    margin: 0 0 6px;
}

.detail-banner-sub {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    font-size: 12.5px;
    gap: 6px;
    margin: 0;
}

.detail-banner-sub svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 14px;
    width: 14px;
}

.detail-stat-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    margin-bottom: 16px;
}

.detail-stat-tile {
    --tile-color: var(--primary);
    --tile-soft: var(--primary-soft);
    align-items: center;
    background: var(--bg-card);
    border: 1px solid var(--border-soft);
    border-radius: 12px;
    display: flex;
    gap: 12px;
    padding: 14px;
    transition: box-shadow .18s ease, transform .18s ease;
}

.detail-stat-tile:hover {
    box-shadow: var(--shadow-card);
    transform: translateY(-1px);
}

.detail-stat-tile--success {
    --tile-color: var(--success);
    --tile-soft: var(--success-soft);
}

.detail-stat-tile--danger {
    --tile-color: var(--danger);
    --tile-soft: var(--danger-soft);
}

.detail-stat-tile--info {
    --tile-color: var(--info);
    --tile-soft: var(--info-soft);
}

.detail-stat-tile--orange {
    --tile-color: var(--orange);
    --tile-soft: var(--orange-soft);
}

.detail-stat-tile--purple {
    --tile-color: var(--purple);
    --tile-soft: var(--purple-soft);
}

.detail-stat-tile--warning {
    --tile-color: var(--warning);
    --tile-soft: var(--warning-soft);
}

.detail-stat-tile--teal {
    --tile-color: var(--teal);
    --tile-soft: var(--teal-soft);
}

.detail-stat-icon {
    align-items: center;
    background: var(--tile-soft);
    border-radius: 10px;
    color: var(--tile-color);
    display: flex;
    flex-shrink: 0;
    height: 36px;
    justify-content: center;
    width: 36px;
}

.detail-stat-icon svg {
    height: 18px;
    width: 18px;
}

.detail-stat-label {
    color: var(--t-muted);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    margin: 0 0 2px;
    text-transform: uppercase;
}

.detail-stat-value {
    color: var(--t-base);
    font-size: 13.5px;
    font-weight: 700;
    margin: 0;
}

.detail-info-strip {
    align-items: flex-start;
    background: var(--bg-muted);
    border-radius: 10px;
    color: var(--t-muted);
    display: flex;
    font-size: 12.5px;
    gap: 8px;
    line-height: 1.5;
    padding: 12px 14px;
}

.detail-info-strip svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 15px;
    margin-top: 2px;
    width: 15px;
}

.detail-info-strip + .detail-info-strip {
    margin-top: 8px;
}

/* Applicant Details' document list — was a bare underlined-link list,
   easy to miss next to everything else now rendered as cards. Each
   document becomes its own clickable tile, same "scan by shape and
   colour" idea as the stat tiles above. */
.detail-doc-grid {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}

.detail-doc-card {
    align-items: center;
    background: var(--bg-muted);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    color: var(--t-base);
    display: flex;
    gap: 10px;
    padding: 12px 14px;
    text-decoration: none;
    transition: border-color .16s ease, box-shadow .16s ease;
}

/* A <button> now, opening the file viewer. */
button.detail-doc-card {
    cursor: pointer;
    font: inherit;
    text-align: left;
    width: 100%;
}

.detail-doc-card-view {
    color: var(--t-muted);
    flex-shrink: 0;
    margin-left: auto;
}

.detail-doc-card:hover {
    border-color: var(--primary);
    box-shadow: var(--shadow-card);
}

.detail-doc-card-icon {
    align-items: center;
    background: var(--primary-soft);
    border-radius: 8px;
    color: var(--primary);
    display: flex;
    flex-shrink: 0;
    height: 34px;
    justify-content: center;
    width: 34px;
}

.detail-doc-card-icon svg {
    height: 16px;
    width: 16px;
}

.detail-doc-card-body {
    min-width: 0;
}

.detail-doc-card-type {
    color: var(--t-base);
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    margin: 0;
}

.detail-doc-card-name {
    color: var(--t-muted);
    display: block;
    font-size: 11.5px;
    margin: 2px 0 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A colour accent per schedule type, on top of the existing "applied"
   highlight — with only one card most offerings show, colour is what
   tells weekday and weekend apart at a glance instead of reading the
   title text. .is-applied still wins outright: border-color is a
   shorthand and overrides whatever the type modifier set on the top
   edge alone. */
.applicant-schedule-card {
    border-top: 3px solid var(--border-soft);
}

.applicant-schedule-card--weekday {
    border-top-color: var(--info);
}

.applicant-schedule-card--weekend {
    border-top-color: var(--purple);
}

/* Every .table (education history here, every DataTable elsewhere)
   otherwise shows up near-black in dark mode: layouts/app.blade.php
   sets data-bs-theme="dark" alongside our own data-theme, and Bootstrap's
   own `.table > :not(caption) > * > *` rule paints every cell with
   `background-color: var(--bs-table-bg)`, which resolves to Bootstrap's
   own --bs-body-bg (#212529), not this app's --bg-card — never this
   theme's colour, in either mode, just Bootstrap's default winning by
   variable instead of by rule (our two selectors below still out-specify
   it once they actually set a background). Transparent lets the
   surrounding .card's own background show through instead, so a table
   always matches whatever it's sitting in. */
.table thead th,
.table tbody td {
    background-color: transparent;
}

/* search/show.blade.php — the public course detail page. A calm, plain
   card header, not a saturated colour block — every bolder attempt at
   this (a soft-token gradient, a fixed red gradient, a fixed navy-blue
   gradient, a per-course tinted gradient) got reverted in turn, on
   direct instruction each time. Same background/border/shadow recipe as
   every other .card on this page, so it reads as the first item in the
   page's own list rather than a separate, competing colour block above
   it — colour is kept to the small icon chip and the fact pills only,
   the same restrained accent-only treatment .course-card already uses
   successfully on the card grid one click before this page. */
.offering-hero-banner {
    align-items: flex-start;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-card);
    display: flex;
    gap: 20px;
    margin-bottom: 24px;
    padding: 24px 26px;
}

.offering-hero-icon {
    align-items: center;
    background: var(--primary-soft);
    border-radius: 14px;
    color: var(--primary);
    display: flex;
    flex-shrink: 0;
    height: 56px;
    justify-content: center;
    width: 56px;
}

.offering-hero-icon svg {
    height: 28px;
    width: 28px;
}

.offering-hero-body {
    min-width: 0;
}

.offering-hero-title {
    color: var(--t-base);
    font-family: Inter Tight, Inter, sans-serif;
    font-size: clamp(22px, 2.6vw, 28px);
    font-weight: 700;
    letter-spacing: -.02em;
    line-height: 1.2;
    margin: 8px 0 6px;
}

.offering-hero-sub {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    font-size: 13.5px;
    gap: 6px;
    margin: 0 0 14px;
}

.offering-hero-sub svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 15px;
    width: 15px;
}

.offering-hero-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.offering-hero-fact {
    align-items: center;
    background: var(--bg-muted);
    border-radius: 999px;
    color: var(--t-base);
    display: inline-flex;
    font-size: 12.5px;
    font-weight: 600;
    gap: 6px;
    padding: 6px 12px;
}

.offering-hero-fact svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 13px;
    width: 13px;
}

.offering-hero-fact--success {
    color: var(--success);
}

.offering-hero-fact--success svg {
    color: var(--success);
}

/* The price, on the banner's right-hand side. "Free" turns green rather
   than the usual price colour so the two read differently at a glance; the
   word itself still says it, so colour is never the only signal. */
.offering-hero-price {
    align-items: flex-end;
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    gap: 2px;
    margin-left: auto;
    text-align: right;
}

.offering-hero-price-label {
    color: var(--t-muted);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.offering-hero-price-value .course-fee {
    color: var(--primary);
    font-family: Inter Tight, Inter, sans-serif;
    font-size: clamp(18px, 2.6vw, 23px);
    font-weight: 800;
    letter-spacing: -.02em;
    line-height: 1.1;
}

.offering-hero-price-value .course-fee.is-free {
    color: var(--success);
}

@media (max-width: 575.98px) {
    .offering-hero-banner {
        flex-direction: column;
    }

    .offering-hero-price {
        align-items: flex-start;
        margin-left: 0;
        text-align: left;
    }
}

/* The apply panel's own accent — a coloured top edge plus a short benefit
   list turns "here is a form" into "here is why you'd fill it in", the
   same shape a course marketplace's purchase box uses. */
.apply-panel {
    border-top: 4px solid var(--primary);
}

.apply-panel h2 {
    align-items: center;
    color: var(--t-base);
    display: flex;
    font-size: 16px;
    font-weight: 700;
    gap: 8px;
    margin: 0 0 4px;
}

.apply-panel h2 svg {
    color: var(--primary);
    flex-shrink: 0;
    height: 18px;
    width: 18px;
}

.apply-panel-benefits {
    border-top: 1px solid var(--border-soft);
    display: flex;
    flex-direction: column;
    gap: 8px;
    list-style: none;
    margin: 14px 0;
    padding: 14px 0 0;
}

.apply-panel-benefits li {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    font-size: 12.5px;
    gap: 8px;
}

.apply-panel-benefits svg {
    color: var(--success);
    flex-shrink: 0;
    height: 15px;
    width: 15px;
}

/* Dashboard analytics (BuildDashboardAnalytics) — theme.css's own .grid
   only ships .col-6/.col-12; a 4-across KPI row needs a quarter-width
   column of its own.

   Real bug found and fixed here: Bootstrap's own .col-3/.col-4/.col-6/
   .col-12 utility classes (bootstrap.min.css, loaded before this file)
   set an explicit width (e.g. width: 33.3333% for .col-4) alongside
   flex: 0 0 auto. Overriding only grid-column, as theme.css's own .col-6/
   .col-12 do, leaves that Bootstrap width in place — CSS resolves each
   property independently, not per-rule — so a "span 4" item ends up
   width: 33.3333% *of its own already-4-columns-wide grid area*, a
   fraction of a fraction. Confirmed live: a .col-4 module card rendered
   at ~121px instead of ~345px. width: auto here cancels Bootstrap's
   value so the grid area's own track width (span N) is what actually
   renders — theme.css's .col-6/.col-12 carried this same latent bug
   (visible as an unexplained gap between two .col-6 chart cards), fixed
   below rather than only for the two new classes this file adds.

   Scoped to .grid > .col-N, not a bare .col-N, on purpose — found live: an
   unscoped version of this rule also matched Bootstrap's OWN .row/.col-N
   markup used elsewhere in the app for plain two-column layouts (the
   Activity Log's "view entry" modal, a <dl class="row">, is one), which
   has nothing to do with this 12-column dashboard grid at all. Cancelling
   Bootstrap's width/flex there scrambled that layout completely — labels
   and values stopped lining up, wrapping into a jumbled two-column mess
   instead of one label-then-value row per pair. .col-N only ever means
   this dashboard grid's own column span when it is a *direct* child of
   .grid (already the documented rule — see e.g. offerings/create.blade.php's
   own comment on why .col-12 sits on the <form> itself) — CSS has no way to
   ask for "same class name, but only my own dashboard's meaning," so the
   selector has to encode that directly instead. */
.grid > .col-3,
.grid > .col-4,
.grid > .col-6,
.grid > .col-12 {
    flex: none;
    width: auto;
}

.grid > .col-3 {
    grid-column: span 3;
}

.grid > .col-4 {
    grid-column: span 4;
}

@media (max-width: 991.98px) {
    .grid > .col-3,
    .grid > .col-4 {
        grid-column: span 6;
    }
}

@media (max-width: 575.98px) {
    .grid > .col-3,
    .grid > .col-4 {
        grid-column: span 12;
    }
}

.kpi-grid {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 991.98px) {
    .kpi-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 575.98px) {
    .kpi-grid {
        gap: 10px;
        grid-template-columns: 1fr;
    }
}

/* Icon + tint colour per KPI meaning — purple (check-circle), warning
   (alert-triangle), info (trending-up), purple (layers), primary (users),
   green (file-text), red (building). Declared once here and referenced by
   .kpi-card/.kpi-icon/.kpi-trend below, rather than repeating each colour
   three times.

   users/file-text/building added on top of the original set — every KPI
   row (Super Admin: users/file-text/check-circle/building) used to leave
   three of its four cards on the same default primary blue, since only
   check-circle had its own rule; reported live as "these all look the
   same colour." Direct instruction later fixed check-circle/users/building
   to specific hex values instead of the theme's own success/teal/orange
   variables, then swapped users' and file-text's colours (Total Students
   <-> Total Applications). */
:root {
    --kpi-accent: var(--primary);
}

/* Direct instruction: fixed hex, not a theme variable — same colour in
   light and dark mode, unlike every other KPI accent here. */
.kpi-card--check-circle {
    --kpi-accent: #ff6969;
}

.kpi-card--alert-triangle {
    --kpi-accent: var(--warning);
}

.kpi-card--trending-up {
    --kpi-accent: var(--info);
}

.kpi-card--layers {
    --kpi-accent: var(--purple);
}

/* Direct instruction: swapped with file-text's colour (Total Applications
   <-> Total Students) — users now takes file-text's original theme-aware
   primary blue, file-text takes users' original fixed green hex. */
.kpi-card--users {
    --kpi-accent: var(--primary);
}

.kpi-card--file-text {
    --kpi-accent: #03a770;
}

.kpi-card--building {
    --kpi-accent: #0392a7;
}

/* Clean, solid cards — same border/shadow language as every other card
   on this page (.card, .module-card), so the KPI row doesn't look like a
   different design system bolted on. The empty right-hand space a plain
   icon+number layout leaves is filled by a single large, faint watermark
   echo of the card's own icon (.kpi-watermark below) — real visual
   interest, not a decorative gradient/blur effect with nothing behind it. */
.kpi-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-left: 3px solid var(--kpi-accent);
    border-radius: 14px;
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    gap: 10px;
    overflow: hidden;
    padding: 18px 20px;
    position: relative;
    transition: box-shadow .18s ease, transform .18s ease;
}

.kpi-card:hover {
    box-shadow: 0 10px 22px -10px color-mix(in srgb, var(--kpi-accent) 35%, transparent);
    transform: translateY(-2px);
}

/* The oversized icon echo — behind the text (z-index below), clipped by
   the card's own overflow:hidden so it never pokes past the border. */
.kpi-watermark {
    bottom: -18px;
    color: var(--kpi-accent);
    height: 96px;
    opacity: .08;
    position: absolute;
    right: -14px;
    width: 96px;
    z-index: 0;
}

.kpi-watermark svg {
    height: 100%;
    width: 100%;
}

.kpi-card-top,
.kpi-value,
.kpi-label {
    position: relative;
    z-index: 1;
}

.kpi-card-top {
    align-items: center;
    display: flex;
    justify-content: space-between;
}

.kpi-icon {
    align-items: center;
    background: color-mix(in srgb, var(--kpi-accent) 14%, transparent);
    border-radius: 11px;
    color: var(--kpi-accent);
    display: flex;
    flex-shrink: 0;
    height: 42px;
    justify-content: center;
    width: 42px;
}

.kpi-icon svg {
    height: 20px;
    width: 20px;
}

.kpi-trend {
    background: color-mix(in srgb, var(--kpi-accent) 14%, transparent);
    border-radius: 999px;
    color: var(--kpi-accent);
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 700;
    padding: 3px 9px;
    white-space: nowrap;
}

.kpi-value {
    color: var(--t-base);
    font-size: 28px;
    font-weight: 800;
    letter-spacing: -.01em;
    line-height: 1.1;
}

.kpi-label {
    color: var(--t-muted);
    font-size: 12.5px;
    font-weight: 500;
}

.chart-card .card-head {
    margin-bottom: 4px;
}

/* Reported live: "Applications by status" etc. wrap/get cut off on a
   narrow chart card — the default .card-title size is too big for a
   half-width col-6 chart panel once the screen itself is this narrow.
   Scoped to .chart-card specifically, not every card's own title. */
@media (max-width: 575.98px) {
    .chart-card .card-title {
        font-size: 12px;
    }
}

.chart-wrap {
    align-items: center;
    display: flex;
    height: 240px;
    justify-content: center;
    min-width: 0;
    position: relative;
    width: 100%;
}

.chart-wrap canvas {
    max-height: 100%;
    max-width: 100%;
}

.chart-wrap--wide {
    height: 200px;
}

@media (max-width: 575.98px) {
    .chart-wrap {
        height: 200px;
    }

    .chart-wrap--wide {
        height: 180px;
    }
}

.chart-empty {
    align-items: center;
    color: var(--t-muted);
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 12.5px;
    text-align: center;
}

.chart-empty svg {
    color: var(--border);
    height: 32px;
    width: 32px;
}

/* Section spacing between the KPI/analytics block and the Quick access
   grid below it — .grid itself already sets its own bottom-of-page
   rhythm via margin-bottom on .hero, this is the gap between two
   consecutive .grid blocks on the same page. */
.dashboard-section + .dashboard-section-head {
    margin-top: 8px;
}

.dashboard-section-head {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 4px 0 16px;
}

.dashboard-section-eyebrow {
    color: var(--t-light);
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: .18em;
    text-transform: uppercase;
}

.dashboard-section-title {
    color: var(--t-base);
    font-size: 18px;
    font-weight: 700;
    margin: 0;
}

/* Quick-access module cards — the whole card is the link, per-icon accent
   colour reuses the same --kpi-accent variable/mapping the KPI cards
   already declare above. */
.module-card {
    align-items: center;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 16px;
    display: flex;
    gap: 14px;
    padding: 18px;
    text-decoration: none;
    transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}

.module-card:hover {
    border-color: color-mix(in srgb, var(--kpi-accent) 40%, var(--border));
    box-shadow: 0 8px 20px -8px color-mix(in srgb, var(--kpi-accent) 25%, transparent);
    transform: translateY(-2px);
}

.module-card-icon {
    align-items: center;
    background: color-mix(in srgb, var(--kpi-accent) 14%, transparent);
    border-radius: 11px;
    color: var(--kpi-accent);
    display: flex;
    flex-shrink: 0;
    height: 42px;
    justify-content: center;
    width: 42px;
}

.module-card-icon svg {
    height: 19px;
    width: 19px;
}

.module-card-body {
    flex: 1;
    min-width: 0;
}

.module-card-title {
    color: var(--t-base);
    font-size: 14.5px;
    font-weight: 700;
    margin: 0 0 3px;
}

.module-card-desc {
    color: var(--t-muted);
    font-size: 12.5px;
    line-height: 1.5;
    margin: 0;
}

.module-card-arrow {
    align-items: center;
    color: var(--t-light);
    display: flex;
    flex-shrink: 0;
    transition: transform .18s ease, color .18s ease;
}

.module-card-arrow svg {
    height: 16px;
    width: 16px;
}

.module-card:hover .module-card-arrow {
    color: var(--kpi-accent);
    transform: translateX(3px);
}

@media (max-width: 767.98px) {
    .module-card {
        grid-column: span 12 !important;
    }
}

/* <x-eligibility-criteria-detail> — always rendered inside an existing
   <x-card title="Entry requirements">, never its own outer card. Rows
   stack label above value rather than inlining "Label: value", since a
   value here can be a long, comma-joined phrase at any width; icon shape
   (not colour) is what tells rule types apart, so this stays calm and
   readable next to whatever else is on the same page instead of adding a
   second, unrelated colour system. */
.eligibility-detail-heading {
    color: var(--t-muted);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .03em;
    margin: 0 0 10px;
    text-transform: uppercase;
}

/* .eligibility-detail-heading:not(:first-child) {
    margin-top: 20px;
} */

.eligibility-rule-list,
.eligibility-document-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    list-style: none;
    margin: 0;
    margin-bottom:30px;
    padding: 0;
}

.eligibility-rule {
    align-items: flex-start;
    display: flex;
    gap: 12px;
}

.eligibility-rule-icon,
.eligibility-document-icon {
    align-items: center;
    border-radius: 9px;
    display: flex;
    flex-shrink: 0;
    height: 32px;
    justify-content: center;
    margin-top: 1px;
    width: 32px;
}

.eligibility-rule-icon {
    background: color-mix(in srgb, var(--primary) 12%, transparent);
    color: var(--primary);
}

.eligibility-rule-icon svg {
    height: 15px;
    width: 15px;
}

.eligibility-rule-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    padding-top: 4px;
}

.eligibility-rule-label {
    color: var(--t-base);
    font-size: 12.5px;
    font-weight: 700;
}

.eligibility-rule-value {
    color: var(--t-muted);
    font-size: 13px;
    line-height: 1.5;
}

.eligibility-document {
    align-items: center;
    color: var(--t-base);
    display: flex;
    font-size: 13px;
    font-weight: 500;
    gap: 10px;
}

.eligibility-document-icon {
    background: color-mix(in srgb, var(--success) 12%, transparent);
    color: var(--success);
    height: 28px;
    margin-top: 0;
    width: 28px;
}

.eligibility-document-icon svg {
    height: 13px;
    width: 13px;
}

/**
 * Tom Select — overrides on top of the vendored tom-select.bootstrap5.min.css
 * (public/assets/css/), the same "unmodified vendor file + app-specific
 * overrides layered in app.css" split this file already follows for
 * theme.css. The vendor CSS reads Bootstrap's own --bs-* variables, which
 * this app's theme.css never sets (it has its own separate --primary/
 * --border/etc. tokens) — without this section Tom Select would render in
 * Bootstrap's stock blue/grey, not this app's palette, and wouldn't switch
 * with light/dark mode at all. Sized to match .select exactly (theme.css)
 * so a searchable dropdown sits flush next to a plain one with no visible
 * height/radius mismatch.
 */
.ts-wrapper.single .ts-control,
.ts-wrapper.multi .ts-control {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: none;
    color: var(--t-base);
    font-size: 13px;
    min-height: unset;
    padding: 6px 12px;
}

.ts-wrapper.single .ts-control input,
.ts-wrapper.multi .ts-control input {
    color: var(--t-base);
}

.ts-wrapper.focus .ts-control {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-soft);
}

.ts-control .item {
    color: var(--t-base);
}

.ts-control input::placeholder {
    color: var(--t-light);
}

.ts-dropdown {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
    color: var(--t-base);
    font-size: 13px;
}

.ts-dropdown .ts-dropdown-content {
    padding: 4px;
}

.ts-dropdown .option {
    border-radius: 6px;
    color: var(--t-base);
    padding: 7px 10px;
}

.ts-dropdown .option.active {
    background: var(--primary-soft);
    color: var(--primary);
}

.ts-dropdown .option.selected {
    color: var(--primary);
    font-weight: 600;
}

.ts-dropdown .no-results {
    color: var(--t-muted);
    padding: 7px 10px;
}

.ts-wrapper.disabled .ts-control {
    background: var(--bg-muted);
    color: var(--t-light);
    cursor: not-allowed;
}

/* opacity is left to the vendor's own default (hidden unless
   hover/focus + has-items) — an earlier version forced opacity: 1
   unconditionally, which showed a "×" next to every "Any" placeholder
   even with nothing picked (found live, against a reference image that
   shows a plain pill with no clear icon until something is actually
   selected). */
.ts-wrapper.plugin-clear_button .clear-button {
    color: var(--t-light);
    /* Hidden by opacity, not removed from the DOM (vendor default) — an
       invisible element still captures clicks by default, so a click
       aimed at opening the dropdown could silently land on this instead
       and do nothing (found live). Only clickable once it's actually
       visible — matches the exact two selectors the vendor CSS already
       uses to set opacity: 1, below. */
    pointer-events: none;
    /* The "×" is plain text (inherits .ts-control's 13px font-size) next
       to the caret, a fixed 16x12px background-image icon — at the
       inherited size it read as noticeably thinner/smaller than the
       caret (found live, from a screenshot). line-height: 1 keeps the
       larger glyph vertically centered inside the same box instead of
       pushing it off-center. */
      font-size: 18px;
    line-height: 1;
    margin-top: -1px;
}

.ts-wrapper.plugin-clear_button.focus.has-items .clear-button,
.ts-wrapper.plugin-clear_button:not(.disabled):hover.has-items .clear-button {
    pointer-events: auto;
}

.ts-wrapper.plugin-clear_button .clear-button:hover {
    color: var(--danger);
}

/* Multi-select (x-form.chip-select): each picked value is a pill with its
   own remove button, in the theme blue so a selection reads at a glance. */
.ts-wrapper.multi .ts-control {
    gap: 6px;
    min-height: 40px;
    padding: 5px 8px;
}

.ts-wrapper.multi.has-items .ts-control {
    padding: 5px 8px;
}

.ts-wrapper.multi .ts-control > div.item,
.ts-wrapper.multi .ts-control > div.item.active {
    align-items: center;
    background: var(--primary-soft);
    border: 0;
    border-radius: 999px;
    color: var(--primary);
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    gap: 2px;
    line-height: 1.4;
    margin: 0;
    padding: 3px 4px 3px 10px !important;
}

.ts-wrapper.multi .ts-control > div.item.active {
    box-shadow: 0 0 0 2px var(--primary);
}

.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove {
    align-items: center;
    border: 0;
    border-radius: 50%;
    color: var(--primary);
    display: inline-flex;
    font-size: 14px;
    height: 18px;
    justify-content: center;
    margin-left: 2px;
    padding: 0;
    width: 18px;
}

.ts-wrapper.plugin-remove_button .item .remove:hover {
    background: var(--danger-soft);
    color: var(--danger);
}

.ts-wrapper.multi .ts-control > input {
    min-width: 6rem !important;
}

/* A dropdown rendered on <body> because its select sits in a modal
   (tom-select-init.js) — lifted above the modal (z-index 1055), or it
   would open underneath it. */
body > .ts-dropdown {
    z-index: 1070;
}

.ts-wrapper.is-invalid .ts-control {
    border-color: var(--danger);
}

.ts-wrapper.is-invalid.focus .ts-control {
    box-shadow: 0 0 0 3px var(--danger-soft);
}

/*
 * "Browse courses" in the signed-in top bar — the way back to the public
 * course search. On a phone it shrinks to just its icon, with the label kept
 * for screen readers, so the top bar still fits.
 */
.topbar-link {
    align-items: center;
    background: var(--primary-soft);
    border: 1px solid var(--primary);
    border-radius: 8px;
    color: var(--primary);
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    gap: 5px;
    height: 30px;
    padding: 0 10px;
    text-decoration: none;
    transition: background .18s, border-color .18s, color .18s, box-shadow .18s;
}

/* Solid brand colour on hover — and a visible focus ring for keyboard users,
   since the colour change alone isn't a focus indicator. */
.topbar-link:hover,
.topbar-link:focus-visible {
    background: var(--primary);
    color: #fff;
}

.topbar-link:focus-visible {
    box-shadow: 0 0 0 3px var(--primary-soft);
    outline: none;
}

.topbar-link svg {
    height: 14px;
    width: 14px;
}

@media (max-width: 575.98px) {
    .topbar-link {
        padding: 0 8px;
    }

    .topbar-link span {
        clip: rect(0 0 0 0);
        height: 1px;
        overflow: hidden;
        position: absolute;
        white-space: nowrap;
        width: 1px;
    }
}

/*
 * The Manage modals (Registered Students, Applications): their actions as matching cards,
 * side by side where there's room and stacked on a narrow screen.
 */
.manage-actions-grid {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.manage-action {
    background: var(--bg-card);
    border: 1px solid var(--border-soft);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    padding: 16px;
}

.manage-action-title {
    color: var(--t-base);
    font-size: 14px;
    font-weight: 700;
    margin: 0 0 6px;
}

.manage-action-text {
    color: var(--t-muted);
    font-size: 13px;
    line-height: 1.5;
    margin: 0 0 12px;
}

/*
 * Course payments — the staff Decision card: the receipt being judged sits
 * on top with a way to open it, then the two outcomes side by side, each
 * marked by colour AND an icon and title (never colour alone), with a
 * full-width button pinned to the bottom so both line up.
 */
.decision-grid {
    display: grid;
    gap: 14px;
    grid-template-columns: 1fr;
}

@media (min-width: 992px) {
    .decision-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.decision-facts {
    display: grid;
    gap: 6px;
    margin: 0 0 14px;
}

.decision-facts div {
    display: flex;
    gap: 12px;
    justify-content: space-between;
}

.decision-facts dt {
    color: var(--t-muted);
    font-size: 12.5px;
    font-weight: 600;
}

.decision-facts dd {
    color: var(--t-base);
    font-size: 13px;
    font-weight: 600;
    margin: 0;
    text-align: right;
    word-break: break-word;
}

.decision-action {
    border-top-width: 3px;
}

.decision-action--verify {
    border-top-color: var(--success);
}

.decision-action--reject {
    border-top-color: var(--danger);
}

.decision-action--receipt {
    border-top-color: var(--primary);
}

.decision-action--receipt .decision-action-icon {
    background: var(--primary-soft);
    color: var(--primary);
}

.decision-action-head {
    align-items: center;
    display: flex;
    gap: 10px;
    margin-bottom: 8px;
}

.decision-action-head .manage-action-title {
    margin: 0;
}

.decision-action-icon {
    align-items: center;
    border-radius: 50%;
    display: inline-flex;
    flex-shrink: 0;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.decision-action-icon svg {
    height: 16px;
    width: 16px;
}

.decision-action--verify .decision-action-icon {
    background: color-mix(in srgb, var(--success) 14%, transparent);
    color: var(--success);
}

.decision-action--reject .decision-action-icon {
    background: color-mix(in srgb, var(--danger) 14%, transparent);
    color: var(--danger);
}

.decision-action-form {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: 12px;
}

.decision-action-button {
    justify-content: center;
    margin-top: auto;
    width: 100%;
}

/* Receipt preview modal: the whole receipt fits on screen, image or PDF. */
.file-preview {
    align-items: center;
    background: var(--bg-muted);
    border-radius: 10px;
    display: flex;
    justify-content: center;
    min-height: 60vh;
    overflow: auto;
}

.file-preview-image {
    display: block;
    height: auto;
    max-height: 75vh;
    max-width: 100%;
    object-fit: contain;
}

.file-preview-pdf {
    border: 0;
    display: block;
    height: 75vh;
    width: 100%;
}

/*
 * Course payments — the student's payment page. The amount owed stands out;
 * the two methods read as choices, with card visibly unavailable (text, not
 * only greyed out, says why).
 */
.payment-amount {
    align-items: baseline;
    border-top: 1px solid var(--border-soft);
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    justify-content: space-between;
    padding-top: 14px;
}

.payment-amount-label {
    color: var(--t-muted);
    font-size: 13px;
    font-weight: 600;
}

.payment-amount-value {
    color: var(--primary);
    font-size: clamp(24px, 3vw, 30px);
    font-weight: 800;
    letter-spacing: -.01em;
}

.payment-methods {
    border: 0;
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    margin: 0 0 20px;
    padding: 0;
}

.payment-methods legend {
    margin-bottom: 8px;
}

.payment-method {
    align-items: flex-start;
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    gap: 10px;
    padding: 12px 14px;
}

.payment-method.is-selected {
    background: var(--primary-soft);
    border-color: var(--primary);
}

.payment-method.is-disabled {
    cursor: not-allowed;
    opacity: .65;
}

.payment-method input {
    margin-top: 3px;
}

.payment-method-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.payment-method-title {
    align-items: center;
    color: var(--t-base);
    display: flex;
    font-size: 14px;
    font-weight: 700;
    gap: 8px;
}

.payment-method-text {
    color: var(--t-muted);
    font-size: 12.5px;
}

.payment-bank {
    background: var(--bg-muted);
    border-radius: 10px;
    display: grid;
    gap: 8px 24px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    margin: 0 0 14px;
    padding: 14px 16px;
}

.payment-bank dt {
    color: var(--t-muted);
    font-size: 12px;
    font-weight: 600;
}

.payment-bank dd {
    color: var(--t-base);
    font-size: 14px;
    font-weight: 600;
    margin: 0;
    word-break: break-word;
}

.payment-note {
    color: var(--t-base);
    font-size: 13px;
    line-height: 1.55;
    margin: 0 0 12px;
}

.payment-receipt-reason {
    color: var(--danger);
    font-size: 12px;
    margin-top: 4px;
}

/* File drop zone (<x-form.file :dropzone="true">). The real file input is
   stretched invisibly over the box, so clicks, dropped files and keyboard
   focus all reach the browser's own control; the ring below is that
   input's focus, shown on the box. */
.file-drop {
    align-items: center;
    background: var(--bg-muted);
    border: 1.5px dashed var(--border);
    border-radius: 12px;
    color: var(--t-muted);
    display: flex;
    flex-direction: column;
    gap: 8px;
    justify-content: center;
    min-height: 128px;
    padding: 20px 16px;
    position: relative;
    text-align: center;
    transition: border-color .15s ease, background-color .15s ease;
}

.file-drop:hover,
.file-drop:focus-within {
    background: var(--primary-soft);
    border-color: var(--primary);
}

.file-drop:focus-within {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent);
}

.file-drop.has-file {
    background: var(--primary-soft);
    border-color: var(--primary);
    border-style: solid;
}

.file-drop.is-invalid,
.file-drop:has(.file-drop-input.is-invalid) {
    border-color: var(--danger);
}

.file-drop-input {
    cursor: pointer;
    height: 100%;
    inset: 0;
    opacity: 0;
    position: absolute;
    width: 100%;
}

.file-drop-icon {
    align-items: center;
    background: var(--bg-card);
    border-radius: 50%;
    color: var(--primary);
    display: inline-flex;
    height: 42px;
    justify-content: center;
    width: 42px;
}

.file-drop-icon svg {
    height: 20px;
    width: 20px;
}

.file-drop-text {
    color: var(--t-base);
    font-size: 13.5px;
    font-weight: 600;
    word-break: break-all;
}

/* The student's own application page: what still needs doing comes first. */
.payment-callout {
    align-items: center;
    background: var(--primary-soft);
    border: 1px solid var(--primary);
    border-radius: 12px;
    display: flex;
    flex-wrap: wrap;
    gap: 12px 20px;
    justify-content: space-between;
    padding: 16px 20px;
}

.payment-callout-title {
    color: var(--t-base);
    font-size: 15px;
    font-weight: 700;
    margin: 0 0 2px;
}

.payment-callout-text {
    color: var(--t-muted);
    font-size: 13px;
    margin: 0;
}

/* ---------- Student profile: step navigation and submit popup ---------- */

.profile-step-nav .btn:focus-visible,
.profile-step-nav__actions .btn:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.profile-step-caption {
    margin: 0 0 16px;
    font-size: 13px;
    font-weight: 600;
    color: var(--t-muted);
}

.profile-step-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 24px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

.profile-step-nav__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 10px;
    margin-left: auto;
}

.profile-step-hint {
    margin: 10px 0 0;
    font-size: 13px;
    text-align: right;
    color: var(--t-muted);
}

.doc-save-status {
    min-height: 1.4em;
    margin: 12px 0 0;
    font-size: 13px;
    font-weight: 600;
}

.profile-submit-done {
    padding: 8px 0 12px;
    text-align: center;
}
.profile-submit-done__icon { display: inline-block; color: var(--success); }
.profile-submit-done__title { margin: 8px 0 4px; font-size: 18px; font-weight: 700; }

@media (max-width: 575.98px) {
    .profile-step-nav { flex-direction: column-reverse; align-items: stretch; }
    .profile-step-nav > .btn,
    .profile-step-nav__actions,
    .profile-step-nav__actions .btn { width: 100%; justify-content: center; text-align: center; }
    .profile-step-nav__actions { flex-direction: column; margin-left: 0; }
    .profile-step-hint { text-align: left; }
}

/* ---------- Terms and Conditions ---------- */

.terms-page { background: var(--bg-card); border-radius: 12px; margin: 0 auto 32px; max-width: 860px; padding: 32px; }
.terms-page__title { font-size: 28px; font-weight: 700; margin: 0 0 16px; }
.terms-page__meta { color: var(--t-muted); font-size: 14px; margin: 0 0 24px; }
.terms-body__heading { font-size: 16px; font-weight: 700; margin: 20px 0 8px; }
.terms-body__list { list-style: disc; margin: 0 0 8px; padding-left: 20px; }
.terms-body__list li { margin-bottom: 6px; }

.apply-terms { border-top: 1px solid var(--border); margin-top: 16px; padding-top: 14px; }
.apply-terms__check { align-items: flex-start; cursor: pointer; display: flex; font-weight: 600; gap: 8px; }
.apply-terms__check input { flex-shrink: 0; margin-top: 4px; }
.apply-terms__read {
    background: none;
    border: 0;
    color: var(--primary);
    cursor: pointer;
    font: inherit;
    margin: 6px 0 0 21px;
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
}

@media (max-width: 575.98px) { .terms-page { padding: 20px; } }

/* The rough local-currency figure under a course fee on the public pages. */
.course-fee-approx {
    color: var(--t-muted);
    display: block;
    font-size: 14px;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
}


/* SOW-CHANGE-NOTE-005: ticked schedules and the optional second preference
   in the Apply panel, and their summary in the confirmation popup. */
.apply-panel-schedules-hint {
    color: var(--t-muted);
    font-size: 12px;
    margin: -4px 0 8px;
}

.apply-panel-schedule-option input[type="checkbox"] {
    flex-shrink: 0;
    margin-top: 3px;
}

.apply-panel-schedule-single {
    font-size: 13px;
    margin: 12px 0 0;
}

.apply-panel-schedule-single .field-label {
    display: block;
    margin-bottom: 4px;
}

.apply-second-pref {
    border-top: 1px solid var(--border-soft);
    margin-top: 16px;
    padding-top: 12px;
}

.apply-second-pref-add,
.apply-second-pref-remove {
    background: none;
    border: 0;
    color: var(--primary);
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    padding: 0;
}

.apply-second-pref-add:hover,
.apply-second-pref-remove:hover {
    text-decoration: underline;
}

.apply-second-pref-head {
    align-items: center;
    display: flex;
    justify-content: space-between;
    margin-bottom: 4px;
}

.apply-second-pref-title {
    color: var(--t-base);
    font-size: 14px;
    font-weight: 700;
    margin: 0;
}

.apply-second-pref-help {
    color: var(--t-muted);
    font-size: 12px;
    margin: 0 0 10px;
}

.apply-confirm-summary {
    background: var(--bg-muted);
    border-radius: 10px;
    font-size: 13px;
    margin: 12px 0 0;
    padding: 10px 12px;
}

.apply-confirm-summary dt {
    color: var(--t-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.apply-confirm-summary dd {
    margin: 2px 0 8px;
}

.apply-confirm-summary dd:last-child {
    margin-bottom: 0;
}

/* Each chosen schedule on its own line, under its course. */
.apply-confirm-schedules {
    margin: 2px 0 0;
    padding-left: 18px;
}

.move-slot-option {
    align-items: center;
    display: flex;
    font-size: 13px;
    gap: 8px;
    margin-bottom: 6px;
}

.detail-second-pref {
    align-items: center;
    display: flex;
    gap: 8px;
    margin: 0 0 6px;
}

.detail-second-pref-slots {
    color: var(--t-muted);
    font-size: 13px;
    margin: 0;
    padding-left: 18px;
}
