/* =====================================================================
   Frontier Schools PD Portal
   Clean, professional district administrative styling (spec 39).
   ===================================================================== */

/* ---------------------------------------------------------------- tokens
   The Frontier Schools palette. Navy carries the interface; gold is an accent
   and never a surface - large gold areas fail contrast against every text
   colour we have, and the brand reads as gold precisely because it is used
   sparingly. Every component below draws from these, so the whole application
   re-skins from this block. */
:root {
    /* Brand */
    --pd-navy: #001842;
    --pd-navy-dark: #000f2b;
    --pd-navy-soft: #0b2a5c;
    --pd-gold: #ffb504;
    --pd-gold-dark: #d99700;

    /* Surfaces */
    --pd-bg: #fffcff;
    --pd-neutral: #ece7e5;
    --pd-neutral-dark: #ddd6d3;
    --pd-card: #ffffff;

    /* Text. Charcoal-navy rather than pure black, per the brand direction. */
    --pd-ink: #14203a;
    --pd-muted: #5c6880;

    /* Lines. Tinted towards navy so rules belong to the palette rather than
       reading as a separate grey system. */
    --pd-line: #e6e3e6;
    --pd-border: #cfd4de;
    --pd-border-strong: #9aa3b5;

    /* Kept so older rules referencing it stay on-brand; navy is the primary
       action colour now, not a separate blue. */
    --pd-blue: #001842;

    /* Radius */
    --pd-r-sm: 8px;
    --pd-r-md: 11px;
    /* Staff Evaluation runs a two-step scale - 8px and 20px - and the large
       step is what cards, tiles and modals wear, so a card here now has the
       same corner as a card there. --pd-r-xl predates that and is the same
       value; it is aliased rather than left as a second number to keep in
       step. */
    --pd-r-lg: 20px;
    --pd-r-xl: var(--pd-r-lg);

    /* Shadow: present but restrained. */
    --pd-shadow-sm: 0 1px 2px rgba(0, 24, 66, .06);
    --pd-shadow: 0 2px 8px rgba(0, 24, 66, .07);

    /* Shell */
    /* Matched to Staff Evaluation, so the navigation column is in the same
       place in both applications and the content edge does not shift when
       somebody moves between them. */
    --pd-sidebar-w: 222px;

    /* One gutter for the page, the header band and the footer, so the product
       name in the band sits directly above the page heading below it.

       It scales rather than stepping: the old 1.25rem was generous on a 375px
       phone, where it cost a tenth of the screen, and mean on a 27in monitor.
       The clamp gives a phone .75rem and a wide desktop 2.25rem, with a smooth
       run between - and it is the same expression Staff Evaluation uses. */
    --pd-gutter: clamp(.75rem, 1.6vw, 2.25rem);
}

html { font-size: 15px; }

/* The typeface is chosen by an administrator and delivered as --pd-font by
   the layout. The value here is the fallback for anything rendered before or
   outside that - and every option carries a system stack behind it, so the app
   still reads correctly if the font host is unreachable. */
body {
    background: var(--pd-bg);
    color: var(--pd-ink);
    font-family: var(--pd-font, "Bricolage Grotesque", -apple-system, BlinkMacSystemFont,
                 "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
    font-optical-sizing: auto;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* Tabular figures keep costs, hours and counts aligned in tables. */
.table, .pd-metric .value, .pd-meta { font-variant-numeric: tabular-nums; }

.pd-main { flex: 1 0 auto; }

/* ------------------------------ navbar ------------------------------ */
.pd-navbar {
    background: var(--pd-navy);
    box-shadow: 0 1px 3px rgba(16, 24, 40, .18);
}
.pd-navbar .navbar-brand,
.pd-navbar .nav-link { color: rgba(255, 255, 255, .86); font-weight: 500; }

/* Give the top-level items room to breathe rather than running together. */
@media (min-width: 992px) {
    .pd-navbar .navbar-nav > .nav-item > .nav-link { padding-left: .85rem; padding-right: .85rem; }
    .pd-navbar .navbar-nav.me-auto { margin-left: 1.25rem; }
    .pd-navbar .navbar-brand { margin-right: .5rem; }
}
.pd-navbar .navbar-brand { font-weight: 700; letter-spacing: .2px; }
.pd-navbar .nav-link:hover,
.pd-navbar .nav-link:focus { color: #fff; }
.pd-navbar .nav-link.active {
    color: #fff;
    box-shadow: inset 0 -2px 0 #7aa7ff;
}
.pd-navbar .navbar-toggler {
    border-color: rgba(255, 255, 255, .3);
}
.pd-navbar .navbar-toggler-icon {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255,255,255,0.85%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

/* White chip so the district's black-and-gold logo reads on the navy bar. */
.pd-logo-chip {
    display: inline-flex;
    align-items: center;
    background: #fff;
    border-radius: 7px;
    padding: 5px 9px;
    line-height: 0;
}
.pd-logo-chip img { display: block; height: 30px; width: auto; }
@media (max-width: 575.98px) { .pd-logo-chip img { height: 22px; } }

.pd-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; }

.pd-notif-menu { width: min(360px, 92vw); max-height: 70vh; overflow-y: auto; }
.pd-notif-item { display: block; padding: .5rem .9rem; border-bottom: 1px solid var(--pd-line); text-decoration: none; color: inherit; }
.pd-notif-item:hover { background: #f7f9fc; }
.pd-notif-item.unread { background: #eef4ff; }

/* ------------------------------- cards ------------------------------ */
.card {
    border: 1px solid var(--pd-border);
    border-radius: 10px;
    background: var(--pd-card);
    /* Clip children to the radius: without it a full-width header or table
       square off the top corners and the border looks broken there. */
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .06);
}
.card-header {
    background: #faf3ec;
    border-bottom: 1px solid var(--pd-border);
    font-weight: 600;
}

.pd-section-title {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 0 0 .75rem;
}

.pd-page-title { font-size: 1.4rem; font-weight: 700; margin-bottom: .15rem; }
.pd-page-sub { color: var(--pd-muted); font-size: .9rem; }

/* --------------------------- metric tiles --------------------------- */
.pd-metric {
    background: #fff;
    border: 1px solid var(--pd-border);
    border-radius: 10px;
    padding: .85rem 1rem;
    height: 100%;
}
.pd-metric .label {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--pd-muted);
    font-weight: 600;
}
.pd-metric .value { font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
.pd-metric.accent { background: var(--pd-navy); border-color: var(--pd-navy); color: #fff; }
.pd-metric.accent .label { color: rgba(255, 255, 255, .75); }

/* --------------------------- PD catalog cards ----------------------- */
.pd-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    border-color: var(--pd-border);
    transition: box-shadow .15s ease, border-color .15s ease;
}
.pd-card:hover {
    border-color: var(--pd-border-strong);
    box-shadow: 0 4px 14px rgba(16, 24, 40, .13);
}
.pd-card .pd-card-title {
    font-size: 1rem; font-weight: 650; line-height: 1.35; margin-bottom: .35rem;
}
.pd-card .pd-card-title a { color: var(--pd-ink); text-decoration: none; }
.pd-card .pd-card-title a:hover { color: var(--pd-blue); text-decoration: underline; }
.pd-meta { font-size: .82rem; color: var(--pd-muted); }
.pd-meta strong { color: var(--pd-ink); font-weight: 600; }

.pd-fav {
    border: none; background: transparent; color: #b9c0cb; font-size: 1.1rem; line-height: 1; padding: .15rem .3rem;
}
.pd-fav.on, .pd-fav:hover { color: #e0245e; }

/* ------------------------- workflow tracker ------------------------- */
.pd-track { display: flex; flex-wrap: wrap; gap: .25rem; align-items: stretch; }
.pd-track-step {
    flex: 1 1 130px;
    min-width: 120px;
    border: 1px solid var(--pd-line);
    border-radius: 8px;
    padding: .5rem .65rem;
    background: #fff;
    font-size: .8rem;
}
.pd-track-step .step-name { font-weight: 600; }
.pd-track-step .step-meta { color: var(--pd-muted); font-size: .75rem; }
.pd-track-step.done { border-color: #b7e4c7; background: #f0fbf4; }
.pd-track-step.current { border-color: #ffd68a; background: #fff9ec; }
.pd-track-step.rejected { border-color: #f5b5b5; background: #fdf1f1; }
.pd-track-icon { font-weight: 700; margin-right: .3rem; }

/* ------------------------------ timeline ---------------------------- */
.pd-timeline { list-style: none; margin: 0; padding: 0 0 0 1.1rem; border-left: 2px solid var(--pd-line); }
.pd-timeline li { position: relative; padding: 0 0 1rem 1rem; }
.pd-timeline li::before {
    content: ""; position: absolute; left: -1.42rem; top: .35rem;
    width: 10px; height: 10px; border-radius: 50%; background: #fff;
    border: 2px solid var(--pd-blue);
}
.pd-timeline .when { font-size: .78rem; color: var(--pd-muted); }
.pd-timeline .what { font-size: .9rem; }

/* ------------------------------- tables ----------------------------- */
.table { font-size: .88rem; }
.table > thead { background: #f7f9fc; }
.table > thead th {
    font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
    color: var(--pd-muted); font-weight: 700; border-bottom: 1px solid var(--pd-line);
    white-space: nowrap;
}
.table-hover > tbody > tr:hover > * { background-color: #f5f8ff; }

/* --------------------------- roomier tables -------------------------- */
/* The default Bootstrap cell padding is tight for long directory rows. */
.pd-table > thead > tr > th {
    padding: .7rem .85rem;
    background: #faf3ec;
    border-bottom: 2px solid var(--pd-border);
    vertical-align: middle;
}
.pd-table > tbody > tr > td {
    padding: .65rem .85rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--pd-line);
}
.pd-table > tbody > tr:last-child > td { border-bottom: 0; }

/* Sortable column headers */
.pd-table-sortable > thead > tr > th { padding: 0; }
.pd-table-sortable > thead > tr > th > a {
    display: block;
    padding: .7rem .85rem;
    color: var(--pd-muted);
    text-decoration: none;
    white-space: nowrap;
    font-size: .74rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 700;
}
.pd-table-sortable > thead > tr > th > a:hover { background: #eef3fa; color: var(--pd-ink); }
.pd-table-sortable > thead > tr > th.sorted > a { color: var(--pd-navy); background: #eaf1fb; }
.pd-sort { font-size: .7rem; opacity: .85; margin-left: .15rem; }

/* Route editor rows need a little breathing room too. */
.pd-route-editor .table > tbody > tr > td { padding: .4rem .5rem; }

/*
    The editor's <form> wraps the modal body and footer, so Save submits the whole
    route. In a scrollable modal, though, .modal-content lays out its direct child -
    the form - not the body. Left as a plain block the form grows to fit everything,
    the body never scrolls, and the footer with Save is pushed below the window: on
    the district route you had to zoom out to find it.

    Making the form a shrinking flex column puts the scroll back on the body and
    keeps the footer, and Save, pinned in view however long the route gets.
*/
.modal-dialog-scrollable .pd-route-editor {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

.modal-dialog-scrollable .pd-route-editor > .modal-body {
    min-height: 0;
    overflow-y: auto;
}

.modal-dialog-scrollable .pd-route-editor > .modal-footer { flex-shrink: 0; }

/* Beside Save once anything changes. Amber rather than red: nothing is wrong, the
   change just is not kept yet. */
.pd-route-dirty {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .25rem .65rem;
    border-radius: 999px;
    background: #fdf2e0;
    color: #8a5300;
}

.pd-route-dirty::before {
    content: "";
    width: .45rem;
    height: .45rem;
    border-radius: 50%;
    background: var(--pd-gold);
}

/* ------------------------------ filters ----------------------------- */
.pd-filters { position: sticky; top: 4.2rem; }
.pd-filter-group + .pd-filter-group {
    margin-top: .9rem;
    padding-top: .9rem;
    border-top: 1px solid var(--pd-border);
}

/* Inputs need a visible edge too, or the filter panel reads as flat text. */
.form-control, .form-select { border-color: var(--pd-border); }
.form-control:hover, .form-select:hover { border-color: var(--pd-border-strong); }
.form-check-input { border-color: var(--pd-border-strong); }
.pd-filter-group h6 {
    font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
    color: var(--pd-muted); font-weight: 700; margin-bottom: .5rem;
}

.form-label { font-size: .82rem; font-weight: 600; margin-bottom: .25rem; }
.form-text { font-size: .78rem; }

.badge { font-weight: 600; letter-spacing: .01em; }

.pd-source-badge { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; }

/* -------------------------- impersonation bar ----------------------- */
/* Deliberately loud: it must be impossible to forget whose account you are
   looking at, on every page, for the whole session. */
/* The navbar and the impersonation bar sit inside one sticky wrapper, so this
   is the only thing pinned to the top of the viewport. Above Bootstrap's
   .sticky-top default (1020) but below .modal (1055) and .dropdown-menu's own
   layer, so open menus are never covered. */
.pd-sticky-header { z-index: 1030; }

.pd-impersonation-bar {
    background: repeating-linear-gradient(135deg, #7a4b00, #7a4b00 12px, #8a5500 12px, #8a5500 24px);
    color: #fff;
    font-size: .85rem;
    padding: .55rem 0;
    border-bottom: 2px solid #5c3800;
    /* Deliberately not positioned: a positioned bar with its own z-index
       formed a stacking context that painted over the navbar's dropdowns. As
       static content it stays underneath them. */
}

/* ---------------------------- back button --------------------------- */
/* Collapse the bar entirely when the link inside it is hidden, so pages
   without history do not carry an empty strip of margin. */
.pd-back-bar:not(:has(.pd-back:not(.d-none))) { display: none; }
.pd-back { font-weight: 600; }

/* ------------------------------ footer ------------------------------ */
.pd-footer {
    flex-shrink: 0;
    background: #fff;
    border-top: 1px solid var(--pd-line);
    font-size: .8rem;
    color: var(--pd-muted);
}

/* --------------------------- sign-in page --------------------------- */
.pd-signin-wrap { min-height: 100vh; display: grid; place-items: center; background: var(--pd-navy); padding: 1rem; }
.pd-signin-card { width: min(420px, 100%); border-radius: 14px; }
.btn-google {
    background: #fff; border: 1px solid #dadce0; color: #3c4043; font-weight: 600;
    display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
}
.btn-google:hover { background: #f8f9fa; border-color: #d2d5d9; color: #3c4043; }

/* --------------------------- responsive ----------------------------- */
@media (max-width: 991.98px) {
    .pd-filters { position: static; }
}
@media (max-width: 575.98px) {
    html { font-size: 14.5px; }
    .pd-page-title { font-size: 1.2rem; }
    .pd-metric .value { font-size: 1.3rem; }
    /* Approval actions must stay easy to hit on a phone (spec 38). */
    .pd-review-actions .btn { width: 100%; }
}

.pd-review-actions .btn { min-height: 44px; }

@media print {
    .pd-navbar, .pd-footer, .pd-no-print { display: none !important; }
    body { background: #fff; }
}

/* ------------------------- dashboard sections ------------------------ */
/* The dashboard mixes three audiences: what is mine, what is my
   building's, and what is the district's. Grouping each under a labelled
   band keeps them from reading as one undifferentiated pile of cards. */
.pd-section { margin-top: 1.9rem; }
.pd-section:first-of-type { margin-top: 0; }
.pd-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .1rem .65rem;
    margin-bottom: .8rem;
    padding-bottom: .4rem;
    border-bottom: 1px solid var(--pd-border);
}
.pd-section-head h2 { font-size: 1.02rem; font-weight: 700; margin: 0; }
.pd-section-head .hint { color: var(--pd-muted); font-size: .82rem; }
.pd-section-head .pd-section-link { margin-left: auto; font-size: .85rem; white-space: nowrap; }

/* ------------------------ department picker ------------------------- */
/* Scrolls rather than pushing the step table off screen when a campus has
   a long department list. */
.pd-dept-picker {
    max-height: 9.5rem;
    overflow-y: auto;
    border: 1px solid var(--pd-border);
    border-radius: 8px;
    padding: .45rem .65rem;
    background: #fff;
    /* Grid, not CSS columns: multi-column inside a fixed height flows sideways
       and produces a horizontal scrollbar instead of wrapping. */
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .1rem 1.25rem;
    align-content: start;
}
.pd-dept-picker .form-check { margin-bottom: 0; font-size: .85rem; min-width: 0; }
.pd-dept-picker .form-check-label {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
.pd-dept-picker .pd-dept-empty { grid-column: 1 / -1; }
@media (max-width: 767.98px) { .pd-dept-picker { grid-template-columns: 1fr; } }

/* --------------------- clickable dashboard tiles -------------------- */
/* A tile that drills down says so: it lifts on hover and takes focus like
   the button it behaves as. */
.pd-metric[data-tile] { cursor: pointer; position: relative; }
.pd-metric[data-tile]:hover {
    border-color: var(--pd-border-strong);
    box-shadow: 0 2px 8px rgba(16, 24, 40, .1);
    transform: translateY(-1px);
}
.pd-metric[data-tile]:focus-visible {
    outline: 2px solid var(--pd-blue);
    outline-offset: 2px;
}
.pd-metric[data-tile]::after {
    content: "›";
    position: absolute;
    top: .5rem;
    right: .7rem;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--pd-muted);
    opacity: 0;
    transition: opacity .15s ease;
}
.pd-metric[data-tile]:hover::after,
.pd-metric[data-tile]:focus-visible::after { opacity: 1; }
.pd-metric.accent[data-tile]::after { color: rgba(255, 255, 255, .8); }
.pd-metric[data-tile] { transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease; }

.pd-tile-headline {
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1.1;
}

/* ------------------- collapsible dashboard sections ------------------ */
.pd-section-toggle {
    display: inline-flex;
    align-items: baseline;
    gap: .4rem;
    background: none;
    border: 0;
    padding: 0;
    color: inherit;
    cursor: pointer;
}
.pd-section-toggle:focus-visible { outline: 2px solid var(--pd-blue); outline-offset: 3px; }
.pd-section-toggle h2 { font-size: 1.02rem; font-weight: 700; margin: 0; }
.pd-caret {
    display: inline-block;
    width: .55rem;
    height: .55rem;
    border-right: 2px solid var(--pd-muted);
    border-bottom: 2px solid var(--pd-muted);
    transform: rotate(45deg);
    transition: transform .15s ease;
    align-self: center;
}
/* Bootstrap flips aria-expanded on the trigger; point the caret at the
   content when open and sideways when closed. */
.pd-section-toggle[aria-expanded="false"] .pd-caret { transform: rotate(-45deg); }
.pd-section-toggle:hover .pd-caret { border-color: var(--pd-ink); }

/* ----------------------- list column toolbars ----------------------- */
/* The filter column and the results column each open with one of these, so
   their cards start on the same line instead of the filter panel riding
   higher than the results by the height of the sort row. */
.pd-list-toolbar {
    display: flex;
    align-items: center;
    min-height: 31px; /* height of a .form-select-sm, which sets the row */
}

/* ---------------------- route coverage / staff ---------------------- */
.pd-staff-chosen {
    border: 1px solid var(--pd-border);
    border-radius: 8px;
    padding: .45rem .55rem;
    min-height: 2.4rem;
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    align-content: flex-start;
    background: #fff;
}
.pd-staff-chip { display: inline-flex; align-items: center; font-weight: 500; }
.pd-staff-chip .btn-close { font-size: .55rem; }

/* Long staff lists scroll inside the card rather than pushing the approval
   steps off the bottom of the dialog. */
.pd-coverage-list { max-height: 14rem; overflow-y: auto; }
.pd-coverage-list .table td { padding: .3rem .5rem; }

/* --------------------- multi-select dropdowns ----------------------- */
/* Looks like a form-select but holds checkboxes, so several options can be
   chosen without the ctrl-click a native <select multiple> demands. */
.pd-multi > .dropdown-toggle {
    background: #fff;
    border: 1px solid var(--pd-border);
    font-size: .8125rem;
    padding: .25rem .5rem;
    color: var(--pd-ink);
}
.pd-multi > .dropdown-toggle:hover { border-color: var(--pd-border-strong); }
.pd-multi > .dropdown-toggle::after { float: right; margin-top: .5em; }
.pd-multi .dropdown-menu { border-color: var(--pd-border); }
.pd-multi .form-check { margin-bottom: .1rem; }

/* ---------------- filter panel typography (larger) ------------------ */
/* The filter column was set two pixels smaller than comfortable; these
   override the small-control defaults for the panel only, leaving the rest
   of the app's density alone. */
.pd-filter-group h6 { font-size: .85rem; }
.pd-filters .form-label,
.pd-filters .form-check-label,
.pd-filters .form-text,
.pd-filters .form-control,
.pd-filters .form-select,
.pd-filters .dropdown-toggle,
.pd-filters .btn { font-size: .93rem; }
.pd-filters .form-check-input { margin-top: .25rem; }
.pd-list-toolbar .small { font-size: .93rem; }

/* Long option lists (300 cities) scroll instead of running off the screen. */
.pd-multi-menu { max-height: 17rem; overflow-y: auto; }
.pd-multi-option .form-check-label {
    white-space: normal;
    overflow-wrap: anywhere;
    line-height: 1.35;
}

/* -------------------- selected-value chips -------------------------- */
/* Under a multi-select: the whole selection, each item removable. The
   button above can only show one value before truncating. */
.pd-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    margin-top: .4rem;
}
.pd-chip {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    max-width: 100%;
    background: #eef3fa;
    border: 1px solid var(--pd-border);
    border-radius: 999px;
    padding: .1rem .25rem .1rem .55rem;
    font-size: .8rem;
    line-height: 1.4;
}
/* Long values are clipped on the chip but kept whole in the title. */
.pd-chip-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 13rem;
}
.pd-chip .btn-close { font-size: .5rem; padding: .25rem; flex: 0 0 auto; }

/* ========================= PD week calendar ========================== */
/* Days across, time down. Sessions are absolutely positioned by their start
   and length, which is what makes clashes and gaps visible at a glance. */
.pd-cal { --pd-cal-gutter: 62px; overflow-x: auto; }

.pd-cal-head,
.pd-cal-body {
    display: grid;
    /* Five weekday columns; the count is a variable so it follows the view
       rather than being asserted twice. */
    grid-template-columns: var(--pd-cal-gutter) repeat(var(--pd-cal-days, 5), minmax(150px, 1fr));
    min-width: 760px;
}

.pd-cal-head {
    position: sticky;
    top: 0;
    z-index: 3;
    background: var(--pd-neutral);
    border-bottom: 1px solid var(--pd-border);
}
.pd-cal-dayhead {
    padding: .45rem .5rem;
    border-left: 1px solid var(--pd-line);
    text-align: center;
    min-width: 0;
}
.pd-cal-dayhead .dow { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--pd-muted); }
.pd-cal-dayhead .dom { font-size: 1.15rem; font-weight: 700; line-height: 1.1; }
.pd-cal-dayhead .name { font-size: .72rem; color: var(--pd-ink); }
.pd-cal-dayhead.empty .dom, .pd-cal-dayhead.empty .dow { color: #9aa3af; }
.pd-cal-dayhead.today .dom { color: var(--pd-blue); }

.pd-cal-body { position: relative; height: var(--pd-cal-height); }

.pd-cal-gutter { position: relative; }
.pd-cal-hour {
    position: absolute;
    right: .4rem;
    transform: translateY(-.55em);
    font-size: .7rem;
    color: var(--pd-muted);
    white-space: nowrap;
}

.pd-cal-col {
    position: relative;
    border-left: 1px solid var(--pd-line);
    min-width: 0;
}
/* A day with no PD day cannot take a drop, and says so by being greyed. */
.pd-cal-col.empty { background: repeating-linear-gradient(45deg, #fafafa, #fafafa 8px, #f3f4f6 8px, #f3f4f6 16px); }
.pd-cal-col.today { background: #f7faff; }
.pd-cal-col.pd-drop-target { background: #eaf2ff; box-shadow: inset 0 0 0 2px var(--pd-blue); }

.pd-cal-line { position: absolute; left: 0; right: 0; border-top: 1px solid #e9edf2; }
.pd-cal-line.half { border-top-style: dotted; }

.pd-cal-event {
    position: absolute;
    border-radius: 6px;
    padding: .2rem .35rem;
    font-size: .74rem;
    line-height: 1.25;
    overflow: hidden;
    cursor: grab;
    border: 1px solid transparent;
    background: #dbeafe;
    border-color: #93c5fd;
    color: #10243e;
}
.pd-cal-event.campus { background: #dcfce7; border-color: #86efac; color: #123021; }
.pd-cal-event.required { border-left: 4px solid #1f2937; }
.pd-cal-event:hover { filter: brightness(.97); }
.pd-cal-event.dragging { opacity: .45; cursor: grabbing; }
.pd-cal-event .ev-time { font-size: .68rem; opacity: .8; }
.pd-cal-event .ev-title { font-weight: 600; }
.pd-cal-event .ev-meta { font-size: .67rem; opacity: .8; }
.pd-cal-event .ev-edit {
    position: absolute; top: 1px; right: 3px;
    font-size: .7rem; text-decoration: none; color: inherit; opacity: 0;
}
.pd-cal-event:hover .ev-edit { opacity: .75; }

/* The ghost that follows the cursor while dragging. */
.pd-cal-ghost {
    position: absolute;
    left: 2px; right: 2px;
    border: 2px dashed var(--pd-blue);
    background: rgba(37, 99, 235, .1);
    border-radius: 6px;
    pointer-events: none;
    font-size: .7rem;
    color: var(--pd-blue);
    font-weight: 600;
    padding: .1rem .3rem;
    z-index: 2;
}

.pd-cal-add {
    position: absolute;
    right: 4px; bottom: 4px;
    width: 22px; height: 22px;
    display: grid; place-items: center;
    border-radius: 50%;
    background: #fff;
    border: 1px solid var(--pd-border);
    color: var(--pd-muted);
    text-decoration: none;
    font-weight: 700;
    opacity: 0;
}
.pd-cal-col:hover .pd-cal-add { opacity: 1; }
.pd-cal-add:hover { background: var(--pd-blue); color: #fff; border-color: var(--pd-blue); }

/* ---------------------------------------------------------------- sync stamp
   Freshness of the catalog feed, shown in the footer of Find PD. It inherits
   the footer size, so it stays as quiet as the copyright line beside it. */
.pd-sync-stamp {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    color: var(--bs-secondary-color, #6c757d);
}

.pd-sync-stamp a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
}

.pd-sync-stamp a:hover { color: var(--pd-border-strong); }

/* The exact stamp sits beside the relative one: the relative form is what
   people read, the exact form is what they quote when reporting a problem. */
.pd-sync-exact { opacity: .8; }

.pd-sync-dot {
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    flex: 0 0 auto;
}

/* --------------------------------------------------------------- PD library
   The reusable-session list beside the week grid. It shares the row so the
   week and the library are visible together - the whole point is dragging
   from one to the other - and drops below the grid when there is no room. */
.pd-cal-shell {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}

.pd-cal-main { flex: 1 1 auto; min-width: 0; }

.pd-cal-library {
    flex: 0 0 260px;
    max-height: calc(100vh - 12rem);
    display: flex;
    flex-direction: column;
}

.pd-cal-library .card-body {
    overflow-y: auto;
    overflow-x: hidden;
    padding: .75rem;
}

@media (max-width: 1199.98px) {
    .pd-cal-shell { flex-wrap: wrap; }
    .pd-cal-library { flex: 1 1 100%; max-height: 22rem; }
}

.pd-lib-list { display: flex; flex-direction: column; gap: .35rem; }

.pd-lib-item {
    border: 1px solid var(--pd-line);
    border-radius: 6px;
    padding: .45rem .55rem;
    background: #fff;
    cursor: grab;
}

.pd-lib-item:hover { border-color: var(--pd-border); background: #fffdf9; }
.pd-lib-item.dragging { opacity: .5; }

.pd-lib-title { font-size: .8rem; font-weight: 600; }
.pd-lib-meta { font-size: .7rem; color: var(--bs-secondary-color, #6c757d); }

/* The column being dragged over, so a drop has an obvious target. */
.pd-cal-col.lib-target { background: #fff8e6; outline: 2px dashed var(--pd-border); outline-offset: -2px; }

/* ------------------------------------------------------- request form layout
   The form is read top to bottom and submitted at the foot, so the reference
   cards are kept tight and the action bar is given weight of its own. */
.pd-form-ref dl { line-height: 1.35; }
.pd-form-ref dd { margin-bottom: .25rem; }
.pd-form-ref dt { margin-bottom: .25rem; }

/* The approval route as a strip rather than a stacked list: it is context,
   not a field, and it was taking the height of a whole section. */
.pd-route-strip {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
}

.pd-route-step {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .75rem;
    line-height: 1.25;
    border: 1px solid var(--pd-line);
    border-radius: 6px;
    padding: .3rem .5rem;
    background: #fffdf9;
}

.pd-route-num {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: 50%;
    background: var(--pd-border);
    color: #fff;
    font-size: .65rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.pd-question {
    border: 1px solid var(--pd-line);
    border-radius: 6px;
    padding: .6rem .7rem;
    background: #fff;
}

/* The submit bar. Deliberately at the end of the document rather than pinned:
   the whole point is that it is reached by going through the form. */
.pd-form-actions {
    border-top: 2px solid var(--pd-border);
    padding-top: 1rem;
    margin-top: .5rem;
    margin-bottom: 2rem;
}

/* ------------------------------------------------------------- review band
   The count is the thing an approver is actually reading, so it is set as a
   numeral in its own right rather than as the first word of a sentence. */
.pd-review-band {
    background: var(--pd-navy);
    color: #fff;
}

.pd-review-count {
    font-size: 3.25rem;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.02em;
    /* Room on both sides: the numeral is the thing being read, and hard against
       the card edge on one side and a rule on the other it was cramped. */
    padding: 0 1.4rem 0 .9rem;
    border-right: 1px solid rgba(255, 255, 255, .25);
}

.pd-review-kicker {
    text-transform: uppercase;
    letter-spacing: .06em;
    font-size: .72rem;
    font-weight: 600;
    opacity: .75;
}

.pd-review-title {
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1.2;
}

.pd-review-sub { font-size: .82rem; opacity: .85; }

@media (max-width: 575.98px) {
    .pd-review-count { font-size: 2.5rem; padding-right: .75rem; }
    .pd-review-title { font-size: 1.1rem; }
}

/* =====================================================================
   FRONTIER APPLICATION SHELL
   Sidebar navigation, top context bar, page frame. Everything below is
   built from the tokens at the top of this file.
   ===================================================================== */

body.pd-app {
    display: block;
    background: var(--pd-bg);
}

.pd-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

@media (min-width: 1200px) {
    .pd-shell { margin-left: var(--pd-sidebar-w); }
}

/* --------------------------------- sidebar --------------------------- */
.pd-sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--pd-sidebar-w);
    background: var(--pd-navy);
    color: rgba(255, 255, 255, .82);
    display: flex;
    flex-direction: column;
    z-index: 1045;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Off-canvas below xl; the grid needs the width more than the navigation does. */
/* The drawer's shadow and the scrim are navy at 45%, not neutral black - the
   column it is casting from is navy, and a grey shadow read as dirt against
   it. */
@media (max-width: 1199.98px) {
    .pd-sidebar {
        transform: translateX(-100%);
        transition: transform .18s ease;
        box-shadow: 0 0 40px rgba(0, 24, 66, .45);
    }
    .pd-sidebar.open { transform: none; }
}

/* Somebody who has asked their system to stop animating gets the drawer
   placed rather than slid. */
@media (prefers-reduced-motion: reduce) {
    .pd-sidebar { transition: none; }
}

.pd-sidebar-scrim {
    position: fixed;
    inset: 0;
    background: rgba(0, 24, 66, .45);
    z-index: 1040;
}

.pd-sidebar-brand {
    padding: 1rem 1rem .85rem;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
}

.pd-sidebar-brand a {
    display: flex;
    align-items: center;
    gap: .7rem;
    text-decoration: none;
    color: #fff;
}

/* The wordmark is white-on-gold artwork, so it keeps its own chip rather than
   sitting directly on the navy where the white would disappear. */
.pd-sidebar .pd-logo-chip {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    border-radius: var(--pd-r-md);
    background: var(--pd-gold);
    display: grid;
    place-items: center;
    overflow: hidden;
    padding: 5px;
}

.pd-sidebar .pd-logo-chip img { width: 100%; height: auto; display: block; }

.pd-brand-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.pd-brand-name { font-weight: 700; font-size: .95rem; letter-spacing: -.01em; }
.pd-brand-product {
    font-size: .62rem;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--pd-gold);
    font-weight: 600;
}

.pd-nav { padding: .35rem .6rem 1.5rem; }

.pd-nav-group {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .11em;
    font-weight: 700;
    color: rgba(255, 255, 255, .45);
    padding: .9rem .6rem .35rem;
}

.pd-nav-link {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .58rem .75rem;
    margin-bottom: 1px;
    border-radius: var(--pd-r-sm);
    /* A blue-tinted off-white rather than flat white at 82%: the same value
       Staff Evaluation uses, so a resting item reads identically in both. */
    color: #c6d5e8;
    text-decoration: none;
    font-size: .875rem;
    font-weight: 500;
    line-height: 1.3;
}

.pd-nav-link svg {
    flex: 0 0 auto;
    width: 1.1rem;
    height: 1.1rem;
    fill: currentColor;
    opacity: .8;
}

.pd-nav-link:hover {
    background: rgba(255, 255, 255, .09);
    color: #fff;
}

/* Its own ring rather than borrowing the hover wash. Hover and focus reading
   the same way meant a keyboard user could not tell which item they were on
   from one the pointer happened to be over. Inset so it is not clipped by the
   scrolling column. */
.pd-nav-link:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -2px;
}

/* Active state: a gold rail plus a lifted background. The rail carries the
   meaning on its own, so it does not depend on colour alone. */
.pd-nav-link.active {
    background: rgba(255, 255, 255, .13);
    color: #fff;
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--pd-gold);
}

.pd-nav-link.active svg { opacity: 1; color: var(--pd-gold); }

.pd-nav-sub {
    font-size: .82rem;
    padding-left: 2.15rem;
    /* The resting colour stepped back, so a child item sits below its parent
       rather than beside it in a different family. */
    color: rgba(198, 213, 232, .78);
}

.pd-nav-toggle .pd-nav-caret {
    margin-left: auto;
    transition: transform .15s ease;
    transform: rotate(90deg);
    opacity: .6;
}

.pd-nav-toggle.collapsed .pd-nav-caret { transform: none; }

/* --------------------------------- topbar ---------------------------- */
.pd-topbar {
    position: sticky;
    top: 0;
    z-index: 1030;
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .6rem 1.25rem;
    background: var(--pd-card);
    border-bottom: 1px solid var(--pd-line);
}

.pd-context { display: flex; gap: .35rem; flex: 0 0 auto; }

.pd-context-item {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    padding: .25rem .7rem;
    border-radius: var(--pd-r-sm);
    background: var(--pd-neutral);
}

.pd-context-label {
    font-size: .58rem;
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 700;
    color: var(--pd-muted);
}

.pd-context-value { font-size: .8rem; font-weight: 600; color: var(--pd-navy); }

/* Where the search box used to be: the product name over the page name, so
   the strip answers both "what is this" and "where am I" instead of repeating
   a control that already exists on the one page that needs it. */
.pd-topbar-heading {
    flex: 1 1 auto;
    min-width: 0;
    line-height: 1.15;
}

/* The only place the product is named now, so it carries more weight than a
   caption would. Set in px rather than rem because it is a fixed size that was
   chosen by eye, not one that should scale with the root. */
.pd-topbar-product {
    font-size: 20px;
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 700;
    color: var(--pd-navy);
    opacity: .7;
}

.pd-topbar-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--pd-muted);
    letter-spacing: 0;
}

.pd-search {
    position: relative;
    flex: 1 1 auto;
    max-width: 420px;
    margin-inline: auto;
}

.pd-search svg {
    position: absolute;
    left: .7rem;
    top: 50%;
    transform: translateY(-50%);
    width: 15px;
    height: 15px;
    fill: var(--pd-muted);
    pointer-events: none;
}

.pd-search .form-control {
    padding-left: 2.1rem;
    border-radius: 999px;
    background: var(--pd-neutral);
    border-color: transparent;
    font-size: .85rem;
}

.pd-search .form-control:focus {
    background: #fff;
    border-color: var(--pd-navy);
    box-shadow: 0 0 0 3px rgba(0, 24, 66, .12);
}

/* The heading absorbs the free space, so the context chips and the actions
   settle together at the right end rather than drifting apart. */
.pd-topbar-actions { display: flex; align-items: center; gap: .4rem; }

.pd-icon-btn {
    display: inline-grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: var(--pd-r-sm);
    background: transparent;
    color: var(--pd-navy);
}

.pd-icon-btn svg { width: 18px; height: 18px; fill: currentColor; }
.pd-icon-btn:hover { background: var(--pd-neutral); }

/* Gold reads as "something new" without needing red-for-alarm. */
.pd-bell-count {
    position: absolute;
    top: 3px;
    right: 3px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--pd-gold);
    color: var(--pd-navy);
    font-size: .65rem;
    font-weight: 800;
    line-height: 17px;
    text-align: center;
}

.pd-user {
    display: flex;
    align-items: center;
    gap: .5rem;
    border: 0;
    background: transparent;
    border-radius: var(--pd-r-sm);
    padding: .25rem .5rem;
    color: var(--pd-navy);
    font-size: .85rem;
    font-weight: 600;
}

.pd-user:hover { background: var(--pd-neutral); }
.pd-user-caret { font-size: .7rem; opacity: .6; }

.pd-avatar {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    object-fit: cover;
}

.pd-avatar-initial {
    display: grid;
    place-items: center;
    background: var(--pd-navy);
    color: #fff;
    font-weight: 700;
    font-size: .8rem;
}

/* ---------------------------------- page ----------------------------- */
/* No max-width: this is a data platform, and on a wide monitor a 1600px cap
   left several hundred pixels of empty margin on each side while the tables
   inside were still scrolling horizontally. The gutter grows a little on very
   wide screens so content does not run to the very edge. */
.pd-page {
    width: 100%;
    padding-inline: var(--pd-gutter);
}

.pd-main { flex: 1 0 auto; padding-block: 1.25rem; }

.pd-page-title {
    font-size: 1.55rem;
    font-weight: 700;
    color: var(--pd-navy);
    letter-spacing: -.015em;
    margin-bottom: .15rem;
}

.pd-page-sub { color: var(--pd-muted); font-size: .88rem; }

.pd-footer {
    background: transparent;
    border-top: 1px solid var(--pd-line);
    color: var(--pd-muted);
    font-size: .78rem;
}

.pd-impersonation-bar {
    background: var(--pd-gold);
    color: var(--pd-navy);
    font-size: .82rem;
    padding: .5rem 1.25rem;
    font-weight: 500;
}

/* =====================================================================
   FRONTIER COMPONENTS
   These come last on purpose: they restate the shared components in the
   terms of the brand, so the palette above is the single source of truth
   and older rules do not have to be hunted down one at a time.
   ===================================================================== */

/* -------------------------------- type ------------------------------- */
h1, h2, h3, h4, h5, h6 { color: var(--pd-navy); font-weight: 700; letter-spacing: -.01em; }
h1 { font-size: 1.55rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.1rem; }
a { color: var(--pd-navy); text-decoration-color: rgba(0, 24, 66, .3); }
a:hover { color: var(--pd-navy-soft); }

.pd-section-title { color: var(--pd-navy); }

/* Small uppercase category labels, as in the brand lockup. */
.pd-eyebrow, .pd-metric .label, .pd-context-label, .pd-nav-group {
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 700;
}

/* -------------------------------- cards ------------------------------ */
.card {
    border: 1px solid var(--pd-line);
    border-radius: var(--pd-r-lg);
    box-shadow: var(--pd-shadow-sm);
}

.card-header {
    background: var(--pd-card);
    border-bottom: 1px solid var(--pd-line);
    color: var(--pd-navy);
    font-weight: 600;
    font-size: .92rem;
    padding: .75rem 1rem;
}

/* A secondary card: filters, empty states, anything supporting rather than
   primary content. */
.pd-card-muted { background: var(--pd-neutral); border-color: transparent; }

/* ---------------------------- metric tiles --------------------------- */
.pd-metric {
    border: 1px solid var(--pd-line);
    border-radius: var(--pd-r-lg);
    padding: 1rem 1.1rem;
    box-shadow: var(--pd-shadow-sm);
}

.pd-metric .label { font-size: .68rem; color: var(--pd-muted); }

.pd-metric .value {
    font-size: 1.8rem;
    font-weight: 700;
    color: var(--pd-navy);
    letter-spacing: -.02em;
}

.pd-metric.accent {
    background: var(--pd-navy);
    border-color: var(--pd-navy);
}
.pd-metric.accent .value { color: #fff; }
.pd-metric.accent .label { color: rgba(255, 255, 255, .7); }

/* A gold hairline marks the tile that matters on a row of otherwise equal
   tiles, without turning the card itself yellow. */
.pd-metric.highlight { border-top: 3px solid var(--pd-gold); }

/* ------------------------------ catalog cards ------------------------ */
.pd-card { border-radius: var(--pd-r-lg); }
.pd-card .pd-card-title a { color: var(--pd-navy); }
.pd-card .pd-card-title a:hover { color: var(--pd-navy-soft); }

/*
    Hover needs to be unmistakable on this page: the results are a dense grid
    of similar cards, and the only thing telling you which one you are about to
    click is the card itself.

    The brand pass had reduced hover to var(--pd-shadow), which is the same
    shadow the card already carries at rest - so hovering changed almost
    nothing. This restores a real change of state: the border goes navy, the
    shadow deepens and spreads, and the card lifts slightly.

    :focus-within matches, so tabbing through the grid shows the same thing
    rather than leaving keyboard users to track a thin focus ring alone.
*/
.pd-card {
    transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease;
}

.pd-card:hover,
.pd-card:focus-within {
    border-color: var(--pd-navy);
    box-shadow: 0 6px 18px rgba(0, 24, 66, .16), 0 2px 6px rgba(0, 24, 66, .10);
    transform: translateY(-2px);
}

/* A gold rule along the top edge - the brand accent used as a thin marker
   rather than a fill, which keeps it off the "no large yellow areas" rule
   and keeps every text contrast on the card unchanged. */
.pd-card::before {
    content: "";
    display: block;
    flex: 0 0 auto;   /* a flex item on a column card - must not be squeezed */
    height: 3px;
    margin: -1px -1px 0;
    border-radius: var(--pd-r-lg) var(--pd-r-lg) 0 0;
    background: var(--pd-gold);
    opacity: 0;
    transition: opacity .15s ease;
}

.pd-card:hover::before,
.pd-card:focus-within::before {
    opacity: 1;
}

/* The lift is decoration; the border, shadow and gold rule carry the meaning,
   so dropping the movement loses nothing for anyone who asks for less of it. */
@media (prefers-reduced-motion: reduce) {
    .pd-card { transition: box-shadow .15s ease, border-color .15s ease; }
    .pd-card:hover, .pd-card:focus-within { transform: none; }
}

/* ------------------------------- buttons ----------------------------- */
.btn {
    border-radius: var(--pd-r-md);
    font-weight: 600;
    font-size: .85rem;
    padding: .45rem .95rem;
}

.btn-lg { border-radius: var(--pd-r-md); font-size: .95rem; padding: .6rem 1.3rem; }
.btn-sm { border-radius: var(--pd-r-sm); font-size: .78rem; padding: .3rem .65rem; }

.btn-primary {
    background: var(--pd-navy);
    border-color: var(--pd-navy);
    color: #fff;
}
.btn-primary:hover, .btn-primary:focus {
    background: var(--pd-navy-soft);
    border-color: var(--pd-navy-soft);
    color: #fff;
}
.btn-primary:active, .btn-check:checked + .btn-primary {
    background: var(--pd-navy-dark) !important;
    border-color: var(--pd-navy-dark) !important;
}

.btn-outline-primary, .btn-outline-secondary, .btn-outline-dark {
    background: var(--pd-card);
    color: var(--pd-navy);
    border-color: var(--pd-border);
}
.btn-outline-primary:hover, .btn-outline-secondary:hover, .btn-outline-dark:hover {
    background: var(--pd-neutral);
    color: var(--pd-navy);
    border-color: var(--pd-border-strong);
}

.btn-secondary { background: var(--pd-navy); border-color: var(--pd-navy); }

/* Gold is for a highlighted action, and always carries navy text: gold with
   white text fails contrast at every size we use. */
.btn-accent {
    background: var(--pd-gold);
    border-color: var(--pd-gold);
    color: var(--pd-navy);
}
.btn-accent:hover { background: var(--pd-gold-dark); border-color: var(--pd-gold-dark); color: var(--pd-navy); }

.btn-light { background: #fff; border-color: var(--pd-border); color: var(--pd-navy); }
.btn-link { color: var(--pd-navy); font-weight: 600; }

.btn:focus-visible { box-shadow: 0 0 0 3px rgba(0, 24, 66, .2); }

/* Destructive stays red - never gold, which reads as a highlight. */
.btn-danger { background: #b42318; border-color: #b42318; }
.btn-danger:hover { background: #912018; border-color: #912018; }

/* ------------------------------- forms ------------------------------- */
.form-control, .form-select {
    border-radius: var(--pd-r-md);
    border-color: var(--pd-border);
    font-size: .87rem;
    color: var(--pd-ink);
}

.form-control:focus, .form-select:focus {
    border-color: var(--pd-navy);
    box-shadow: 0 0 0 3px rgba(0, 24, 66, .12);
}

.form-control-sm, .form-select-sm { border-radius: var(--pd-r-sm); }

.form-label {
    font-size: .78rem;
    font-weight: 600;
    color: var(--pd-navy);
    margin-bottom: .25rem;
}

.form-text { font-size: .76rem; color: var(--pd-muted); }

.form-check-input:checked { background-color: var(--pd-navy); border-color: var(--pd-navy); }
.form-check-input:focus { border-color: var(--pd-navy); box-shadow: 0 0 0 3px rgba(0, 24, 66, .12); }

/* ------------------------------- tables ------------------------------ */
.table { --bs-table-border-color: var(--pd-line); margin-bottom: 0; }

.table > thead > tr > th {
    background: var(--pd-neutral);
    color: var(--pd-navy);
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    font-weight: 700;
    border-bottom: 1px solid var(--pd-border);
    padding: .6rem .75rem;
    white-space: nowrap;
}

.table > tbody > tr > td {
    border-bottom: 1px solid var(--pd-line);
    padding: .6rem .75rem;
    vertical-align: middle;
    font-size: .85rem;
}

.table > tbody > tr:last-child > td { border-bottom: 0; }

/* A very light navy tint rather than a grey wash, so hover and selection read
   as the same family as the rest of the interface. */
.table-hover > tbody > tr:hover > * { background-color: rgba(0, 24, 66, .035); }
.table > tbody > tr.selected > * { background-color: rgba(0, 24, 66, .06); }

/* Headers stay put on the long data tables. */
.pd-table-sticky thead th { position: sticky; top: 0; z-index: 2; }

/* ------------------------------- badges ------------------------------ */
.badge {
    border-radius: 999px;
    font-weight: 600;
    font-size: .7rem;
    padding: .3em .7em;
    letter-spacing: .01em;
}

.pd-badge-neutral { background: var(--pd-neutral); color: var(--pd-navy); }
.badge.bg-secondary { background: var(--pd-neutral) !important; color: var(--pd-navy); }
.badge.bg-primary { background: var(--pd-navy) !important; }
.badge.bg-light { background: var(--pd-neutral) !important; color: var(--pd-navy) !important; }
.badge.bg-dark { background: var(--pd-navy) !important; }

/* Gold badge for a highlighted state; navy text keeps it legible. */
.pd-badge-accent { background: var(--pd-gold); color: var(--pd-navy); }

/* Muted, professional semantics rather than saturated alert colours. */
.badge.bg-success { background: #0f7b4f !important; }
.badge.bg-danger { background: #b42318 !important; }
.badge.bg-warning { background: #b7791f !important; color: #fff !important; }
.badge.bg-info { background: var(--pd-navy-soft) !important; color: #fff !important; }

/* ------------------------------- alerts ------------------------------ */
.alert { border-radius: var(--pd-r-md); border-width: 1px; font-size: .87rem; }
.alert-success { background: #eef7f2; border-color: #bfe0d0; color: #0b5d3b; }
.alert-danger { background: #fdf2f1; border-color: #f0c4c0; color: #8c1d13; }
.alert-warning { background: #fff8e8; border-color: var(--pd-gold); color: #6b4a00; }
.alert-info { background: #eef2f9; border-color: #c4cfe3; color: var(--pd-navy); }
.alert-light { background: var(--pd-neutral); border-color: var(--pd-line); color: var(--pd-ink); }

/* ------------------------------ dropdowns ---------------------------- */
.dropdown-menu {
    border-radius: var(--pd-r-md);
    border-color: var(--pd-line);
    box-shadow: var(--pd-shadow);
    font-size: .85rem;
    padding: .35rem;
}
.dropdown-item { border-radius: var(--pd-r-sm); padding: .4rem .6rem; }
.dropdown-item:hover, .dropdown-item:focus { background: var(--pd-neutral); color: var(--pd-navy); }
.dropdown-header {
    font-size: .65rem;
    text-transform: uppercase;
    letter-spacing: .09em;
    font-weight: 700;
    color: var(--pd-muted);
}

/* -------------------------------- misc ------------------------------- */
.modal-content { border-radius: var(--pd-r-lg); border-color: var(--pd-line); }
.modal-header, .modal-footer { border-color: var(--pd-line); }
.nav-tabs { border-bottom-color: var(--pd-line); }
.nav-tabs .nav-link { border-radius: var(--pd-r-sm) var(--pd-r-sm) 0 0; color: var(--pd-muted); font-weight: 600; font-size: .85rem; }
.nav-tabs .nav-link.active { color: var(--pd-navy); border-color: var(--pd-line) var(--pd-line) #fff; box-shadow: inset 0 2px 0 var(--pd-gold); }
.page-link { color: var(--pd-navy); border-color: var(--pd-line); }
.page-item.active .page-link { background: var(--pd-navy); border-color: var(--pd-navy); }
.progress-bar { background: var(--pd-navy); }
.pd-notif-item.unread { background: #fff8e8; box-shadow: inset 3px 0 0 var(--pd-gold); }
.pd-notif-item:hover { background: var(--pd-neutral); }

/* The old top navbar is gone; this keeps the rule from colouring anything if
   a stray element still carries the class. */
.pd-navbar { display: none; }

/* The review band and calendar already read from --pd-navy, so they follow
   the palette. These are the few places that named a colour directly. */
.pd-review-band { background: var(--pd-navy); }
.pd-review-count { color: var(--pd-gold); }
.pd-sync-stamp a:hover { color: var(--pd-navy); }
.pd-lib-item { border-radius: var(--pd-r-sm); }
.pd-lib-item:hover { border-color: var(--pd-border-strong); background: var(--pd-neutral); }
.pd-question { border-radius: var(--pd-r-md); border-color: var(--pd-line); }
.pd-route-step { border-color: var(--pd-line); background: var(--pd-neutral); border-radius: var(--pd-r-sm); }
.pd-route-num { background: var(--pd-navy); }
.pd-form-actions { border-top: 2px solid var(--pd-navy); }
.pd-track-step { border-radius: var(--pd-r-sm); }

/* ---------------------------------------------------------- brand lockup
   The supplied artwork is the full horizontal logo. It is never recoloured,
   cropped or squeezed: it sits whole on a white panel with clear space around
   it, and the product name sits underneath as type rather than as part of the
   mark. */
.pd-sidebar-brand a {
    display: block;
    text-align: left;
}

.pd-logo-panel {
    display: block;
    background: #fff;
    border-radius: var(--pd-r-md);
    /* Deeper below the mark than above it: the lockup is optically top-heavy,
       so even padding reads as though it is sitting low in the panel. */
    padding: .6rem .7rem .95rem;
    margin-bottom: .55rem;
}

/* The panel spans the sidebar, but the lockup inside it is capped: filling
   the full 220px made the mark 113px tall, which swamped the product name
   underneath and pushed the first nav item most of the way down the screen.
   Capped by height, so the aspect ratio does the rest. */
.pd-logo-panel img {
    display: block;
    width: auto;
    max-width: 100%;
    max-height: 62px;
    margin: 0 auto;
}

/* Two lines across the full column. The words are long, and forcing them onto
   one would mean shrinking them below the size that makes the product name
   legible - so the type fills the column and breaks where it falls. */
.pd-sidebar-brand .pd-brand-product {
    display: block;
    width: 100%;
    font-size: .86rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 700;
    line-height: 1.25;
    color: var(--pd-gold);
    overflow-wrap: break-word;
    /* Centred under the panel, which is itself centred on the mark. */
    text-align: center;
}

/* The sign-in card shows the same lockup, so it gets the same clear space. */
.pd-signin-card img { max-width: 240px; }

/* ------------------------------------------------------- calendar tiles
   District-wide and campus sessions are told apart by tint depth within the
   navy family rather than by two unrelated hues, and "required" carries a
   gold rail so the distinction is not colour alone. */
.pd-cal-event.district {
    background: #e8edf6;
    border-color: #c3cee3;
    color: var(--pd-navy);
}

.pd-cal-event.campus {
    background: var(--pd-neutral);
    border-color: var(--pd-neutral-dark);
    color: var(--pd-ink);
}

.pd-cal-event.required { border-left: 4px solid var(--pd-gold); }
.pd-cal-event { border-radius: var(--pd-r-sm); }

.pd-cal-ghost {
    border-color: var(--pd-navy);
    background: rgba(0, 24, 66, .08);
    border-radius: var(--pd-r-sm);
}

.pd-cal-col.lib-target {
    background: #fff8e8;
    outline-color: var(--pd-gold);
}

/* Workflow tracker: muted semantics in the brand family. */
.pd-track-step.done { border-color: #bfe0d0; background: #f2f9f5; }
.pd-track-step.current { border-color: var(--pd-gold); background: #fff8e8; }
.pd-track-step.rejected { border-color: #f0c4c0; background: #fdf4f3; }

/* The sign-in card picks up the shell palette too. */
.pd-signin-wrap { background: var(--pd-bg); }
.pd-signin-card { border-radius: var(--pd-r-xl); border: 1px solid var(--pd-line); }
.btn-google {
    background: #fff;
    border: 1px solid var(--pd-border);
    color: var(--pd-navy);
    font-weight: 600;
    border-radius: var(--pd-r-md);
}
.btn-google:hover { background: var(--pd-neutral); border-color: var(--pd-border-strong); }

/* ------------------------------------------------------- catalog density
   Two or three cards to a row, chosen by the reader and remembered. Three
   fits more on screen; two makes every card half a row wide, which is the
   difference between readable and squinting for a lot of people. */
.pd-result-col { width: 100%; }

@media (min-width: 768px) {
    /* One-up stays one-up at every width: somebody who chose it wants the
       largest card, not a second column as soon as there is room. */
    .pd-results.cols-1 > .pd-result-col { width: 100%; }
    .pd-results.cols-2 > .pd-result-col { width: 50%; }
    .pd-results.cols-3 > .pd-result-col { width: 50%; }
}

@media (min-width: 1400px) {
    .pd-results.cols-3 > .pd-result-col { width: 33.3333%; }
}

/* The chosen density is pressed, so the control says which one is active
   rather than relying on the layout to imply it. */
.pd-density .btn[aria-pressed="true"] {
    background: var(--pd-navy);
    border-color: var(--pd-navy);
    color: #fff;
}

/* ------------------------------------------------------ catalog text size
   A scale factor on the results grid, applied to the type inside the cards.
   Everything is expressed against one variable so the whole card grows in
   proportion rather than one line of it pulling away from the rest. */
.pd-results { --pd-card-em: 1; }

.pd-results .pd-card-title { font-size: calc(1rem * var(--pd-card-em)); }
.pd-results .pd-meta { font-size: calc(.82rem * var(--pd-card-em)); }
.pd-results .badge { font-size: calc(.7rem * var(--pd-card-em)); }
.pd-results .btn { font-size: calc(.8rem * var(--pd-card-em)); }
.pd-results .small, .pd-results small { font-size: calc(.8rem * var(--pd-card-em)); }

.pd-textsize .btn { font-weight: 700; min-width: 2.4rem; }

/* At the largest step the buttons would otherwise wrap awkwardly on a narrow
   toolbar; the control is the one thing that must stay reachable. */
.pd-list-toolbar { row-gap: .5rem; }

/* ------------------------------------------------------- icon row actions
   A square button carrying an icon only, for actions that repeat on every
   row of a table. The label lives in title and aria-label, so the meaning is
   available to a screen reader and on hover rather than being carried by the
   glyph alone. */
.pd-icon-action {
    display: inline-grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    vertical-align: middle;
}

.pd-icon-action svg {
    width: 15px;
    height: 15px;
    fill: currentColor;
}

/* ------------------------------------------------------------ app switcher
   The grid of other Frontier applications, opened from the header. Tiles
   rather than a list: these are destinations, and a person recognises them by
   shape and colour long before they read the name. */
/* ---------------------------------------------------- app switcher panel
   A panel that floats in from the right rather than a dropdown, so the list
   can grow without running off the bottom of the screen. It is inset from the
   edges of the window with rounded corners, which is what separates it from
   the page as a surface of its own instead of a slab bolted to the edge.

   Bootstrap's offcanvas does the opening, focus trap and Escape; everything
   here is appearance. */
.pd-apps-panel {
    --bs-offcanvas-width: min(380px, calc(100vw - 1.5rem));
    --bs-offcanvas-bg: var(--pd-card);
    --bs-offcanvas-border-width: 0;
    --pd-apps-inset: .75rem;

    top: var(--pd-apps-inset);
    right: var(--pd-apps-inset);
    bottom: var(--pd-apps-inset);
    height: auto;
    border-radius: var(--pd-r-lg);
    overflow: hidden;
    color: var(--pd-ink);
    box-shadow:
        0 24px 60px rgba(0, 24, 66, .28),
        0 4px 14px rgba(0, 24, 66, .12);
    /* An ease-out that settles rather than stops, so it glides in. */
    transition: transform .32s cubic-bezier(.22, .9, .24, 1);
}

/* Closed, it has to clear its own inset as well as its width - otherwise the
   rounded edge would sit a few pixels inside the window. Open and opening are
   restated with the same weight so this rule does not hold them shut. */
/* The inset is set here too, at the same weight as Bootstrap's .offcanvas-end,
   which pins the panel with top: 0 and right: 0 and otherwise wins - leaving
   the rounded corners pressed flat against the window. */
.pd-apps-panel.offcanvas-end {
    top: var(--pd-apps-inset);
    right: var(--pd-apps-inset);
    bottom: var(--pd-apps-inset);
    transform: translateX(calc(100% + 2 * var(--pd-apps-inset)));
}
.pd-apps-panel.offcanvas-end.showing,
.pd-apps-panel.offcanvas-end.show:not(.hiding) { transform: none; }

/* The page behind softens rather than going dark: the panel is a quick hop to
   another tool, not a dialog demanding a decision. */
.offcanvas-backdrop.show { opacity: 1; background: rgba(0, 24, 66, .28); }
@supports (backdrop-filter: blur(2px)) {
    .offcanvas-backdrop.show { backdrop-filter: blur(2px); }
}

/* Header in the navy of the top bar, with the gold hairline the page header
   uses, so the panel reads as part of the same family. */
.pd-apps-panel .offcanvas-header {
    position: relative;
    padding: 1.05rem 1.15rem;
    background: var(--pd-navy);
    color: #fff;
    border-bottom: 0;
}

.pd-apps-panel .offcanvas-header::after {
    content: "";
    position: absolute;
    left: 1.15rem;
    right: 1.15rem;
    bottom: 0;
    height: 2px;
    border-radius: 2px;
    background: var(--pd-gold);
    opacity: .9;
}

.pd-apps-panel .offcanvas-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: .02em;
    color: #fff;
}

/* Bootstrap's close icon is drawn dark; on navy it has to be light. */
.pd-apps-panel .btn-close {
    filter: invert(1) grayscale(1) brightness(2);
    opacity: .75;
    border-radius: 50%;
}
.pd-apps-panel .btn-close:hover { opacity: 1; }
.pd-apps-panel .btn-close:focus-visible { box-shadow: 0 0 0 3px rgba(255, 181, 4, .6); opacity: 1; }

.pd-apps-panel .offcanvas-body {
    padding: 1.1rem 1rem 1.25rem;
    background: var(--pd-bg);
    scrollbar-width: thin;
}

.pd-apps-section + .pd-apps-section { margin-top: 1.25rem; }

/* Small uppercase label with a rule running out to the edge - reads as a
   heading at a glance without competing with the application names. */
.pd-apps-group {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin: 0 .15rem .6rem;
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--pd-muted);
}
.pd-apps-group::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--pd-line);
}

.pd-apps-footer {
    padding: .7rem 1rem .8rem;
    background: var(--pd-card);
    border-top: 1px solid var(--pd-line);
    text-align: center;
}

.pd-apps-footer a {
    display: inline-block;
    padding: .4rem .95rem;
    border-radius: 999px;
    font-size: .8rem;
    font-weight: 600;
    color: var(--pd-navy);
    text-decoration: none;
    background: var(--pd-neutral);
    transition: background .15s ease;
}
.pd-apps-footer a:hover { background: var(--pd-neutral-dark); }

.pd-apps-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .55rem;
}

/* Each application is a small card of its own: white on the tinted body,
   rounded, lifting slightly under the pointer. */
.pd-app-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .45rem;
    padding: .8rem .35rem .7rem;
    border-radius: var(--pd-r-md);
    background: var(--pd-card);
    border: 1px solid var(--pd-line);
    text-decoration: none;
    color: var(--pd-navy);
    text-align: center;
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}

.pd-app-tile:hover {
    color: var(--pd-navy);
    border-color: transparent;
    transform: translateY(-2px);
    box-shadow: 0 8px 18px rgba(0, 24, 66, .12);
}

.pd-app-tile:focus-visible {
    outline: 2px solid var(--pd-gold);
    outline-offset: 2px;
    color: var(--pd-navy);
}

.pd-app-mark {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 13px;
    background: linear-gradient(145deg, var(--pd-navy-soft), var(--pd-navy));
    color: var(--pd-gold);
    font-weight: 800;
    font-size: 1rem;
    letter-spacing: .01em;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08);
}

.pd-app-name {
    font-size: .72rem;
    font-weight: 600;
    line-height: 1.25;
    overflow-wrap: anywhere;
}

@media (max-width: 575.98px) {
    .pd-apps-panel { --pd-apps-inset: .5rem; }
}

@media (prefers-reduced-motion: reduce) {
    .pd-apps-panel,
    .pd-app-tile { transition: none; }
    .pd-app-tile:hover { transform: none; }
}

/* Each application is its own form so a row saves alone; the row is laid out
   with flex rather than table cells because a form cannot be a table row. */
.pd-applink-row {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    padding: .6rem .75rem .2rem;
}

.pd-applink-cell { flex: 0 0 auto; }
.pd-applink-remove { padding: 0 .75rem .6rem; text-align: right; }

/* ------------------------------------------------- route staff picker
   Our own dropdown rather than a native datalist. With 300 staff the browser
   rendered the datalist as a full-screen overlay in its own colours, which
   covered the dialog and was unreadable in a dark theme. */
.pd-staff-picker { position: relative; }

.pd-staff-options {
    position: absolute;
    z-index: 20;
    left: 0;
    right: 0;
    max-height: 15rem;
    overflow-y: auto;
    background: var(--pd-card);
    border: 1px solid var(--pd-border);
    border-radius: var(--pd-r-md);
    box-shadow: var(--pd-shadow);
    margin-top: .2rem;
    padding: .2rem;
}

.pd-staff-option {
    display: block;
    width: 100%;
    text-align: left;
    border: 0;
    background: transparent;
    border-radius: var(--pd-r-sm);
    padding: .35rem .5rem;
    line-height: 1.25;
}

.pd-staff-option:hover, .pd-staff-option:focus-visible { background: var(--pd-neutral); }
.pd-staff-option-name { display: block; font-size: .82rem; font-weight: 600; color: var(--pd-navy); }
.pd-staff-option-meta { display: block; font-size: .72rem; color: var(--pd-muted); }

/* ------------------------------------------------ route coverage edits */
.pd-coverage-remove {
    border: 0;
    background: transparent;
    color: var(--pd-muted);
    line-height: 1;
    padding: 0 .3rem;
    font-size: 1.1rem;
}

.pd-coverage-remove:hover { color: #b42318; }

.pd-coverage-removed {
    border-top: 1px solid var(--pd-line);
    padding-top: .75rem;
}

.pd-removed-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-size: .75rem;
    padding: .2rem .3rem .2rem .55rem;
    border-radius: 999px;
    background: var(--pd-neutral);
    color: var(--pd-ink);
}

.pd-removed-chip .pd-coverage-restore {
    border: 0;
    background: transparent;
    color: var(--pd-muted);
    line-height: 1;
    padding: 0 .2rem;
}

.pd-removed-chip .pd-coverage-restore:hover { color: var(--pd-navy); }

/* ---------------------------------------------------- sync progress bar
   Shown only while a run is live. The numbers behind it are written to the
   run row as the work happens, so the bar reflects real progress. */
.pd-sync-progress { min-width: 9rem; }
.pd-sync-progress .progress { height: 6px; border-radius: 999px; background: var(--pd-neutral); }
.pd-sync-progress .progress-bar { background: var(--pd-gold); }
.pd-sync-progress-text { font-size: .68rem; color: var(--pd-muted); font-variant-numeric: tabular-nums; }

/* The address under the name in the coverage list. Two accounts can carry the
   same name, so the name alone is not an identifier. */
.pd-coverage-email {
    font-size: .72rem;
    color: var(--pd-muted);
    line-height: 1.2;
}

/* --------------------------------------------- coverage table headers
   Sortable columns, sorted in the browser: the rows are already loaded and a
   round trip per click would be slower and would lose the ticked selection. */
.pd-coverage-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--pd-neutral);
}

.pd-coverage-table th.pd-sortable { cursor: pointer; user-select: none; }
.pd-coverage-table th.pd-sortable:hover { color: var(--pd-navy); }
.pd-sort-mark { font-size: .6rem; opacity: .8; }
.pd-coverage-table tbody tr:has(.pd-coverage-pick:checked) { background: rgba(0, 24, 66, .045); }

/* Filter box above the coverage list, and the rows it hides. */
.pd-coverage-search-wrap { flex: 1 1 18rem; max-width: 26rem; }
.pd-coverage-table tbody tr.pd-filtered-out { display: none; }

/* The "+N more departments" chip on the routes table. Hovering or focusing it
   lists the rest, so the row stays one line without hiding anything. */
.pd-dept-more {
    background: var(--pd-neutral);
    color: var(--pd-navy);
    cursor: help;
    border: 1px dashed var(--pd-border);
}

.pd-dept-more:hover, .pd-dept-more:focus-visible {
    background: var(--pd-navy);
    color: #fff;
    border-color: var(--pd-navy);
}

/* Filter results are swapped in place rather than reloaded, so there is no
   page-load indicator to signal that anything is happening. A brief fade is
   enough - dimming further would make fast, successful filtering look slow. */
[data-results] {
    transition: opacity .12s ease-out;
}

.pd-region-busy {
    opacity: .55;
    pointer-events: none;
}


/* -------- academic calendar context beside the decision box -------- */
/* A narrow column, so the table is tightened: smaller type, less padding, and
   the date held to its own line pair so the two columns carrying the answer
   get the rest of the width. */
.pd-caltable { font-size: .78rem; }

.pd-caltable th {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--pd-muted);
    font-weight: 700;
}

.pd-caltable td,
.pd-caltable th { padding: .4rem .5rem; vertical-align: top; }

.pd-caltable td:first-child,
.pd-caltable th:first-child { padding-left: .85rem; }

.pd-caltable td:last-child,
.pd-caltable th:last-child { padding-right: .85rem; }

.pd-caltable .badge { font-size: .64rem; font-weight: 600; }

/* Testing windows stack one per line and wrap rather than widening the
   column - an April date can sit inside three overlapping windows. */
.pd-caltest { color: var(--pd-ink); line-height: 1.3; }
.pd-caltest + .pd-caltest { margin-top: .2rem; }

/* Add and remove on a PD day row. Square so the two sit as a pair, and sized
   to line up with the small inputs beside them rather than the default button
   height, which would leave the row looking uneven. */
.pd-icon-btn-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    line-height: 1;
}

/* Holds the row height steady when the first row has no remove button, so the
   inputs do not shift as days are added and taken away. */
.pd-day-actions { min-height: 2rem; }

/* ------------------------------ settings index ----------------------- */
/* Fills the width it is given rather than sitting at a fixed column count:
   the page is read on a laptop and on a wide desktop, and a card that is
   readable at one size should not be stretched to fill the other. */
.pd-settings-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: .85rem;

    /* Every tile the same height, not just the same width. 1fr on auto rows in
       a grid of indefinite height sizes every row to the tallest card, so one
       long description no longer leaves a ragged shelf of different-sized
       tiles beside it. */
    grid-auto-rows: 1fr;
}

.pd-setting-card {
    /* Column flex so the card fills its equalised row and the description takes
       up the slack, rather than the text floating with space beneath it. */
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: 1rem 1.1rem;
    background: var(--pd-card);
    border: 1px solid var(--pd-border);
    border-radius: var(--pd-r-lg);
    color: var(--pd-ink);
    text-decoration: none;
    transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease;
}

/* The same hover the result cards use, so a card behaves the way cards
   behave everywhere else in the portal. */
.pd-setting-card:hover,
.pd-setting-card:focus-visible {
    border-color: var(--pd-navy);
    box-shadow: 0 6px 18px rgba(0, 24, 66, .16), 0 2px 6px rgba(0, 24, 66, .10);
    transform: translateY(-2px);
    color: var(--pd-ink);
}

.pd-setting-head {
    display: flex;
    align-items: center;
    gap: .55rem;
    margin-bottom: .45rem;
}

.pd-setting-head svg {
    width: 17px;
    height: 17px;
    flex: 0 0 auto;
    color: var(--pd-navy);
}

.pd-setting-title {
    font-weight: 650;
    font-size: .95rem;
    line-height: 1.2;
}

/* Pushed to the far end so the numbers line up down the right of the grid
   and can be scanned as a column. */
.pd-setting-count {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: .72rem;
    font-weight: 700;
    color: var(--pd-muted);
    background: var(--pd-neutral);
    border-radius: 999px;
    padding: .1rem .45rem;
}

.pd-setting-desc {
    flex: 1 1 auto;
    margin: 0;
    font-size: .8rem;
    line-height: 1.45;
    color: var(--pd-muted);
}

/* Amber rather than red: these are "look at this soon", not failures. */
.pd-setting-card.needs-attention { border-color: #e8b84b; }

.pd-setting-warn {
    margin: .5rem 0 0;
    font-size: .76rem;
    font-weight: 600;
    color: #8a6100;
}

/* ------------------------------------------------------- navy header band
   The whole top of the screen is navy: the sidebar column, the brand corner
   and the bar across the content. The page then curves away from it at the
   inside corner, so the navy reads as a frame the content sits inside rather
   than a lid on top of it.

   Taken from Staff Evaluation so the two applications wear the same header.
   The brand block is deliberately taller than the bar beside it - the plate
   overhangs into the navy column, which is why the bar carries no shadow. */

.pd-sidebar-brand {
    flex: none;
    padding: 0;
    border-bottom: 0;
}

.pd-sidebar-brand a {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: 1.1rem .9rem;
    text-decoration: none;
}

/* The plate is white because the F is black: on navy it would vanish. */
.pd-logo-card {
    flex: none;
    display: grid;
    place-items: center;
    width: 54px;
    height: 54px;
    background: #fff;
    border-radius: 14px;
}

.pd-logo-card img {
    display: block;
    width: auto;
    height: auto;
    max-width: 34px;
    max-height: 34px;
}

/* The words are typography, not artwork. The supplied lockup has a near-black
   wordmark that would disappear against this, and recolouring a mark we were
   given is not ours to do. */
.pd-wordmark {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.05;
}

.pd-wordmark-a {
    font-size: 1.15rem;
    font-weight: 800;
    letter-spacing: .01em;
    color: #fff;
}

/* Spreads to FRONTIER's width, flush at both ends. The letters are separate
   elements and space-between does the distributing, so this stays right
   whichever typeface an administrator has chosen - a tuned letter-spacing
   would only be correct for the one it was measured against. */
.pd-wordmark-b {
    display: flex;
    justify-content: space-between;
    font-size: .82rem;
    font-weight: 700;
    color: var(--pd-gold);
}

.pd-topbar {
    position: sticky;
    top: 0;
    z-index: 1030;
    display: flex;
    align-items: center;
    gap: 1rem;
    min-height: 60px;
    height: auto;
    padding: .5rem var(--pd-gutter) .5rem 1.1rem;
    background: var(--pd-navy);
    color: #e8eef7;
    border-bottom: 0;
    /* A shadow here muddies the rounded corner below it. */
    box-shadow: none;
}

.pd-topbar-product {
    font-size: 1.22rem;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: #fff;
    opacity: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Everything in the band is white-on-navy. Scoped to .pd-topbar so the same
   classes keep their navy-on-white behaviour anywhere else they appear. */
.pd-topbar-actions { margin-left: auto; }

.pd-topbar .pd-icon-btn { color: rgba(255, 255, 255, .88); }

.pd-topbar .pd-icon-btn:hover,
.pd-topbar .pd-user:hover {
    background: rgba(255, 255, 255, .12);
    color: #fff;
}

/* The global focus ring is navy, which is invisible against this. */
.pd-topbar .pd-icon-btn:focus-visible,
.pd-topbar .pd-user:focus-visible {
    outline: 2px solid var(--pd-gold);
    outline-offset: 2px;
}

.pd-topbar .pd-user { color: #fff; }
.pd-topbar .pd-user-caret { opacity: .75; }

/* Gold on navy for the fallback initial - white on navy would read as a hole
   rather than a face. A real photo gets a thin ring so it does not float. */
.pd-topbar .pd-avatar-initial { background: var(--pd-gold); color: var(--pd-navy); }
.pd-topbar .pd-avatar { box-shadow: 0 0 0 2px rgba(255, 255, 255, .28); }

.pd-topbar .pd-context-item { background: rgba(255, 255, 255, .10); }
.pd-topbar .pd-context-label { color: rgba(255, 255, 255, .60); }
.pd-topbar .pd-context-value { color: #fff; }

/* The inside corner. Only where there is one: below 1200px the sidebar is
   off-canvas and a radius there would just clip the top of the page. */
@media (min-width: 1200px) {
    .pd-shell { background: var(--pd-navy); }

    .pd-main,
    .pd-footer { background: var(--pd-bg); }

    .pd-main { border-top-left-radius: var(--pd-r-lg); }

    /* When impersonating, that bar is what meets the band, so the corner
       belongs to it instead. */
    .pd-impersonation-bar { border-top-left-radius: var(--pd-r-lg); }
    .pd-impersonation-bar + .pd-main { border-top-left-radius: 0; }

    /* The corner has to belong to the bar, not to the page.
       .pd-main carries the radius, and .pd-main scrolls - so the moment the
       page moved, the curve travelled up behind the sticky bar and the join
       went square while everything above it stayed put. The radius still earns
       its place for a short page and for clipping the panel's own background,
       but it cannot be the thing you see once scrolling starts.

       So the bar draws its own. A box the size of the radius, hung off the
       bottom-left of the sticky bar: navy, with a quarter disc of page colour
       centred on its far corner. What is left navy is exactly the wedge
       outside the curve, and because it hangs off a sticky element it stays
       where it is however far the page moves.

       It sits inside the page gutter, over the block padding at the top of
       .pd-main, so there is nothing underneath it to cover up. */
    .pd-topbar::after {
        content: "";
        position: absolute;
        left: 0;
        top: 100%;
        width: var(--pd-r-lg);
        height: var(--pd-r-lg);
        background: var(--pd-navy);
        background-image: radial-gradient(circle at 100% 100%,
            var(--pd-bg) 0 var(--pd-r-lg), transparent var(--pd-r-lg));
    }

    /* While impersonating, the gold bar is what meets the band and owns the
       corner. Painting a page-coloured curve over it would put a notch in the
       warning. */
    .pd-shell:has(.pd-impersonation-bar) .pd-topbar::after { display: none; }
}

/* The product name ellipsises rather than wrapping, so the bar keeps its
   height. On a 375px screen the gap between the menu button and the account
   controls is narrow enough that the full name cannot fit at 1.22rem, so it
   steps down first and truncates only if it still has to. */
@media (max-width: 767.98px) {
    .pd-topbar-product {
        font-size: .95rem;
        letter-spacing: .06em;
    }
}

/* ---------- Drag-to-reorder rows (Settings -> App Switcher) ---------- */
.pd-drag-handle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    margin-top: .1rem;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: #8a94a6;
    cursor: grab;
    touch-action: none;
}
.pd-drag-handle svg { width: 14px; height: 14px; fill: currentColor; }
.pd-drag-handle:hover { background: rgba(0, 24, 66, .06); color: #001842; }
.pd-drag-handle:focus-visible { outline: 2px solid #001842; outline-offset: 1px; }
[data-sortable-rows].is-sorting,
[data-sortable-rows].is-sorting .pd-drag-handle { cursor: grabbing; user-select: none; }
[data-sortable-rows] tr.is-dragging > td { background: #fff8e6; }
[data-sortable-rows] tr.is-dragging { position: relative; z-index: 1; box-shadow: 0 6px 18px rgba(0, 24, 66, .14); }
@media (prefers-reduced-motion: no-preference) {
    [data-sortable-rows] tr > td { transition: background-color .15s ease; }
}

/* ---------- Start guide (Views/Shared/_StartGuide.cshtml) ---------- */
.pd-guide { position: fixed; inset: 0; z-index: 2000; }
.pd-guide.is-centered { background: rgba(0, 24, 66, .55); }
.pd-guide.is-centered .pd-guide-spot { display: none; }
.pd-guide-spot {
    position: fixed;
    border-radius: 12px;
    pointer-events: none;
    box-shadow: 0 0 0 3px #ffb504, 0 0 0 9999px rgba(0, 24, 66, .55);
}
.pd-guide-card {
    position: fixed;
    width: min(380px, calc(100vw - 32px));
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    padding: 1.1rem 1.2rem 1rem;
    border-radius: 16px;
    background: #fff;
    color: #1f2937;
    box-shadow: 0 18px 48px rgba(0, 24, 66, .28);
}
.pd-guide.is-centered .pd-guide-card { width: min(460px, calc(100vw - 32px)); padding: 1.5rem 1.5rem 1.2rem; }
.pd-guide-progress { height: 4px; margin-bottom: .8rem; overflow: hidden; border-radius: 99px; background: #eef1f6; }
.pd-guide-progress span { display: block; width: 0; height: 100%; border-radius: inherit; background: #ffb504; }
.pd-guide-kicker { font-size: .7rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #8a6a00; }
.pd-guide-title { margin: .2rem 0 .45rem; font-size: 1.1rem; font-weight: 800; color: #001842; outline: none; }
.pd-guide.is-centered .pd-guide-title { font-size: 1.35rem; }
.pd-guide-body { font-size: .9rem; line-height: 1.5; color: #374151; }
.pd-guide-body p { margin: 0 0 .5rem; }
.pd-guide-body ul { margin: 0 0 .25rem; padding: 0; list-style: none; }
.pd-guide-body li { position: relative; margin-bottom: .3rem; padding-left: 1.35rem; }
.pd-guide-body li::before {
    content: "";
    position: absolute;
    left: .15rem;
    top: .42em;
    width: .6rem;
    height: .32rem;
    border-left: 2px solid #1a7f4b;
    border-bottom: 2px solid #1a7f4b;
    transform: rotate(-45deg);
}
.pd-guide-actions { display: flex; align-items: center; gap: .5rem; margin-top: .9rem; }
.pd-guide-skip {
    padding: .25rem 0;
    border: 0;
    background: none;
    font-size: .85rem;
    color: #6b7280;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.pd-guide-skip:hover { color: #001842; }
.pd-guide-skip:focus-visible { outline: 2px solid #001842; outline-offset: 2px; border-radius: 4px; }
@media (prefers-reduced-motion: no-preference) {
    .pd-guide-spot,
    .pd-guide-card { transition: top .25s ease, left .25s ease, width .25s ease, height .25s ease; }
}
@media print { .pd-guide { display: none !important; } }

/* ---------- Error log (Settings -> Error Log) ---------- */
.pd-error-trace {
    max-height: 32rem;
    margin: 0;
    padding: 1rem;
    overflow: auto;
    background: #0f1b33;
    color: #e6edf7;
    font-size: .75rem;
    line-height: 1.5;
    white-space: pre;
}

/* A route step skipped because the requester holds it themselves. */
.pd-route-step.is-skipped { opacity: .6; }
.pd-route-step.is-skipped strong { font-weight: 600; }
