/* Google Font.
   FIRST in the file, and it has to be: an @import after any style rule is
   invalid and the browser drops it silently. It used to sit below the :root
   block, so DM Sans never loaded at all and every screen fell back to
   Helvetica/Arial — a font substitution is not the kind of failure anyone
   reports, which is why it survived. */
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&display=swap');

/* ─────────────────────────────────────────────
   WyderNet LTI Pages — lti-pages.css
   Matches the clean, minimal design in the brief
   ───────────────────────────────────────────── */

:root {
    /* Aug 2026: values updated to match the MVP mockup's flow_55 palette
       (--green/--blue/--purple/--amber) — token NAMES kept as --wy-teal* to
       avoid a project-wide rename across every part-app-*.php/CSS file that
       already references them; only the hex values changed. See
       docs/mvp-flow55-alignment-spec.md §0. */
    --wy-teal:        #217a5c;
    --wy-teal-dark:   #1a6249;
    --wy-teal-light:  #e6f2ec;
    --wy-teal-bd:     #c9e3d5;
    --wy-teal-icon:   #217a5c;
    --wy-blue:        #1668b8;
    --wy-blue-bg:     #e8f1fb;
    --wy-blue-bd:     #c8ddf4;
    --wy-purple:      #5a4fcf;
    --wy-purple-bg:   #eeecfb;
    --wy-purple-bd:   #d3cef4;
    --wy-amber:       #9c5c0d; /* was #b06a12 — 3.78:1 as text on --wy-amber-bg (#fbf0dc) and 4.29:1 behind white text as a fill, both under AA 4.5:1. #9c5c0d clears both. */
    --wy-amber-bg:    #fbf0dc;
    --wy-amber-bd:    #eed7ac;
    --wy-text:        #1a1a1a;
    --wy-text-muted:  #656c79; /* was #6b7280 — 4.28:1 on --wy-amber-bg (#fbf0dc), under AA 4.5:1. #656c79 clears 4.5:1 on white, --wy-bg and --wy-amber-bg. */
    --wy-text-hint:   #6b6b6b; /* was #757575 — 4.6:1 on white but only 4.37:1 on --wy-bg (#f9f9f8) and 4.08:1 on --wy-amber-bg (#fbf0dc), both under AA 4.5:1. #6b6b6b clears 4.5:1 on all three. */
    --wy-border:      #e5e7eb;
    /* Form-control borders only. --wy-border (#e5e7eb) is 1.24:1 on white,
       which is fine for a decorative divider and fails WCAG 1.4.11 for a
       control whose boundary is the only thing identifying it — an input, a
       select, the upload dropzone. 3.19:1 on white, 3.03:1 on --wy-bg, with
       headroom so a later nudge can't silently drop under 3:1.
       (VPAT backlog item A5.)
       2026-09-27: #909090 → #8a8a8a. The redesign's tinted surfaces put
       controls on --wy-surface-page/-panel, where #909090 fell to 2.96:1.
       #8a8a8a is 3.45:1 on white and ≥3.1:1 on every new surface. */
    --wy-border-control: #8a8a8a;
    --wy-bg:          #f9f9f8;
    --wy-card-bg:     #ffffff;
    --wy-radius:      12px;
    --wy-radius-sm:   8px;
    --wy-shadow:      0 1px 4px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.06);
    --wy-font:        'DM Sans', 'Helvetica Neue', Arial, sans-serif;
    /* The height of a row's leading controls — the disclosure chevron
       (.wy-gap-chev) and the bulk-select checkbox (.wy-rec-pick) sit in
       boxes of exactly this height, TOP-ALIGNED, so their centres land on
       the same axis as the row title's first line. They were independently
       aligned before: the chevron to the top of the row, the checkbox to the
       row's vertical middle, which put the checkbox visibly lower and left
       of the chevron on any row taller than one line. One token because the
       alignment is a relationship between them, not a property of either. */
    --wy-rowctl-size: 30px;
    /* Text selection. The browser default is a blue that belongs to no palette
       in this product, and it is the one colour the app never chose: with no
       ::selection rule anywhere, every screen — including the student's
       published guide — highlighted in the UA's blue.
       It is worst on a primary button, where the default paints blue behind
       white label text and the label stops being readable. ::selection sets
       BOTH background and foreground, so one high-contrast pair is correct
       over every surface in the app: a pale green band with near-black text
       reads on white cards, on the recessed fill, and on --wy-teal buttons
       alike. 11.6:1, comfortably past AA. */
    --wy-select-bg:   #b6dcc8;
    --wy-select-fg:   #1a1a1a;

    /* ── UX redesign (Refined 1A, 2026-09-27) ──────────────────────────────
       Richness from SURFACES, never decoration: a pale sage page behind white
       cards, a pale green for supportive panels, a warm pale reserved for
       recommendations. Each text token was re-checked against each surface:
       --wy-text-muted and --wy-text-hint clear 4.5:1 on all three (lowest
       4.76:1, --wy-text-muted on --wy-surface-panel). */
    --wy-surface-page:      #f3f6f4;
    --wy-surface-panel:     #edf5f0;
    --wy-surface-panel-bd:  #d6e8dd;
    --wy-surface-highlight: #fdf6ea;
    --wy-surface-highlight-bd: #f0dfbf;
    --wy-shadow-sm: 0 1px 2px rgba(16,40,28,.05);
    --wy-shadow-md: 0 1px 3px rgba(16,40,28,.06), 0 6px 18px rgba(16,40,28,.06);
    --wy-radius-lg: 14px;
    --wy-space-1: .25rem;
    --wy-space-2: .5rem;
    --wy-space-3: .75rem;
    --wy-space-4: 1rem;
    --wy-space-5: 1.25rem;
    --wy-space-6: 1.5rem;
    --wy-space-7: 2rem;
    --wy-space-8: 2.5rem;
    --wy-topbar-h: 60px;
}

/* Visually hidden, still announced. For labels whose visible form is an icon
   or a placeholder, and for count suffixes like ", 2 open". */
.wy-sr-only{position:absolute !important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

/* Declared on the bare element selectors, not scoped to a wrapper: selection
   happens wherever a person drags, and a rule scoped to .wy-container would
   leave the header, the sidebar and the public guide on the UA default.
   The two are separate rules on purpose — a selector list containing an
   unrecognised pseudo-element is dropped WHOLE by the browser, so pairing
   ::selection with ::-moz-selection in one list loses both. */
::selection      { background: var(--wy-select-bg); color: var(--wy-select-fg); }
::-moz-selection { background: var(--wy-select-bg); color: var(--wy-select-fg); }


/* WCAG 1.4.11 — Neve styles every form control it can reach with
   `form input:read-write, form select, form textarea { border-color:
   var(--formfieldbordercolor) }` and sets that token to #dddddd (1.36:1 on
   white) from the Customizer. Two things make overriding it awkward: Neve's
   rule is (0,1,2) so most of our own class selectors lose to it, and its
   inline <style> is emitted in wp_head — AFTER our <link>s — so an equal
   specificity tie loses too.
   Re-declaring the custom property on `body` sidesteps both: property
   resolution takes the nearest ancestor's computed value, so everything
   under <body> reads ours regardless of selector weight or source order.
   This is what fixes the controls we never style ourselves (.wy-text-input:
   Course name / Course code / Enrollment on New Collection). */
body {
    --formfieldbordercolor: var(--wy-border-control);

    /* Neve's BLUE, and the reason a bare <button> turned blue on hover.
       Its stylesheet carries
           button:hover { background: var(--primarybtnhoverbg) }
       with --primarybtnhoverbg resolving to --nv-primary-accent (#2f5aae),
       a colour from the parent theme's palette that appears nowhere in this
       product. At (0,1,1) it beats any single-class rule of ours, so every
       button we style without naming a hover BACKGROUND inherited it —
       .wy-fac-badge (the amber faculty-comment marker on Review),
       .wy-fac-modal__x and .wy-ffrow__comment among them.
       Retinted rather than fought selector-by-selector, by the same
       property-resolution trick as --formfieldbordercolor directly above:
       this reaches the buttons we never style at all, including WordPress's
       own, where a per-component override reaches only what we remembered. */
    --primarybtnbg:         var(--wy-teal);
    --primarybtncolor:      #fff;
    --primarybtnhoverbg:    var(--wy-teal-dark);
    --primarybtnhovercolor: #fff;

    /* The same blue, one level up. Both of Neve's accents resolve to #2f5aae,
       and they reach far more than buttons — the one that kept showing is
           a:focus, a:hover { opacity: .9; color: var(--nv-secondary-accent) }
       which paints the LABEL of every .wy-btn rendered as an <a>, i.e. every
       navigation button in the wizard.
       Retinted at the source instead of per component: blue is not in this
       product's palette at all, so there is nowhere it is the right answer,
       and anything of Neve's we have not styled yet inherits green from here
       rather than surfacing the next blue nobody predicted. */
    --nv-primary-accent:   var(--wy-teal);
    --nv-secondary-accent: var(--wy-teal-dark);
}

/* Neve's button hover, neutralised for EVERY bare <button> in the app.
   38 of our own button classes carry no hover background of their own —
   .wy-review-edit-btn, .wy-source-toggle, .wy-dash-chip, the three modal
   close buttons, the faculty comment badge and the rest — and every one of
   them was inheriting the blue above. Per-component overrides would have
   been 38 selectors and would have missed the 39th.

   ── The selector is `html button`, and the two facts that force it ──
   Neve's style-main-new.min.css is enqueued LAST, after this file, so an
   equal-specificity rule here LOSES to it on source order. And a component
   rule like `.wy-source-toggle:hover` is (0,2,0), not (0,1,1): a
   pseudo-class counts in the CLASS column, so every component already
   outranks Neve's `button:hover` (0,1,1) and only ever lost by declaring no
   background at all.

   So this rule has to sit strictly between those two. `html button:hover` is
   (0,1,2) — an extra ELEMENT, which raises it past Neve without touching the
   class column:

       button:hover            (0,1,1)   Neve, loaded last
       html button:hover       (0,1,2)   this rule           ← beats Neve
       .wy-source-toggle:hover (0,2,0)   any component       ← beats this

   A component that genuinely wants a hover fill therefore still wins with
   its ordinary single-class rule, and one that declares nothing gets no fill
   instead of a blue one. `:not(.wy-btn)` would have been (0,2,1) — the
   argument's specificity counts — and would have clobbered every component.

   Safe for .wy-btn regardless: every variant resolves through
   .wy-btn:hover:not(:disabled) at (0,3,0). Checked variant by variant,
   including --outline and --sm, which carry no hover of their own. No bare
   <input type=submit|button|reset> exists in any template, so the rest of
   Neve's selector list has nothing here to reach. */
html button:hover,
html button:focus {
    background: none;
}



/* Defensive: the native [hidden] attribute only carries UA-stylesheet
   priority (display:none), so any class here that sets its own display
   (flex/grid/block — .wy-alert, .wy-oa-searching, etc.) silently wins the
   cascade and keeps rendering a "hidden" element. Found live 2026-08 on the
   4-tab request screens: server-rendered <div hidden> placeholders (error
   boxes, the OA "sent" banner, the OA search spinner) stayed visible on
   every page load even though JS only ever un-hides them after a real
   event — this rule makes hidden actually mean hidden regardless of what
   else targets the element. */
[hidden] {
    display: none !important;
}

/* Page wrapper */
.wy-lti-page {
    font-family:      var(--wy-font);
    background:       var(--wy-bg);
    min-height:       100vh;
    padding:          2rem 1rem;
    color:            var(--wy-text);
    -webkit-font-smoothing: antialiased;
}

/* Hide default Neve header/footer chrome on LTI pages */
.wy-lti-page ~ footer,
header.site-header {
    display: none !important;
}

/* Container — 900px is THE standard width for every app screen.
   It used to be 720px with a .wy-container--wide opt-in to 900, which meant
   the ten screens that opted in (Review, Materials, Editor, Gaps, Preview,
   Fulfillment...) sat visibly wider than the sixteen that did not, and every
   new screen had to remember to opt in. The default is now the standard and
   the modifier is gone. */
.wy-container {
    max-width: 900px;
    margin:    0 auto;
}

/* ── Course header ── */
.wy-course-header {
    margin-bottom: 1.5rem;
}

.wy-label {
    font-size:      0.75rem;
    font-weight:    600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color:          var(--wy-text-muted);
}

.wy-course-title {
    font-size:   1.75rem;
    font-weight: 600;
    margin:      0.25rem 0 0;
    color:       var(--wy-text);
    line-height: 1.2;
}

.wy-placeholder {
    color: var(--wy-text-muted);
}

/* ── Card ── */
.wy-card {
    background:    var(--wy-card-bg);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       2.5rem 2rem;
    text-align:    center;
}

/* Card icon */
.wy-card-icon {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           72px;
    height:          72px;
    background:      var(--wy-teal-light);
    border-radius:   50%;
    margin-bottom:   1.25rem;
    color:           var(--wy-teal-icon);
}

.wy-card-title {
    font-size:     1.25rem;
    font-weight:   600;
    margin:        0 0 0.75rem;
    color:         var(--wy-text);
}

/* 404 page — the numeral sits above the icon/title as its own small label,
   not a replacement for either. */
.wy-404-num {
    display:        block;
    font-size:      0.8rem;
    font-weight:    700;
    letter-spacing: 0.1em;
    color:          var(--wy-teal);
    margin-bottom:  0.4rem;
}

.wy-card-description {
    font-size:   0.95rem;
    color:       var(--wy-text-muted);
    line-height: 1.6;
    max-width:   540px;
    margin:      0 auto 1.75rem;
}

/* ── Page intro (icon + title on one line, description under) ──
   Sits ABOVE the white card so the card contains only the form. The icon is
   a fixed 40px disc next to the title rather than a centred badge above it,
   and the row centres the two so they share a baseline band regardless of
   whether the title wraps. */
.wy-page-intro {
    margin-bottom: 1.25rem;
    text-align:    left;
}

.wy-page-intro__row {
    display:     flex;
    align-items: center;
    gap:         0.75rem;
}

.wy-page-intro__icon {
    flex:            0 0 auto;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           40px;
    height:          40px;
    border-radius:   50%;
    background:      var(--wy-teal-light);
    color:           var(--wy-teal-icon);
}

.wy-page-intro__title {
    margin:      0;
    font-size:   1.45rem;
    font-weight: 700;
    line-height: 1.3;
    color:       var(--wy-text);
}

.wy-page-intro__desc {
    margin:      0.5rem 0 0;
    font-size:   0.95rem;
    line-height: 1.6;
    color:       var(--wy-text-muted);
    max-width:   62ch;
}

/* "Add your course information" — separates the course-identity fields above
   from the two ways of supplying the course itself below. */
.wy-upload-section-title {
    font-size:   1.05rem;
    font-weight: 700;
    color:       var(--wy-text);
    text-align:  left;
    margin:      1.6rem 0 0;
}

/* Hint text that sits under a FIELD rather than inside a dropzone. .wy-card
   is text-align:center, which these inherited — correct for the centred
   dropzone hints, wrong for a sentence explaining the input above it. */
.wy-field-hint {
    text-align: left;
}

/* Upload dropzone and Course Context side by side — either one satisfies the
   form, so they get equal visual weight rather than a primary/secondary
   arrangement. Collapses to a single column on narrow screens, where the
   dropzone lands first because it is the more common input.

   Three explicit rows (label / hint / control) with the columns opting into
   subgrid, so a two-line hint in one column cannot push its control out of
   step with the other. Without this the tops differed by 20px and the bottoms
   by 76px. The control row is 1fr, which is what makes both controls the same
   height and aligns them top AND bottom. */
.wy-upload-split {
    display:               grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows:    auto auto 1fr;
    /* 1.1rem read as "almost touching" once the drop zone had a dashed border
       and the textarea a solid one — two visible edges 17px apart. */
    column-gap:            1.75rem;
    row-gap:               0;
    align-items:           stretch;
    text-align:            left;
}

.wy-upload-split__col {
    display:            grid;
    grid-template-rows: subgrid;
    grid-row:           span 3;
    min-width:          0;
}

/* One column, full width.
   The split is two columns because there are normally two ways in — upload a
   file, or paste the list. An organization with no PDF adapter has only the
   first (pasted text is read by the PDF adapter), and the dropzone was then
   sitting in a half-width column with nothing beside it.
   :only-child rather than a modifier class, so it follows whatever the PHP
   decides to render without the two having to agree about a class name. */
.wy-upload-split__col:only-child {
    grid-column: 1 / -1;
}

/* Browsers without subgrid (pre-2023) keep the old stacked behaviour rather
   than collapsing the layout — the rows just won't align across columns. */
@supports not (grid-template-rows: subgrid) {
    .wy-upload-split__col {
        display:        flex;
        flex-direction: column;
    }
}

@media (max-width: 640px) {
    .wy-upload-split {
        grid-template-columns: 1fr;
        grid-template-rows:    none;
        row-gap:               1.1rem;
    }
    /* One column per row again — subgrid spanning would misalign the stack. */
    .wy-upload-split__col {
        display:        flex;
        flex-direction: column;
        grid-row:       auto;
    }
}

.wy-textarea {
    width:          100%;
    flex:           1 1 auto;
    min-height:     150px;
    border:         1px solid var(--wy-border-control);
    border-radius:  var(--wy-radius-sm);
    padding:        0.65rem 0.8rem;
    font-size:      0.92rem;
    font-family:    inherit;
    line-height:    1.55;
    color:          var(--wy-text);
    resize:         vertical;
    box-sizing:     border-box;
}

/* The dropzone owns a 1.5rem bottom margin of its own; inside the split it
   would push the two columns out of alignment. Stretched to fill the shared
   control row, its content is centred so it doesn't sit at the top of a tall
   box. */
.wy-upload-split .wy-dropzone {
    margin-bottom:   0;
    display:         flex;
    flex-direction:  column;
    justify-content: center;
    padding:         1.5rem;
}

/* Both controls fill the shared 1fr row, which is what aligns their bottoms. */
.wy-upload-split .wy-dropzone,
.wy-upload-split .wy-textarea {
    height:     100%;
    box-sizing: border-box;
}

/* min-width:0 IS THE FIX for the drop zone overlapping Course Context, and the
   reason is not obvious enough to leave unwritten.
 *
 * .wy-upload-split__col is itself a grid container (it uses subgrid rows), so
 * the drop zone is a GRID ITEM in it, and a grid item's automatic minimum size
 * makes the auto-sized track grow to the item's MIN-CONTENT width. One
 * unbreakable 50-character filename therefore pushed the drop zone out past
 * the column it lives in: measured at 1920px, the column track was 371px and
 * the drop zone 455px, putting its remove buttons 40px inside the textarea.
 *
 * The column TRACK was never the problem — .wy-upload-split__col already
 * carries min-width:0, so the 1fr tracks stayed even. Only the box inside it
 * overflowed, which is why the layout looked broken while the grid was fine.
 *
 * The filename wrapping below is the other half: this stops the box growing,
 * that stops the content needing it to. Both are needed — either alone leaves
 * the row clipped or the column pushed. */
.wy-upload-split .wy-dropzone {
    min-width: 0;
}

/* ── Drop zone ── */
.wy-dropzone {
    position:      relative;
    border:        2px dashed var(--wy-border-control);
    border-radius: var(--wy-radius-sm);
    padding:       2.5rem 1.5rem;
    margin-bottom: 1.5rem;
    cursor:        pointer;
    transition:    border-color 0.2s ease, background 0.2s ease;
}

.wy-dropzone:hover,
.wy-dropzone.wy-drag-over {
    border-color: var(--wy-teal);
    background:   var(--wy-teal-light);
}

.wy-dropzone.wy-drag-over .wy-upload-icon {
    color: var(--wy-teal);
}

/* Hidden real file input covers the whole dropzone */
.wy-file-input {
    position: absolute;
    inset:    0;
    opacity:  0;
    cursor:   pointer;
    width:    100%;
    height:   100%;
}

/* .wy-file-input itself is opacity:0, so a keyboard user tabbing to it got
   no visible focus indicator at all (an invisible element's default outline
   renders invisibly too) — :focus-within puts the ring on the dropzone
   that's actually seen instead, same teal convention every other focusable
   control on this page already uses. */
.wy-dropzone:focus-within {
    outline: 2px solid var(--wy-teal);
    outline-offset: 2px;
}

.wy-upload-icon {
    color:         var(--wy-text-hint);
    margin-bottom: 0.5rem;
    transition:    color 0.2s ease;
}

.wy-dropzone-text {
    font-size:   0.95rem;
    color:       var(--wy-text-muted);
    margin:      0 0 0.25rem;
}

.wy-link {
    color:           var(--wy-teal);
    font-weight:     500;
    text-decoration: underline;
    cursor:          pointer;
}

.wy-dropzone-hint {
    font-size: 0.8rem;
    color:     var(--wy-text-hint);
    margin:    0.1rem 0 0;
}

/* Upload field label, above a dropzone (e.g. "Syllabus", "Assignments") */
.wy-upload-label {
    display:       block;
    font-weight:   600;
    font-size:     0.9rem;
    color:         var(--wy-text);
    text-align:    left;
    margin:        0 0 0.5rem;
}

.wy-upload-label-hint {
    font-weight: 400;
    color:       var(--wy-text-muted);
}

/* Selected file state */
.wy-dropzone-selected {
    display:     flex;
    align-items: center;
    gap:         0.75rem;
    /* So the row can shrink to its container instead of the container growing
       to the row — see the min-width note on .wy-upload-split .wy-dropzone. */
    min-width:   0;
    color:       var(--wy-teal-dark);
    font-weight: 500;
    font-size:   0.95rem;
    transition:  color 0.15s;
}

.wy-dropzone-selected-list {
    min-width: 0;
}

/* The filename. WRAPS rather than truncates: a librarian identifying which
   syllabus they just attached needs the end of the name — the version number
   and the extension are exactly what distinguishes
   "…CHI-3020-01 (5).pdf" from "…CHI-3020-01 (4).pdf".
   `anywhere` rather than `break-word` so the break is taken into account when
   the browser computes min-content, which is what actually lets the box
   shrink; `break-word` breaks visually but leaves min-content unchanged.
   The single-file drop zones elsewhere use #selected-filename and keep their
   one-line ellipsis — one name on one row has nothing to disambiguate. */
.wy-dropzone-selected__name {
    flex:           1 1 auto;
    min-width:      0;
    overflow-wrap:  anywhere;
}

/* A selected file row is clickable (the whole dropzone re-opens the picker),
   so it needs to say so on hover. Dark teal -> teal: deliberately small, since
   the row is content rather than a control. The list-scoped selector is here
   so this cannot lose to .wy-dropzone-selected-list .wy-dropzone-selected
   below, which is a descendant selector and outranks a bare class. */
.wy-dropzone-selected:hover,
.wy-dropzone-selected-list .wy-dropzone-selected:hover {
    color: var(--wy-teal);
}

/* "+ Add another file". Styled as its own class rather than reusing .wy-link,
   because this is a <button> and Neve's theme ships a bare
   button:hover/:focus/:active rule (background var(--primarybtnbg), color
   #fff) that beats a plain class selector on specificity regardless of load
   order — the same fight .wy-req-meta__head has below. Re-declaring on the
   SAME pseudo-classes at class+pseudo specificity is what actually wins;
   without it the label turned white and vanished into the dropzone. */
.wy-dropzone-addmore {
    background:      none;
    border:          0;
    padding:         0.4rem 0;
    text-align:      left;
    font:            inherit;
    font-weight:     500;
    color:           var(--wy-teal-dark);
    text-decoration: underline;
    cursor:          pointer;
    transition:      color 0.15s;
}

.wy-dropzone-addmore:hover,
.wy-dropzone-addmore:focus,
.wy-dropzone-addmore:active {
    background: none;
    color:      var(--wy-teal);
}

.wy-dropzone-addmore:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 2px;
}

/* Multiple selected files (assignments dropzone) — one .wy-dropzone-selected
   row per file, stacked. */
.wy-dropzone-selected-list .wy-dropzone-selected {
    padding: 0.3rem 0;
    text-align: left;
}

.wy-dropzone-selected svg {
    flex-shrink: 0;
    color:       var(--wy-teal);
}

#selected-filename {
    flex:          1;
    text-align:    left;
    overflow:      hidden;
    text-overflow: ellipsis;
    white-space:   nowrap;
}

.wy-remove-file {
    background:  none;
    border:      none;
    /* Pinned to the right of the row and never squeezed: it is the row's only
       control, and it belongs inside the drop zone's border. */
    flex-shrink: 0;
    margin-left: auto;
    cursor:      pointer;
    color:       var(--wy-text-muted);
    font-size:   1rem;
    padding:     0.25rem;
    line-height: 1;
    transition:  color 0.15s;
}

.wy-remove-file:hover {
    color: #e53e3e;
}

/* ── Submit button ── */
.wy-btn {
    display:          inline-flex;
    align-items:      center;
    gap:              0.5rem;
    background:       var(--wy-teal);
    color:            #fff;
    border:           none;
    border-radius:    var(--wy-radius-sm);
    padding:          0.75rem 1.75rem;
    font-family:      var(--wy-font);
    font-size:        0.95rem;
    font-weight:      600;
    cursor:           pointer;
    transition:       background 0.2s ease, opacity 0.2s ease, transform 0.1s ease;
    /* A label is not content. Clicking a button — and especially the
       double-click a person makes when a first press seems not to have
       registered, which is common here because most actions cost 2–3
       seconds — otherwise selects the label and leaves a highlight band
       sitting on the control afterwards. Nothing is lost: no text in this
       app exists only inside a button. */
    -webkit-user-select: none;
    user-select:         none;
}

.wy-btn:hover:not(:disabled),
.wy-btn:focus:not(:disabled),
.wy-btn--primary:hover:not(:disabled),
.wy-btn--primary:focus:not(:disabled) {
    background: var(--wy-teal-dark);
    color:      #fff;
    opacity:    1;
    transform:  translateY(-1px);
}

/* Focus is not hover. The variants above answer both states with one
   declaration block so a label can never be left unreadable — Neve's
   `a:focus, a:hover { color: var(--nv-secondary-accent) }` paints both — but
   the 1px lift belongs to the pointer alone: a click leaves the control
   focused, and without this the button would stay raised after the mouse had
   gone. Reduced motion is unaffected either way; this removes movement. */
.wy-btn:focus:not(:hover):not(:disabled) {
    transform: none;
}

.wy-btn:active:not(:disabled) {
    transform: translateY(0);
}

/* A blanket opacity is not a safe way to say "disabled": it lightens the teal
   background toward the white page (#217a5c -> ~#9bc3b6) while white text
   composited over white STAYS white, so the label sat at 1.93:1 against its
   own background — below even the 3:1 large-text floor, and the reason
   "Start" read as almost transparent. The disabled state now names both
   colours outright (4.6:1) and still reads as unavailable through the muted
   fill and the cursor. */
.wy-btn:disabled {
    background: var(--wy-teal-light);
    color:      var(--wy-teal);
    opacity:    1;
    cursor:     not-allowed;
}

/* ── Alert ── */
.wy-alert {
    display:       flex;
    align-items:   center;
    gap:           0.5rem;
    padding:       0.75rem 1rem;
    border-radius: var(--wy-radius-sm);
    font-size:     0.875rem;
    margin-bottom: 1.25rem;
    text-align:    left;
}

.wy-alert--error {
    background: #fef2f2;
    color:      #b91c1c;
    border:     1px solid #fecaca;
}

.wy-alert--success {
    background: var(--wy-teal-light);
    color:      var(--wy-teal-dark);
    border:     1px solid var(--wy-teal-bd);
}

/* Informational, NOT an error: the OER screen's "this replaces the current
   access option" note states a consequence of a save the librarian is
   deliberately making. Amber rather than red because nothing has gone wrong
   and nothing is blocked — colouring it as an error would read as a refusal.
   Contrast checked against its own background (#78350f on #fffbeb, 9.7:1),
   and the icon carries the meaning too, so tint is never the only cue. */
.wy-alert--notice {
    background: #fffbeb;
    color:      #78350f;
    border:     1px solid #fde68a;
}

.wy-alert--notice svg { flex-shrink: 0; }

/* ── Mail preview (Share recommendations) ── */
.wy-mailpreview {
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    overflow:      hidden;
    background:    var(--wy-bg);
}
.wy-mailpreview__top {
    padding:       0.75rem 0.95rem;
    border-bottom: 1px solid var(--wy-border);
    font-size:     0.82rem;
    color:         var(--wy-text-muted);
}
.wy-mailpreview__top b {
    color: var(--wy-text);
}
.wy-mailpreview__body {
    padding:     1rem 1.05rem;
    font-family: ui-monospace, Menlo, monospace;
    font-size:   0.82rem;
    color:       var(--wy-text-muted);
    white-space: pre-line;
}

/* ── Settings screen ── */
.wy-settings-h {
    font-weight: 700;
    font-size:   1rem;
    margin-bottom: 0.2rem;
}
.wy-settings-sub {
    font-size:     0.82rem;
    color:         var(--wy-text-muted);
    margin-bottom: 1.1rem;
}
.wy-settings-grid2 {
    display:               grid;
    grid-template-columns: 1fr 1fr;
    gap:                   1rem;
}
@media (max-width: 640px) {
    .wy-settings-grid2 { grid-template-columns: 1fr; }
}
.wy-settings-pill {
    display:       inline-block;
    margin-left:   0.5rem;
    padding:       0.15rem 0.6rem;
    font-size:     0.72rem;
    font-weight:   600;
    color:         var(--wy-text-muted);
    background:    var(--wy-bg);
    border:        1px solid var(--wy-border);
    border-radius: 999px;
    vertical-align: middle;
}
.wy-settings-switch {
    display:       flex;
    align-items:   center;
    gap:           0.5rem;
    font-size:     0.88rem;
    color:         var(--wy-text);
    margin-bottom: 0.5rem;
}

/* ── Success state ── */
.wy-success-state {
    text-align: center;
    padding:    4rem 2rem;
}

.wy-success-icon {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           80px;
    height:          80px;
    background:      var(--wy-teal-light);
    border-radius:   50%;
    color:           var(--wy-teal);
    margin-bottom:   1.5rem;
    animation:       wy-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.wy-success-state h2 {
    font-size:     1.5rem;
    font-weight:   600;
    margin-bottom: 0.5rem;
}

.wy-success-state p {
    color: var(--wy-text-muted);
}

@keyframes wy-pop {
    from { transform: scale(0.5); opacity: 0; }
    to   { transform: scale(1);   opacity: 1; }
}

/* ── Responsive ── */
@media (max-width: 480px) {
    .wy-card {
        padding: 1.75rem 1.25rem;
    }
    .wy-course-title {
        font-size: 1.4rem;
    }
}


/* ─────────────────────────────────────────────
   WyderNet Header & Footer
   ───────────────────────────────────────────── */

/* Reset body margin that Neve adds */
body.wy-lti-body {
    margin:     0;
    padding:    0;
    background: var(--wy-bg);
}

/* ── Header ── */
.wy-header {
    background:   var(--wy-card-bg);
    border-bottom: 1px solid var(--wy-border);
    padding:      0 1.5rem;
    height:       56px;
    display:      flex;
    align-items:  center;
    position:     sticky;
    top:          0;
    z-index:      100;
}

.wy-header-inner {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    width:           100%;
    /* Matches .wy-container (900px), so the logo lines up with the page
       content below it. At 960 it did not: .wy-header carries 1.5rem of side
       padding, so a 960 inner box started 6px LEFT of a 900px .wy-container on
       every width — visible against any left-aligned heading. With both at 900
       the two left edges land on the same pixel:
         header   24 + ((W - 48) - 900) / 2  =  W/2 - 450
         content       (W - 900) / 2         =  W/2 - 450   */
    max-width:       900px;
    margin:          0 auto;
}

/* ── The header lines up with whatever column the page below it uses ──────
   .wy-header-inner's 900px matches .wy-container, and .wy-container has no
   side padding, which is the only reason that value works: two boxes line up
   when the header's inner max-width equals the CONTENT BOX of the container
   below — its max-width MINUS its own horizontal padding. The header's own
   padding cancels out of that arithmetic entirely.

   The guide pages do not use .wy-container. Measured on dev before this rule,
   at 1568px: the logo sat 110px RIGHT of the heading on a default-layout
   guide (.wy-guide-inner, 1200px + 2.5rem) and 106px LEFT of it on a compact
   one (.wn-page, 720px + 1rem). Both had been that way since those layouts
   existed; it is not specific to the faculty review, which is simply the
   screen it was reported from.

   Rather than compute a third max-width per layout (a number that would drift
   the first time a container's padding changed), the header BORROWS the
   container's own max-width AND padding and zeroes its own — so the two boxes
   are identical by construction, at every width including the mobile
   breakpoints below.

   :has() is the right tool here because the header is rendered before the page
   knows which layout it is: the compact/default choice comes from
   organization.guide_template, which the faculty page only learns after its
   Supabase fetch. A browser without :has() keeps today's 900px.

   THE HEADER TAKES .wy-main's INSET TOO, and that half is what makes the
   alignment hold at every width rather than only at wide ones. <header> is a
   SIBLING of <main>, so it never had main's 1rem side padding. While the
   container's max-width still binds that 1rem cancels out of the centring on
   both sides and the two agree anyway — which is why matching max-width and
   padding alone measured exact at 1440px and 16px out at 390px, where neither
   box is centred any more and the inset is all that is left. Adopting it makes
   the header's content box identical to the container's in both cases.

   .wn-page's numbers mirror --wn-max-width / --wn-space-4 / --wn-space-3 in
   assets/wydernet-styles.css, which that file owns; they must be changed
   together. No page carries both containers — part-library-search.php's
   .wy-guide-inner is in an early-return error branch. */
body:has(.wy-guide-inner) .wy-header,
body:has(.wn-page) .wy-header { padding: 0 1rem; }   /* = .wy-main's inset */

body:has(.wy-guide-inner) .wy-header-inner { max-width: 1200px; padding: 0 2.5rem; }
body:has(.wn-page) .wy-header-inner        { max-width: 720px;  padding: 0 1rem; }

@media (max-width: 768px) {
    body:has(.wy-guide-inner) .wy-header-inner { padding: 0 1rem; }
}
@media (max-width: 480px) {
    body:has(.wn-page) .wy-header-inner { padding: 0 0.75rem; }
}

.wy-header-logo a,
.wy-header-logo img {
    display:    block;
    max-height: 32px;
    width:      auto;
}

.wy-header-sitename {
    font-family: var(--wy-font);
    font-weight: 600;
    font-size:   1rem;
    color:       var(--wy-teal);
    letter-spacing: -0.01em;
}

.wy-header-right {
    display:     flex;
    align-items: center;
    gap:         1.25rem;
}

.wy-header-user {
    display:     flex;
    align-items: center;
    gap:         0.35rem;
    font-size:   0.85rem;
    color:       var(--wy-text-muted);
}

.wy-header-return {
    display:         flex;
    align-items:     center;
    gap:             0.3rem;
    font-size:       0.85rem;
    font-weight:     500;
    color:           var(--wy-teal);
    text-decoration: none;
    transition:      color 0.15s;
}

.wy-header-return:hover {
    color: var(--wy-teal-dark);
}

/* ── Main content area ── */
.wy-main {
    min-height: calc(100vh - 56px - 48px);
    padding:    2rem 1rem;
}

/* ── Footer ── */
.wy-footer {
    border-top:  1px solid var(--wy-border);
    background:  var(--wy-card-bg);
    height:      48px;
    display:     flex;
    align-items: center;
}

.wy-footer-inner {
    display:     flex;
    align-items: center;
    gap:         0.6rem;
    font-size:   0.8rem;
    color:       var(--wy-text-hint);
    padding:     0 1.5rem;
    max-width:   960px;
    margin:      0 auto;
    width:       100%;
}

.wy-footer-divider {
    color: var(--wy-border);
}

.wy-footer-powered a {
    color:           var(--wy-teal);
    /* Underlined, not just coloured: the teal link sits at only 1.13:1
       against the surrounding --wy-text-hint footer text, so colour alone
       cannot be the sole means of identifying it (WCAG 1.4.1 Use of Colour). */
    text-decoration: underline;
}

.wy-footer-powered a:hover {
    text-decoration: underline;
}

.wy-footer-cookie a {
    color:           var(--wy-teal);
    /* Underlined, not just coloured: the teal link sits at only 1.13:1
       against the surrounding --wy-text-hint footer text, so colour alone
       cannot be the sole means of identifying it (WCAG 1.4.1 Use of Colour). */
    text-decoration: underline;
}

.wy-footer-cookie a:hover {
    text-decoration: underline;
}

.wy-footer-course {
    margin-left: auto;
    font-style:  italic;
}


/* ─────────────────────────────────────────────
   Research Guide Page — full-width banded layout
   ───────────────────────────────────────────── */

/* Override base page padding for the guide — bands handle their own spacing */
.wy-guide-page {
    padding: 0;
}

/* ── Full-width bands ── */
.wy-guide-band {
    width:      100%;
    border-bottom: 1px solid var(--wy-border);
}

.wy-guide-inner {
    max-width: 1200px;
    margin:    0 auto;
    padding:   1.5rem 2.5rem;
}

/* Band 1 — title */
.wy-guide-band--title {
    background: var(--wy-teal-light);
}

.wy-guide-band--title .wy-label {
    display: block;
    margin-bottom: 0.4rem;
}

.wy-guide-band--title .wy-course-title {
    margin: 0;
    font-size: 1.9rem;
}

/* Band 2 — share */
.wy-guide-band--share {
    background: #ffffff;
}

/* Band 3 — edit actions */
.wy-guide-band--actions {
    background: #f5faf8;
    border-bottom: none;
}

.wy-guide-band--actions .wy-guide-inner {
    padding-top:    1rem;
    padding-bottom: 1rem;
}

/* Band 4 — guide body content */
.wy-guide-band--body {
    background:    #ffffff;
    border-bottom: none;
}

/* ── Share box (inside Band 2) ── */
.wy-guide-share-box {
    display:       flex;
    flex-direction: column;
    gap:           0.35rem;
}

.wy-share-title-row {
    display:     flex;
    align-items: center;
    gap:         0.5rem;
    color:       var(--wy-text-muted);
}

.wy-share-label {
    font-weight: 600;
    font-size:   0.9rem;
    color:       var(--wy-text);
    flex:        1;
}

.wy-share-hint {
    font-size:   0.8rem;
    color:       var(--wy-text-hint);
    margin:      0;
    line-height: 1.5;
    padding-left: 1.5rem;
}

/* ── Toggle switch ── */
.wy-toggle {
    position:    relative;
    display:     inline-block;
    width:       36px;
    height:      20px;
    flex-shrink: 0;
}

.wy-toggle input {
    opacity: 0;
    width:   0;
    height:  0;
}

.wy-toggle-slider {
    position:      absolute;
    inset:         0;
    background:    var(--wy-border);
    border-radius: 20px;
    cursor:        not-allowed;
    transition:    background 0.2s;
}

.wy-toggle-slider::before {
    content:       '';
    position:      absolute;
    width:         14px;
    height:        14px;
    left:          3px;
    top:           3px;
    background:    #fff;
    border-radius: 50%;
    transition:    transform 0.2s;
    box-shadow:    0 1px 3px rgba(0,0,0,0.15);
}

.wy-toggle input:checked + .wy-toggle-slider            { background: var(--wy-teal); }
.wy-toggle input:checked + .wy-toggle-slider::before    { transform: translateX(16px); }

/* .wy-toggle input is opacity:0/zero-size, so its own focus outline renders
   invisibly — same fix as .wy-dropzone:focus-within for .wy-file-input.
   Adjacent-sibling combinator works here since .wy-toggle-slider is the
   input's next sibling in markup. Accessibility audit, 2026-08-23. */
.wy-toggle input:focus-visible + .wy-toggle-slider {
    outline: 2px solid var(--wy-teal);
    outline-offset: 2px;
}

/* ── Secondary button ── */
.wy-btn--secondary {
    background:   transparent;
    color:        var(--wy-teal);
    border:       1.5px solid var(--wy-teal);
}

.wy-btn--secondary:hover:not(:disabled),
.wy-btn--secondary:focus:not(:disabled) {
    background:   var(--wy-teal-light);
    border-color: var(--wy-teal-dark);
    color:        var(--wy-teal-dark);
    transform:    translateY(-1px);
}

/* ── Danger button/card — delete-collection confirmation ──
   Same red family as .wy-alert--error (#b91c1c/#fecaca/#fef2f2), so a
   destructive confirmation reads as the same "danger" language already
   established by error alerts, not a one-off color. */
.wy-btn--danger {
    background: #b91c1c;
    color:      #fff;
}

.wy-btn--danger:hover:not(:disabled),
.wy-btn--danger:focus:not(:disabled) {
    background: #991b1b;
    transform:  translateY(-1px);
}

.wy-card--danger {
    text-align:  left;
    border-top:  3px solid #b91c1c;
}

/* ── Dark button — Home screen "Set to Draft" confirm (WyderNet_Home_
   Implementation_Prompt_2.md §6). Deliberately NOT .wy-btn--danger's red:
   taking a published collection offline is a caution, not a destructive
   delete, so it gets its own visual weight distinct from the red family. */
.wy-btn--dark {
    background: var(--wy-text);
    color:      #fff;
}

.wy-btn--dark:hover:not(:disabled),
.wy-btn--dark:focus:not(:disabled) {
    background: #000;
    transform:  translateY(-1px);
}

.wy-card--danger li {
    margin-bottom: 0.4rem;
}

/* ── Guide text sections ── */
.wy-guide-section {
    margin-bottom: 2.25rem;
    color:         var(--wy-text);
    line-height:   1.75;
    font-size:     0.95rem;
}

.wy-guide-section h2 {
    font-size:      1.05rem;
    font-weight:    700;
    color:          var(--wy-teal-dark);
    margin:         0 0 0.75rem;
    padding-bottom: 0.45rem;
    border-bottom:  2px solid var(--wy-teal-light);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.wy-guide-section p {
    margin: 0 0 0.85rem;
    color:  var(--wy-text-muted);
}

.wy-guide-section ul,
.wy-guide-section ol {
    padding-left: 0;
    margin:       0 0 0.85rem;
    list-style:   none;
    color:        var(--wy-text-muted);
}

.wy-guide-section li {
    position:      relative;
    padding-left:  1.35rem;
    margin-bottom: 0.55rem;
    line-height:   1.65;
}

.wy-guide-section li::before {
    content:     '';
    position:    absolute;
    left:        0;
    top:         0.55em;
    width:       7px;
    height:      7px;
    border-radius: 50%;
    background:  var(--wy-teal);
    flex-shrink: 0;
}

.wy-guide-section li strong {
    color:       var(--wy-text);
    font-weight: 600;
}

.wy-guide-section em {
    font-style: italic;
}

/* ── Resource tables ── */
.wy-guide-table-block {
    margin-bottom: 2.5rem;
}

.wy-guide-table-wrap {
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    overflow-x:    auto;
    box-shadow:    0 1px 3px rgba(0,0,0,0.04);
}

.wy-guide-table {
    width:           100%;
    border-collapse: collapse;
    font-size:       0.875rem;
}

.wy-guide-table thead tr {
    background: var(--wy-teal-light);
}

.wy-guide-table th {
    padding:       0.65rem 1rem;
    text-align:    left;
    font-weight:   600;
    font-size:     0.78rem;
    color:         var(--wy-teal-dark);
    white-space:   nowrap;
    border-bottom: 1px solid #c9e3de;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.wy-guide-table td {
    padding:        0.7rem 1rem;
    border-bottom:  1px solid var(--wy-border);
    color:          var(--wy-text);
    vertical-align: middle;
}

.wy-guide-table tbody tr:last-child td { border-bottom: none; }
.wy-guide-table tbody tr:hover         { background: #f7fbf9; }

.wy-col-filter {
    color:          var(--wy-text-hint);
    margin-left:    0.3rem;
    vertical-align: middle;
}

.wy-col-action {
    width:      44px;
    text-align: center;
}

.wy-table-link {
    color:           var(--wy-teal);
    text-decoration: none;
    font-weight:     500;
}

.wy-table-link:hover {
    text-decoration: underline;
    color:           var(--wy-teal-dark);
}

.wy-table-empty,
.wy-table-loading {
    padding:    1.5rem 1rem;
    text-align: center;
    color:      var(--wy-text-hint);
    font-size:  0.875rem;
}

/* ── Eye button ── */
.wy-table-eye-btn {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           28px;
    height:          28px;
    border-radius:   6px;
    color:           var(--wy-teal);
    transition:      background 0.15s, color 0.15s;
    text-decoration: none;
}

.wy-table-eye-btn:hover {
    background: var(--wy-teal-light);
    color:      var(--wy-teal-dark);
}

/* ── Availability badges ── */
.wy-badge {
    display:       inline-flex;
    align-items:   center;
    gap:           0.3rem;
    padding:       0.2rem 0.6rem;
    border-radius: 999px;
    font-size:     0.76rem;
    font-weight:   500;
    white-space:   nowrap;
}

.wy-badge--full-text   { background: var(--wy-teal-light); color: var(--wy-teal-dark); }
/* MVP's Review screen uses a distinct blue "Open access" badge (b-blue),
   separate from green "In collection" — was sharing the teal/green tone
   here before the flow_55 alignment pass; see docs/mvp-flow55-alignment-spec.md §0. */
.wy-badge--open-access { background: var(--wy-blue-bg); color: var(--wy-blue); }
.wy-badge--library     { background: #ecfdf5; color: #065f46; }
/* MVP treats "ILL" as purple consistently (Request ILL buttons, Fulfillment's
   b-purple ILL badge) — matches that here instead of the previous amber tone. */
.wy-badge--ill         { background: var(--wy-purple-bg); color: var(--wy-purple); }
.wy-badge--ai-only     { background: #fff7ed; color: #9a3412; }
.wy-badge--doi         { background: #eff6ff; color: #1e40af; }
.wy-badge--default     { background: #f3f4f6; color: #4b5563; }

/* ── Syllabus Review (mandatory_reading_list guide mode) ──
   Ported from the MVP mockup's .stat3/.exgroup/.exitem/.tag rules,
   restyled onto the --wy-* design tokens used across this theme. */
.wy-stat3 {
    display:               grid;
    grid-template-columns: repeat(3, 1fr);
    gap:                   0.8rem;
    margin:                0 0 1.3rem;
}

.wy-stat {
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    padding:       0.9rem 1rem;
    text-align:    center;
}

.wy-stat__value {
    font-size:   1.7rem;
    font-weight: 700;
    color:       var(--wy-text);
}

.wy-stat__label {
    font-size: 0.78rem;
    color:     var(--wy-text-hint);
}

@media (max-width: 640px) {
    .wy-stat3 { grid-template-columns: 1fr; }
}

/* ── Review: display-mode radios + assignment cards ────────────────────────
   The toggle sits under the Course Collection hero, above the first box. */
.wy-revmode {
    display:       flex;
    align-items:   center;
    gap:           1rem;
    flex-wrap:     wrap;
    margin:        0 0 1rem;
    padding:       0.6rem 0.9rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-card-bg);
    font-size:     0.86rem;
}

.wy-revmode__lbl {
    font-weight:    700;
    font-size:      0.72rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color:          var(--wy-text-hint);
}

.wy-revmode__opt {
    display:     inline-flex;
    align-items: center;
    gap:         0.4rem;
    cursor:      pointer;
    color:       var(--wy-text);
}

.wy-revmode__opt input { accent-color: var(--wy-teal); cursor: pointer; margin: 0; }

/* `display` beats the UA's [hidden] rule, so an inline-flex element with the
   hidden ATTRIBUTE set stays on screen. The faculty filter is hidden from JS
   once nothing is unresolved, and without this it never went anywhere — the
   attribute was set, screen readers dropped it, and it kept rendering. */
.wy-revmode__opt[hidden] { display: none; }

/* One assignment. Its own disclosure, holding the same five boxes the flat
   view shows — which is why the boxes inside are inset rather than restyled. */
.wy-asgn {
    margin:        0 0 1rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    background:    var(--wy-card-bg);
    overflow:      hidden;
}

.wy-asgn__head {
    display:     flex;
    align-items: flex-start;
    gap:         0.6rem;
    padding:     0.85rem 1rem;
    cursor:      pointer;
    list-style:  none;
}

.wy-asgn__head::-webkit-details-marker { display: none; }
.wy-asgn[open] > .wy-asgn__head { border-bottom: 1px solid var(--wy-border); }
.wy-asgn__content { flex: 1 1 auto; min-width: 0; }

.wy-asgn__eyebrow {
    display:        block;
    font-size:      0.72rem;
    font-weight:    700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color:          var(--wy-teal);
    margin-bottom:  0.12rem;
}

.wy-asgn__title { display: block; font-weight: 700; font-size: 0.98rem; color: var(--wy-text); }

.wy-asgn__desc {
    display:   block;
    font-size: 0.84rem;
    color:     var(--wy-text-muted);
    margin-top: 0.15rem;
}

.wy-asgn__count {
    flex-shrink: 0;
    font-size:   0.8rem;
    color:       var(--wy-text-muted);
    white-space: nowrap;
    padding-top: 0.1rem;
}

/* An assignment with nothing sourced yet. Quieter than a count, but present:
   on a review screen the empty assignment is the gap worth seeing. */
.wy-asgn__count--none { font-style: italic; color: var(--wy-text-hint); }

.wy-asgn__body { padding: 0.6rem 0.9rem 0.9rem; }
.wy-asgn__body > .wy-exgroup { margin-bottom: 0.6rem; }
.wy-asgn__body > .wy-exgroup:last-child { margin-bottom: 0; }

.wy-exgroup {
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-card-bg);
    margin-bottom: 0.7rem;
    overflow:      hidden;
}

/* Manual chevron (not the native <summary> marker — list-style:none below
   suppresses that) so the disclosure indicator and the title always sit on
   one row regardless of how much content follows underneath, e.g. Review's
   4-box Course Materials/WyderNet Recommendations x Ready to Use/Need
   Sourcing split, which puts an explainer sentence below the title. Same
   pattern as .wy-gap-chev/.wy-gap-summary elsewhere on this screen. */
.wy-exgroup__head {
    display:       flex;
    align-items:   flex-start;
    gap:           0.6rem;
    padding:       0.7rem 0.95rem;
    background:    var(--wy-bg);
    border-bottom: 1px solid var(--wy-border);
    cursor:        pointer;
    color:         var(--wy-text);
    list-style:    none;
}

.wy-exgroup__head::-webkit-details-marker {
    display: none;
}

.wy-exgroup:not([open]) .wy-exgroup__head {
    border-bottom: none;
}

.wy-exgroup__chev {
    flex-shrink: 0;
    font-weight: 700;
    color:       var(--wy-text-hint);
    line-height: 1.5;
    transition:  transform 0.15s;
}

/* Keyed on the CHEVRON inside any open disclosure's summary — NOT on the
   summary's own class, which is what broke. `.wy-exgroup__chev` is the shared
   glyph and it is used under two different summaries: `.wy-exgroup__head` on
   the five section boxes, and `.wy-asgn__head` on an assignment card. The old
   selector named only the first, so "For the whole course" opened while still
   pointing right — a disclosure whose icon does not move reads as a dead
   control. Any future summary that reuses this glyph now rotates for free. */
details[open] > summary .wy-exgroup__chev {
    transform: rotate(90deg);
}

/* Everything to the right of the chevron — title/count row, plus
   .wy-exgroup__desc underneath when present. min-width:0 lets long titles
   wrap/truncate instead of overflowing the card on narrow screens. */
.wy-exgroup__content {
    flex:      1 1 auto;
    min-width: 0;
}

/* Title + count row — the head's own flex line; .wy-exgroup__desc (the
   collapsed-state explainer sentence) sits underneath it, outside this row,
   so it's visible before expanding without competing with the count for
   the same line. */
.wy-exgroup__head-row {
    display:     flex;
    align-items: center;
    gap:         0.5rem;
    font-weight: 600;
    font-size:   0.92rem;
}

.wy-exgroup__count {
    margin-left: auto;
    font-size:   0.78rem;
    font-weight: 500;
    color:       var(--wy-text-hint);
}

.wy-exgroup__desc {
    margin:      0.3rem 0 0;
    font-size:   0.8rem;
    font-weight: 400;
    color:       var(--wy-text-muted);
}

.wy-exgroup__body {
    padding: 0 0.2rem;
}

/* WyderNet Recommendations — Ready to Use: select + "Send to instructor"
   toolbar (mock's .rectools) and per-row checkbox (mock's .recpick). */
/* Per-box bulk toolbar. Named for the Recommendations box it was built for;
   it is now on every box except Deleted resources (whose action is Restore).
   See wy_app_review_bulk_bar(), lti-parts/part-app-review.php. */
.wy-bulk-actions {
    display:     inline-flex;
    align-items: center;
    gap:         0.45rem;
    flex-wrap:   wrap;
}

/* Disabled Delete: still legible, clearly inert. A bulk action that looks
   pressable with nothing selected is how you get an empty-selection alert. */
.wy-bulk-actions .wy-btn:disabled {
    opacity: 0.45;
    cursor:  not-allowed;
}

.wy-rectools {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             0.6rem;
    padding:         0.55rem 0.75rem;
    border-top:      1px solid var(--wy-border);
    background:      rgba(255, 255, 255, 0.5);
    flex-wrap:       wrap;
}
.wy-rectools__selall {
    display:     flex;
    align-items: center;
    gap:         0.4rem;
    font-size:   0.82rem;
    font-weight: 600;
    color:       var(--wy-text);
    cursor:      pointer;
}
.wy-rectools__selall input { accent-color: var(--wy-teal); cursor: pointer; }
/* Top-aligned box of the same height as the chevron, with the checkbox
   centred inside it — that is what puts the two controls and the title on
   one line. align-self is required because .wy-exitem is align-items:center;
   without it the checkbox re-centres against the full row height. */
.wy-rec-pick {
    display:         flex;
    align-items:     center;
    justify-content: center;
    align-self:      flex-start;
    flex-shrink:     0;
    /* Exactly the input's own width, so this box's left edge IS the
       checkbox's left edge — that is what lines it up with "Select all
       shown" above, which has no such box and starts at the same padding. */
    width:           1rem;
    height:          var(--wy-rowctl-size);
    cursor:          pointer;
}
.wy-rec-pick input {
    accent-color: var(--wy-teal);
    cursor:       pointer;
    width:        1rem;
    height:       1rem;
    margin:       0;
}

/* "Select all shown" sits in the same left column as the row checkboxes:
   .wy-rectools and .wy-exitem share 0.75rem of left padding, so the two
   checkboxes line up only while this box is the same width as .wy-rec-pick
   and centres its input the same way. */
.wy-rectools__selall input {
    width:  1rem;
    height: 1rem;
    margin: 0;
}
.wy-exitem {
    display:       flex;
    align-items:   center;
    gap:           0.7rem;
    padding:       0.6rem 0.75rem;
    border-top:    1px solid var(--wy-border);
    font-size:     0.88rem;
}

.wy-exitem[hidden] { display: none; }

/* Icon-only row actions (View/Remove on a recommendation row) — compact,
   neutral by default so a badge + two icons still fit .wy-exitem__status
   without crowding; .wy-remove-resource gets the danger tint on hover only,
   matching the weight of .wy-dash-row__delete elsewhere without the
   permanent red chip (too heavy for an inline row action). */
.wy-icon-btn {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           30px;
    height:          30px;
    /* Declared, not assumed: the parent theme pads every <button> by
       13px/15px, which on a 30px border-box leaves a NEGATIVE content box and
       collapses the 16px glyph below to zero width. Every .wy-icon-btn on a
       page that loads that stylesheet used to be an <a>, so the button variant
       rendered an empty square the first time one appeared (the faculty
       review's comment control). Found by looking at the page. */
    padding:         0;
    border-radius:   var(--wy-radius-sm);
    border:          1px solid var(--wy-border);
    background:      var(--wy-card-bg);
    color:           var(--wy-text-muted);
    cursor:          pointer;
    flex-shrink:     0;
}
.wy-icon-btn:hover { background: var(--wy-bg); color: var(--wy-text); }
.wy-icon-btn.wy-remove-resource:hover { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.wy-icon-btn svg { width: 16px; height: 16px; }

/* An action that is not available YET (Edit, while a collection is still
   generating). Kept in place and kept visible rather than removed: a control
   that vanishes and reappears makes the row jump and leaves no clue that the
   action exists at all. aria-disabled carries the state for assistive tech;
   the page's click handler is what actually blocks it. */
.wy-icon-btn--disabled,
.wy-icon-btn[aria-disabled="true"] {
    opacity:        .45;
    cursor:         not-allowed;
    pointer-events: auto;      /* the click handler still needs the event */
}
.wy-icon-btn--disabled:hover,
.wy-icon-btn[aria-disabled="true"]:hover {
    background: transparent;   /* no hover affordance for a dead control */
    color:      inherit;
}
/* The hover rule above touches colour, not outline — but this component has
   lost its focus ring twice before to exactly that kind of edit, so the ring
   is restored here at higher specificity where source order cannot decide it. */
.wy-dash-row .wy-icon-btn:focus-visible {
    outline:        2px solid var(--wy-teal-dark);
    outline-offset: 2px;
}

/* .wy-progress centres itself and caps at 340px for the pending screen. In a
   collection row it spans the row and sits under the title, so the bar reads
   as belonging to that collection rather than to the page. */
.wy-dash-progress {
    max-width: none;
    margin:    .15rem 0 .55rem 2.1rem;   /* aligns with the row title, clear of the expand caret */
}
.wy-dash-progress .wy-progress__track { height: 5px; }
.wy-dash-progress .wy-progress__label {
    justify-content: flex-start;
    gap:             .6rem;
    margin:          .3rem 0 0;
    font-size:       .75rem;
}

/* In-app "View" detail — inline expand instead of navigating away, since
   many licensed database/journal URLs block iframe embedding outright. */
.wy-exitem-detail {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             0.75rem;
    padding:         0.5rem 0.75rem;
    margin:          0 0 0.4rem;
    background:      var(--wy-bg);
    border-radius:   var(--wy-radius-sm);
    font-size:       0.8rem;
    color:           var(--wy-text-muted);
}
.wy-exitem-detail[hidden] { display: none; }
.wy-exitem-detail__url { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.wy-exitem:first-child {
    border-top: none;
}

.wy-exitem__text {
    flex:          1;
    min-width:     0;
    display:       flex;
    flex-direction: column;
    gap:           0.1rem;
}

.wy-exitem__title {
    color:       var(--wy-text);
    font-weight: 700;
    font-size:   0.95rem;
}

.wy-exitem__meta {
    font-size: 0.78rem;
    color:     var(--wy-text-hint);
}

/* Librarian's "Note to students" (tagged-items.php's wy_render_student_
   material_items()) — deliberately --wy-text-muted, not the lighter
   --wy-text-hint above: this is librarian-authored guidance a student is
   meant to actually read, not decorative metadata, and --wy-text-hint's
   ~2.5:1 contrast on white fails WCAG AA (see Accessibility Standard,
   project CLAUDE.md) for text this size. */
.wy-exitem__note {
    display:   block;
    font-size: 0.82rem;
    color:     var(--wy-text-muted);
    margin-top: 0.15rem;
}

.wy-exitem__title-row {
    display:     flex;
    align-items: center;
    gap:         0.4rem;
    flex-wrap:   wrap;
}

/* ── Preview screen: outer section card ("Course Materials"/"Library
   Recommended Materials"/a plain librarian-created section) vs. the inner
   per-assignment cards nested inside it (.wy-exgroup--sub below). Both
   reuse the base .wy-exgroup component (border/radius/white background),
   but need two different visual weights so the nesting reads at a glance
   instead of every level looking identical. Scoped to these two modifiers
   only — the bare .wy-exgroup (Review's 4 boxes, Materials' list) is
   untouched, since those aren't nested inside one another. ── */
.wy-exgroup--section {
    box-shadow:    var(--wy-shadow);
    margin-bottom: 1.1rem;
}
.wy-exgroup--section > .wy-exgroup__head {
    background:    transparent;
    border-bottom: none;
    padding:       1rem 1.1rem 0.6rem;
}
/* .wy-exgroup__title is now a real h2 (top-level Course Materials/Library
   Recommended Materials/custom sections) or h3 (per-assignment subgroups)
   for screen-reader heading navigation (accessibility audit, 2026-08-23) —
   previously a plain <span> inheriting its parent row's styling. This base
   reset keeps every existing visual size/weight untouched; the more
   specific --section rule below still overrides it for that variant same
   as before. */
.wy-exgroup__title {
    margin:      0;
    font:        inherit;
    color:       inherit;
    display:     inline;
}
.wy-exgroup--section > .wy-exgroup__head .wy-exgroup__title {
    color:       var(--wy-teal-dark);
    font-weight: 700;
    font-size:   1.05rem;
}
.wy-exgroup--section > .wy-exgroup__body {
    padding:        0 1.1rem 1.1rem;
    display:        flex;
    flex-direction: column;
    gap:            0.85rem;
}

.wy-exgroup--sub {
    background:    var(--wy-bg);
    box-shadow:    none;
    margin-bottom: 0;
}
.wy-exgroup--sub > .wy-exgroup__head {
    background: transparent;
}
.wy-exgroup--sub > .wy-exgroup__body {
    padding: 0.15rem 0 0;
}
.wy-exgroup--sub .wy-exitem {
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    margin:        0 0.7rem 0.55rem;
    padding-left:  0.75rem;
    padding-right: 0.75rem;
}

/* Expandable row (all four Review boxes) — collapsed state shows title +
   pills only, left-aligned; expanding reveals the byline (author ·
   publication year · container). Reuses .wy-gap-summary/.wy-gap-chev
   (already generic, not gap-row-specific) so the exact same chevron/cursor/
   rotation behavior applies everywhere a row expands on this screen. */
.wy-exitem-details {
    flex:      1;
    min-width: 0;
}
.wy-exitem-pills {
    display:     flex;
    align-items: center;
    gap:         0.35rem;
    flex-wrap:   wrap;
    margin-top:  0.15rem;
}
.wy-exitem-expand {
    font-size:   0.78rem;
    color:       var(--wy-text-hint);
    margin-top:  0.3rem;
    /* Lines up with the title text above, not the chevron button to its
       left — .wy-gap-summary's chevron (30px) + gap (0.5rem = 8px) = 38px,
       and this sits as a sibling of <summary>, outside its flex layout, so
       it needs the same offset applied manually to align. */
    margin-left: 38px;
}
.wy-exitem-expand > div + div {
    margin-top: 0.2rem;
}

/* Bibliographic line (inc/bibliography.php's wy_bib_line_html()) — the
   type-aware "author · year · journal · DOI" line shown on EVERY item row
   in every Review box, collapsed or expanded, per
   WyderNet_Metadata_by_Item_Type.docx. Deliberately --wy-text-muted rather
   than --wy-text-hint: this is the citation a librarian reads to decide
   whether a match is right, not decorative metadata, and it needs the
   stronger of the two AA-clearing tones (see --wy-text-muted's own note).
   The leading fragment (usually the author) is emphasized to full --wy-text
   the same way the mockup's `.bib .k` is, so a long list of rows can be
   scanned down the authors alone. */
.wy-bib {
    display:     block;
    font-size:   0.8rem;
    line-height: 1.45;
    color:       var(--wy-text-muted);
    margin-top:  0.25rem;
}
.wy-bib__k {
    color:       var(--wy-text);
    font-weight: 600;
}

/* Edit-modal fieldset for a database's librarian-maintained bibliographic
   fields (part-app-review.php). A <fieldset> for the real grouping semantics
   — six related inputs under one legend — with the browser's default border/
   padding/margin cleared so it matches the flat single-column form around it
   rather than announcing itself as a box. The four short fields pair up on
   wide screens and stack below --wy-bp-sm, where a half-width text input
   stops being usable. */
.wy-edit-bib {
    border:  0;
    margin:  0 0 1rem;
    padding: 0;
}
.wy-edit-bib > legend {
    padding:       0;
    margin-bottom: 0.5rem;
}
.wy-edit-bib__grid {
    display:               grid;
    grid-template-columns: 1fr 1fr;
    gap:                   0.3rem 0.7rem;
    margin-bottom:         0.8rem;
}
@media (max-width: 480px) {
    .wy-edit-bib__grid {
        grid-template-columns: 1fr;
    }
}

.wy-tag {
    flex-shrink:   0;
    font-size:     0.7rem;
    font-weight:   600;
    border-radius: 6px;
    padding:       0.15rem 0.5rem;
    white-space:   nowrap;
}

/* .wy-tag--uncertain retired — the 'uncertain' requirement_level folded into
   'recommended'; its amber tone lives on now as .wy-hint--low-confidence.
   .wy-tag--recommended retired the same way — nothing sets origin
   'recommended' as a requirement_level value anymore (see 'additional'
   below); the badge for an AI-recommended ITEM is .wy-tag--rec. */

/* Classification pills — Required / Optional / WyderNet (see
   wy_tagged_item_badge(), inc/tagged-items.php): one purple treatment for
   all three, since all three answer the same question ("why is this on the
   guide") even though they come from different signals (syllabus
   requirement wording vs. an AI recommendation). Kept as three class names
   (not one shared class) so a future need to differentiate them again
   doesn't require touching every call site — today they're identical. */
.wy-tag--required,
.wy-tag--additional,
.wy-tag--rec {
    background: #f2f0fb;
    color:      #5a4fcf;
}

/* Material type — text-only gray pill (wy_tagged_item_type_badge()/
   wy_app_review_format_badge()), same box model as the purple classification
   pills above (all inherited from .wy-tag: height, font, padding, radius) so
   the two categories differ ONLY by background/color, never by shape —
   deliberately no border, no icon, no flex layout here, so nothing about
   this pill's rendered box differs from .wy-tag--required/--rec/--additional
   in any of the four Review boxes. */
.wy-tag--format {
    background: #efeee7;
    color:      #5f5e55;
}

/* ── Callout (informational note) ── */
.wy-callout {
    display:       flex;
    align-items:   flex-start;
    gap:           0.6rem;
    background:    #fbfdfc;
    border:        1px solid var(--wy-border);
    border-left:   3px solid var(--wy-teal);
    border-radius: var(--wy-radius-sm);
    padding:       0.85rem 1.05rem;
    font-size:     0.85rem;
    color:         var(--wy-text-muted);
    text-align:    left;
}

.wy-callout__icon {
    flex-shrink: 0;
    color:       var(--wy-teal);
    margin-top:  0.1rem;
}

/* ── Review screen hero card ──
   Ported from the MVP mockup's .cc-* rules (flow_55.html, `review` screen)
   onto this project's --wy-* tokens — see part-app-review.php and
   docs/mvp-flow55-alignment-spec.md §10. .wy-card defaults to centered text
   and 2.5rem/2rem padding (built for the icon-card screens); this card
   needs MVP's left-aligned, tighter layout instead. */
.wy-cc-card {
    padding:    1.5rem 1.6rem;
    text-align: left;
    margin-bottom: 1.5rem;
}

.wy-cc-head {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             12px;
    margin-bottom:   0.6rem;
}

.wy-cc-title {
    display:        flex;
    align-items:    center;
    gap:            0.5rem;
    font-size:      0.72rem;
    font-weight:    800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color:          var(--wy-text);
}

.wy-cc-title .ic {
    stroke: var(--wy-teal);
}

.wy-cc-tag {
    font-size:   0.75rem;
    color:       var(--wy-text-hint);
    font-weight: 600;
}

/* No trailing margin — .wy-cc-hero's own margin-bottom already provides
   the gap above this sentence, and .wy-cc-card's padding-bottom closes the
   card out. A leftover margin-bottom here (from when this sentence used to
   sit BEFORE the hero, not after) doubled up with the card padding into
   ~2.5rem of dead space below the text vs. only 1.5rem above the eyebrow —
   this keeps the two symmetric. */
.wy-cc-desc {
    color:     var(--wy-text-muted);
    font-size: 0.9rem;
    margin:    0;
}

.wy-cc-hero {
    display:       flex;
    align-items:   baseline;
    gap:           0.7rem;
    margin-bottom: 0.5rem;
}

.wy-cc-num {
    font-size:      2.9rem;
    font-weight:    800;
    letter-spacing: -0.02em;
    line-height:    1;
    color:          var(--wy-text);
}

.wy-cc-lbl {
    font-size: 0.95rem;
    color:     var(--wy-text-muted);
}

.wy-cc-bar {
    display: flex;
    gap:     3px;
    height:  13px;
    margin:  0.2rem 0 1.1rem;
}

.wy-cc-seg {
    height:        100%;
    border-radius: 4px;
}

.wy-cc-seg--req { background: var(--wy-teal); }
.wy-cc-seg--add { background: #7cc3a4; }

.wy-cc-kids {
    display:               grid;
    grid-template-columns: 1fr 1fr;
    gap:                   1rem;
}

.wy-cc-kid {
    display:     flex;
    gap:         0.65rem;
    align-items: flex-start;
}

.wy-cc-dot {
    width:         12px;
    height:        12px;
    border-radius: 4px;
    margin-top:    5px;
    flex:          0 0 auto;
}

.wy-cc-dot--req { background: var(--wy-teal); }
.wy-cc-dot--add { background: #7cc3a4; }

.wy-cc-knum {
    font-size:      1.5rem;
    font-weight:    800;
    letter-spacing: -0.01em;
    line-height:    1.1;
    color:          var(--wy-text);
}

.wy-cc-kname {
    font-size:   0.9rem;
    font-weight: 700;
    margin:      1px 0 2px;
    color:       var(--wy-text);
}

.wy-cc-khelp {
    font-size:   0.8rem;
    color:       var(--wy-text-hint);
    line-height: 1.4;
}

@media (max-width: 560px) {
    .wy-cc-kids { grid-template-columns: 1fr; }
}

/* ── Sourcing row component (mandatory_reading_list guide mode, Phase B1) ──
   Originally the standalone Gaps screen (now retired — Review's own "Course
   Materials — Need Sourcing" box has the same actions inline), ported 1:1
   from the MVP mockup's "Gap analysis" screen (C:\temp\wy\mvp\files\
   index.html, data-screen="gaps") — crumb + h1 + lead, then a single card of
   .wy-gaprow rows. Still used by Review and Fulfillment. Mockup color refs:
   --purple:#5a4fcf (ILL), --amber:#b06a12 (purchase), --green-d:#217a5c /
   --ink3:#97958b. The amber and ink3 refs are the mockup's originals; both
   fail WCAG AA on their own backgrounds, so this stylesheet ships the
   darkened --wy-amber instead (see the token block at the top). */

.wy-crumb {
    font-size:     0.85rem;
    color:         var(--wy-text-hint);
    margin-bottom: 0.35rem;
}

.wy-crumb b {
    color: var(--wy-text);
}

.wy-crumb a {
    color:           var(--wy-teal-dark);
    text-decoration: none;
    font-weight:     600;
}
.wy-crumb a:hover {
    text-decoration: underline;
}

.wy-page-title {
    font-size:      1.7rem;
    font-weight:    800;
    letter-spacing: -0.01em;
    margin:         0 0 0.75rem;
    color:          var(--wy-text);
}

.wy-lead {
    color:  var(--wy-text-muted);
    margin: 0 0 1.4rem;
    max-width: 720px;
}

.wy-lead-ok {
    color: #217a5c;
}

.wy-lead-warn {
    color: var(--wy-amber);
}

.wy-gaps-card {
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       1.5rem 1.6rem;
}

.wy-gaps-card-title {
    font-weight:   700;
    margin-bottom: 0.3rem;
}

.wy-gaps-card-desc {
    font-size:     0.84rem;
    color:         var(--wy-text-hint);
    margin-bottom: 0.7rem;
}

.wy-gaprow {
    display:               grid;
    /* Three columns since bulk select reached this box: the checkbox, the
       item, its actions. The checkbox column is `auto` so it collapses to
       nothing on any future gap row rendered without one. */
    grid-template-columns: auto 1fr auto;
    gap:                   0.9rem;
    align-items:           center;
    padding:               0.9rem 0;
    border-top:            1px solid var(--wy-border);
}

/* This row is a grid, not a flex line, so .wy-rec-pick's align-self does the
   work on its own — but the row's own 0.9rem top padding sits above the
   title, so the control box starts from the content edge like the chevron
   beside it. */
.wy-gaprow > .wy-rec-pick { align-self: start; }

.wy-gaprow[hidden] { display: none; }

.wy-gaprow:first-of-type {
    border-top: 0;
}

.wy-gap-t {
    font-weight: 700;
    font-size:   0.95rem;
    color:       var(--wy-text);
}

.wy-gap-m {
    font-size:   0.8rem;
    color:       var(--wy-text-hint);
    margin-top:  0.1rem;
}

.wy-gap-note {
    font-size:  0.78rem;
    color:      var(--wy-text-hint);
    margin-top: 0.3rem;
}

/* Unverified resolver link (e.g. an institution's SFX/OpenURL service) —
   EDS found it but couldn't confirm full text; the librarian checks it
   themselves before requesting ILL. Distinct amber tint so it doesn't read
   as a confirmed "held" signal. */
.wy-gap-link {
    display:      flex;
    align-items:  center;
    gap:          0.35rem;
    font-size:    0.78rem;
    color:        var(--wy-amber);
    margin-top:   0.35rem;
}

.wy-gap-link svg {
    flex-shrink: 0;
}

.wy-gap-link a {
    color:           var(--wy-amber);
    font-weight:     600;
    text-decoration: underline;
}

.wy-gap-link a:hover {
    color: #94590f;
}

/* ── Fulfillment's Open / Fulfilled lists: an action COLUMN ───────────────
   Scoped to .wy-gaps-card because Review embeds .wy-gaprow too, where the
   actions are icons rather than long text links and the current behaviour is
   right.

   The row is `auto 1fr auto` and carries only two children here, so the
   details land in the AUTO column and size to their own text — a long title
   pushed the actions somewhere different on every row. Naming a fixed width
   for the action column is what turns five ragged blocks into one column.

   The width is set from the longest label the screen can show ("Add approved
   to collection"), so nothing has to wrap to fit; --wy-ff-act-col is the one
   place to change if a longer action is ever added. */
.wy-gaps-card {
    --wy-ff-act-col: 15rem;
}

.wy-gaps-card .wy-gaprow {
    /* TWO columns, because a fulfillment row has no bulk-select checkbox — it
       is the details and the actions. With the inherited three-column
       template the actions auto-placed into the `1fr` column and shrink-wrapped
       inside it, which is why naming a width for the third column alone
       changed nothing. */
    grid-template-columns: 1fr var(--wy-ff-act-col);
}

.wy-gaps-card .wy-gap-act {
    /* The action area IS the column, rather than a blob sitting at its end —
       that is what makes every row's actions start and end on the same two
       vertical lines. Pinned to the LAST column so a checkbox added here later
       cannot push it back into the flexible one, and stretched because the
       base rule's `justify-self: end` otherwise shrink-wraps it to its text. */
    grid-column:     -2 / -1;
    justify-self:    stretch;
    width:           100%;
    flex-direction:  column;
    align-items:     flex-end;
    justify-content: center;
    flex-wrap:       nowrap;
    gap:             0.5rem;
    text-align:      right;
}

/* One line each. "Mark received — add link" broke across two lines mid-phrase
   on the narrower rows, which is what made the list read as uneven. */
.wy-gaps-card .wy-gap-act .wy-link-action,

/* Narrow screens: the details and the actions become two rows rather than two
   columns. 15rem of action column plus a title has nowhere to go at phone
   width, and letting it wrap is the awkwardness this replaces. */
@media (max-width: 760px) {
    .wy-gaps-card .wy-gaprow {
        grid-template-columns: auto 1fr;
    }

    .wy-gaps-card .wy-gap-act {
        grid-column:  1 / -1;
        width:        auto;
        align-items:  flex-start;
        text-align:   left;
        margin-top:   0.2rem;
    }

    /* Wrapping is correct here: a long label has to fit a narrow screen, and
       the column alignment it would break no longer exists. */
    .wy-gaps-card .wy-gap-act .wy-link-action,
}

.wy-gap-act {
    display:       flex;
    gap:           0.6rem;
    align-items:   center;
    flex-wrap:     wrap;
    justify-self:  end;
    /* Buffer before the card's right edge — Fulfillment's .wy-gaps-card
       already has generous padding (1.5rem 1.6rem) so this is a small
       top-up there; Review's embedded version needs more (see the
       .wy-exgroup__body-scoped override below, which fully matches
       .wy-exitem's inset instead of just adding a bit more). */
    padding-right: 0.5rem;
}

/* Review screen only — .wy-gaprow/.wy-gap-act's shared base above assumes
   Fulfillment's .wy-gaps-card padding (1.5rem 1.6rem); the embedded version
   here has none (.wy-exgroup__body's own inset is ~0.2rem), which left
   Need Sourcing's rows sitting noticeably closer to the left edge than
   Ready to Use's .wy-exitem rows (0.75rem inset) right above them. Matches
   .wy-exitem's inset exactly rather than layering on more ad hoc padding. */
.wy-exgroup__body .wy-gaprow {
    padding-left:  0.75rem;
    padding-right: 0.75rem;
}
.wy-exgroup__body .wy-gap-act {
    padding-right: 0;
}

.wy-gap-act .wy-link-action {
    background:      none;
    border:          0;
    color:           var(--wy-teal);
    font-weight:     600;
    font-size:       0.8rem;
    cursor:          pointer;
    font-family:     var(--wy-font);
    padding:         0;
    text-decoration: none;
}

.wy-gap-act .wy-link-action:hover {
    text-decoration: underline;
}

.wy-btn--sm {
    padding:       0.5rem 1rem;
    font-size:     0.85rem;
    border-radius: 11px;
}

.wy-btn--ill {
    background: #5a4fcf;
}

.wy-btn--ill:hover:not(:disabled),
.wy-btn--ill:focus:not(:disabled) {
    background: #4a3fb8;
}

.wy-btn--buy {
    background: var(--wy-amber);
}

.wy-btn--buy:hover:not(:disabled),
.wy-btn--buy:focus:not(:disabled) {
    background: #94590f;
}

/* OER — tinted outline, same treatment as the mock's .btn.oer (a lighter
   weight than the solid ILL/Purchase actions, since finding an open copy
   isn't a request to anyone). */
.wy-btn--oer {
    background: var(--wy-teal-light);
    border:     1.5px solid var(--wy-teal-bd);
    color:      var(--wy-teal-dark);
}

.wy-btn--oer:hover:not(:disabled),
.wy-btn--oer:focus:not(:disabled) {
    background: #d9ebe1;
}

.wy-tag--requested {
    display:       inline-flex;
    align-items:   center;
    gap:           0.3rem;
    background:    #e6f2ec;
    color:         #1a6249;
    border:        1px solid #c9e3d5;
    border-radius: 20px;
    padding:       0.4rem 0.85rem;
    font-size:     0.82rem;
    font-weight:   600;
    white-space:   nowrap;
}

@media (max-width: 640px) {
    .wy-gaprow {
        grid-template-columns: 1fr;
    }
    .wy-gap-act {
        justify-self: start;
    }
}

/* "Source this resource" dropdown — replaces the OER/ILL/Purchase button
   trio on a Need Sourcing row with one trigger icon; the three actions move
   into a popover menu. .wy-source-wrap gives the popover a positioning
   anchor (the row itself may be inside several nested flex/grid contexts
   with their own stacking, so anchoring directly to the trigger's own
   wrapper is more reliable than trying to position off the row or card). */
.wy-source-wrap {
    position: relative;
    display:  inline-flex;
}
.wy-source-btn {
    background:   var(--wy-teal-light);
    border-color: var(--wy-teal-bd);
    color:        var(--wy-teal-dark);
}
.wy-source-btn:hover { background: #d9ebe1; }
/* position:fixed, not absolute — the outer accordion card (.wy-exgroup) has
   overflow:hidden for its rounded corners, which was silently clipping any
   absolutely-positioned popover trying to escape a row inside it (the
   trigger's own .wy-source-wrap ancestor doesn't matter; .wy-exgroup is
   further up the tree either way). Fixed positioning is computed in JS
   (part-app-review.php's positionSourcePopover()) from the trigger's
   getBoundingClientRect() at open time — not clipped by ANY ancestor's
   overflow, regardless of nesting. top/left below are just a fallback
   default in case JS positioning fails for some reason; every real open
   overrides both inline. */
.wy-source-popover {
    position:      fixed;
    top:           0;
    left:          0;
    z-index:       1000;
    width:         260px;
    max-width:     calc(100vw - 2rem);
    /* Safety net for a very short viewport — JS (positionSourcePopover())
       already flips the popover above/below the trigger to fit normally;
       this only kicks in if the popover is taller than the viewport has
       room for either way, so it scrolls instead of running off-screen. */
    max-height:    calc(100vh - 2rem);
    overflow-y:    auto;
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       0.6rem;
    text-align:    left;
}
.wy-source-popover[hidden] { display: none; }
/* Left-anchored variant — for a trigger near the row's LEFT edge (the
   edition-warning icon, right after the title) rather than the right-edge
   "Source this resource" trigger the base right:0 rule was written for;
   opening rightward here keeps it inside the card instead of running off
   the left edge. */
.wy-source-popover--left {
    left:  0;
    right: auto;
}
.wy-source-popover__label {
    font-size:     0.68rem;
    font-weight:   700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color:         var(--wy-text-hint);
    padding:       0.2rem 0.5rem 0.5rem;
}
.wy-source-opt {
    display:         flex;
    align-items:     flex-start;
    gap:             0.6rem;
    width:           100%;
    background:      none;
    border:          0;
    border-radius:   var(--wy-radius-sm);
    padding:         0.5rem;
    text-align:      left;
    cursor:          pointer;
    font-family:     var(--wy-font);
    text-decoration: none;
    color:           inherit;
}
.wy-source-opt:hover { background: var(--wy-bg); }
.wy-source-opt__icon {
    flex:            0 0 auto;
    width:           28px;
    height:          28px;
    border-radius:   var(--wy-radius-sm);
    display:         flex;
    align-items:     center;
    justify-content: center;
}
.wy-source-opt__icon svg { width: 15px; height: 15px; }
.wy-source-opt--oer .wy-source-opt__icon { background: var(--wy-teal-light); color: var(--wy-teal-dark); }
.wy-source-opt--ill .wy-source-opt__icon { background: #eeeeff; color: #5a4fcf; }
.wy-source-opt--buy .wy-source-opt__icon { background: #fbf0dc; color: var(--wy-amber); }
.wy-source-opt--oa  .wy-source-opt__icon { background: var(--wy-blue-bg); color: var(--wy-blue); }
/* The library's own catalog: teal, the colour this product uses for "ours". */
.wy-source-opt--catalog .wy-source-opt__icon { background: var(--wy-teal-light); color: var(--wy-teal-dark); }

/* The catalog option is the only <button> in this menu; the other four are
   anchors. The parent theme styles `button:hover` with white text, and that
   rule beats `.wy-source-opt { color: inherit }` — so on hover the label and
   the magnifying glass turned white on a near-white background and vanished.
   Same failure this stylesheet has already had twice with `:hover` rules from
   outside overriding ours (see the focus-ring notes); pinned here at a
   specificity source order cannot undo, for every state that can repaint it. */
button.wy-source-opt,
button.wy-source-opt:hover,
button.wy-source-opt:focus,
button.wy-source-opt:active {
    color:      var(--wy-text);
    background: none;
}

button.wy-source-opt:hover { background: var(--wy-bg); }

button.wy-source-opt:hover  .wy-source-opt__icon,
button.wy-source-opt:focus  .wy-source-opt__icon,
button.wy-source-opt:active .wy-source-opt__icon {
    background: var(--wy-teal-light);
    color:      var(--wy-teal-dark);
}
.wy-source-opt__title {
    font-size:   0.85rem;
    font-weight: 700;
    color:       var(--wy-text);
}
.wy-source-opt__desc {
    font-size: 0.76rem;
    color:     var(--wy-text-muted);
}

/* Edition-not-confirmed warning icon (wy_reading_edition_disclaimer_html(),
   inc/tagged-items.php) — small, inline, no border/background (unlike
   .wy-source-btn's bordered chip), sitting right after the title the same
   way a footnote marker would. Reuses .wy-source-wrap/.wy-source-popover
   for the popover box itself. */
/* The frame, the size and the hover now come from .wy-icon-btn, which this
   button also carries — it sits between Open and Comment and has to read as
   the same kind of control. It used to declare its own 18px borderless box
   with `background: none`, and the parent theme's `button:hover` then painted
   over the glyph: reported as "on rollover it turns white and disappears".
   Only the colour is its own. */
.wy-edition-toggle {
    color: var(--wy-amber);
}
/* Two classes, so this beats .wy-icon-btn:hover's `color: var(--wy-text)` on
   source order AND on specificity — the amber is the whole signal, and a
   hover that greys it out says the opposite of what the icon means. */
.wy-icon-btn.wy-edition-toggle:hover {
    background: var(--wy-bg);
    color:      var(--wy-amber);
}
.wy-edition-popover__text {
    padding:   0.3rem 0.5rem 0.5rem;
    font-size: 0.82rem;
    color:     var(--wy-text);
}
.wy-edition-popover__link {
    display: block;
    padding: 0 0.5rem 0.4rem;
}

/* ── Review screen: expandable "Needs sourcing" rows ──
   Ported from the MVP's .arow/.ainfo/.adet/.adk/.adv rules (flow_55.html,
   `review` screen) — native <details>/<summary> instead of the MVP's
   .arow.open JS toggle. See part-app-review.php's wy_app_review_detail_rows()
   for why the revealed fields (Source/ISBN/Citation) differ from MVP's
   mockup values (Type/Edition) — those aren't in the reading schema. */
.wy-gap-summary {
    display:     flex;
    align-items: flex-start;
    gap:         0.5rem;
    cursor:      pointer;
    list-style:  none;
}

.wy-gap-summary::-webkit-details-marker {
    display: none;
}

/* Bordered icon-button chevron (matches .wy-icon-btn's Edit/View/Delete
   treatment) — a plain text "›" character read as unstyled/decorative;
   this reads as a real, clickable-looking toggle. Not an actual <button>
   (the whole <summary> is already the click target — nesting a real button
   inside it would create a redundant, confusing tab stop). */
.wy-gap-chev {
    flex-shrink:     0;
    width:           var(--wy-rowctl-size);
    height:          var(--wy-rowctl-size);
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    border:          1px solid var(--wy-border);
    border-radius:   var(--wy-radius-sm);
    background:      var(--wy-card-bg);
    color:           var(--wy-text-muted);
    transition:      transform 0.15s;
}

.wy-gap-chev svg {
    width:  14px;
    height: 14px;
}

/* Row with nothing to expand (a recommendation whose resource has no
   description) — keeps the chevron's 30px box, invisible, so the title/pills/
   bibliographic line sit on the SAME left edge as every row that does have a
   disclosure. Without it such a row loses the chevron (30px) + flex gap
   (0.5rem = 8px) = 38px of indent and visibly hangs left of its neighbours,
   which reads as a broken row rather than a row with less to show. The
   control itself is still omitted rather than rendered-and-inert: a
   disclosure with nothing behind it is worse than no disclosure.
   visibility:hidden (not display:none) is the point — it keeps the layout box
   while removing the border, background and glyph, and takes the span out of
   the accessibility tree. */
.wy-gap-chev--none {
    visibility: hidden;
}

/* Same generalisation as .wy-exgroup__chev above: the glyph decides, not
   whichever summary happens to wrap it. */
details[open] > summary .wy-gap-chev {
    transform: rotate(90deg);
}

.wy-gap-detail {
    display:               grid;
    grid-template-columns: auto 1fr;
    gap:                   0.3rem 1rem;
    margin:                0.7rem 0 0;
    padding-top:           0.7rem;
    border-top:            1px dashed var(--wy-border);
}

.wy-gap-detail-k {
    font-size:      0.68rem;
    font-weight:    700;
    color:          var(--wy-text-hint);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding-top:    0.06rem;
}

.wy-gap-detail-v {
    font-size: 0.82rem;
    color:     var(--wy-text);
}

/* ── Request-compose segmented toggle (ILL / Purchase) ──
   Ported from the MVP's .segbar/.seg rules (flow_55.html), docs/mvp-flow55-
   alignment-spec.md §12/§14. Links, not buttons — each option is a full
   navigation (?action=request_ill|request_purchase) so the page re-renders
   with the right defaults, rather than a client-side toggle. */
.wy-segbar {
    display:       inline-flex;
    border:        1px solid var(--wy-border);
    border-radius: 10px;
    overflow:      hidden;
    margin:        0.1rem 0 1.1rem;
}
.wy-seg {
    border:          0;
    background:      var(--wy-card-bg);
    font-family:     inherit;
    font-size:       0.85rem;
    font-weight:     600;
    color:           var(--wy-text-muted);
    padding:         0.5rem 1rem;
    cursor:          pointer;
    text-decoration: none;
    display:         inline-block;
}
.wy-seg + .wy-seg {
    border-left: 1px solid var(--wy-border);
}
.wy-seg--on {
    background: var(--wy-teal);
    color:      #fff;
}

/* ── 4-tab resource sourcing screens (ILL / Purchase / OER / Open Access) ──
   inc/request-tabs.php + lti-parts/part-app-request-{ill,purchase,oer,
   openaccess}.php. Section heading + collapsible metadata card + option-row
   list, all built from the existing --wy-* tokens rather than a parallel
   palette. */

.wy-sechead {
    font-size:      0.72rem;
    font-weight:    800;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color:          var(--wy-text-hint);
    margin:         0 0 0.55rem;
}

/* Collapsible metadata card (spec §3.4) — title + chevron always visible,
   the key/value grid + citation only once expanded. */
.wy-req-meta {
    background:    #fbfaf6;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    padding:       0.85rem 1.05rem;
    margin-bottom: 1.2rem;
}
.wy-req-meta__head {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             1rem;
    width:           100%;
    background:      none;
    border:          0;
    padding:         0;
    font:            inherit;
    cursor:          pointer;
    text-align:      left;
    outline:         none;
}
/* Neve's own theme CSS has a generic button:hover/:focus/:active rule
   (background: var(--primarybtnbg), a blue #2f5aae, color: #fff) — a bare
   element+pseudo-class selector, which beats a plain .wy-req-meta__head
   class rule on specificity regardless of load order. Re-declaring
   background/color/border explicitly on the SAME states, at class+pseudo
   specificity, is what actually wins. */
.wy-req-meta__head:hover,
.wy-req-meta__head:focus,
.wy-req-meta__head:active {
    background: none;
    color:      inherit;
    border:     0;
    outline:    none;
}
.wy-req-meta__head:hover .wy-req-meta__title,
.wy-req-meta__head:focus .wy-req-meta__title {
    color: var(--wy-teal-dark);
}
.wy-req-meta__head:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 3px;
    border-radius:  4px;
}
.wy-req-meta__title {
    font-weight: 700;
    font-size:   1rem;
    color:       var(--wy-text);
}
.wy-req-meta__chev {
    width:      18px;
    height:     18px;
    stroke:     var(--wy-text-hint);
    fill:       none;
    stroke-width: 2.2;
    flex:       0 0 auto;
    transition: transform 0.15s;
}
.wy-req-meta__chev--up { transform: rotate(180deg); }
.wy-req-meta__body {
    margin-top:  0.75rem;
    padding-top: 0.75rem;
    border-top:  1px solid var(--wy-border);
}
.wy-req-meta__grid {
    display:               grid;
    grid-template-columns: 1fr 1fr;
    gap:                   0.5rem 1.3rem;
}
.wy-req-meta__k {
    font-size:      0.66rem;
    font-weight:    800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color:          var(--wy-text-hint);
}
.wy-req-meta__v { font-size: 0.9rem; color: var(--wy-text); }
.wy-req-meta__cite {
    grid-column: 1 / -1;
    font-size:   0.82rem;
    color:       var(--wy-text-muted);
    margin-top:  0.4rem;
}
@media (max-width: 560px) {
    .wy-req-meta__grid { grid-template-columns: 1fr; }
}

/* Uniform option rows — Purchase's supplier list, OER's repository list.
   Same visual language as .wy-source-opt but full-width bordered rows
   instead of a popover menu item. */
.wy-opts {
    display:        flex;
    flex-direction: column;
    gap:            0.55rem;
}
.wy-opt-sep {
    height:     1px;
    background: var(--wy-border);
    margin:     1.2rem 0 1rem;
}
.wy-supplier-row {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             1rem;
    width:           100%;
    border:          1px solid var(--wy-border);
    background:      var(--wy-card-bg);
    border-radius:   var(--wy-radius-sm);
    padding:         0.8rem 1rem;
    font:            inherit;
    text-align:      left;
    text-decoration: none;
    color:           inherit;
    cursor:          pointer;
}
.wy-supplier-row:hover {
    border-color: var(--wy-teal-bd);
    background:   #fbfaf6;
}
.wy-supplier-row__name { font-weight: 700; font-size: 0.95rem; color: var(--wy-text); }
.wy-supplier-row__act {
    display:     inline-flex;
    align-items: center;
    gap:         0.4rem;
    font-weight: 700;
    font-size:   0.88rem;
    color:       var(--wy-teal-dark);
    flex:        0 0 auto;
}

/* A supplier row's individual action buttons (Open / Email) — a supplier
   with both a link and an acquisition email needs BOTH reachable, not one
   silently unused, so each is its own real, independently clickable
   <button> rather than the whole-row-is-one-button pattern .wy-supplier-row
   itself still uses elsewhere (OER's repo links, the generic "Email
   library acquisitions" trigger). Same Neve button:hover/:focus/:active
   collision as .wy-req-meta__head (see that rule's comment) — explicit
   overrides needed here too. */
.wy-supplier-row__acts {
    display:  flex;
    gap:      0.5rem;
    flex:     0 0 auto;
    flex-wrap: wrap;
}
.wy-supplier-row__actbtn {
    display:         inline-flex;
    align-items:     center;
    gap:             0.4rem;
    font-family:     inherit;
    font-weight:     700;
    font-size:       0.86rem;
    color:           var(--wy-teal-dark);
    background:      none;
    border:          1px solid var(--wy-teal-bd);
    border-radius:   var(--wy-radius-sm);
    padding:         0.4rem 0.7rem;
    cursor:          pointer;
    outline:         none;
}
.wy-supplier-row__actbtn:hover,
.wy-supplier-row__actbtn:focus,
.wy-supplier-row__actbtn:active {
    background:   var(--wy-teal-light);
    color:        var(--wy-teal-dark);
    border-color: var(--wy-teal-bd);
}
.wy-supplier-row__actbtn:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 2px;
}

/* Open Access tab — advisory notes, spinner, recommendation cards. */
.wy-oa-note, .wy-oa-clarify {
    display:       flex;
    gap:           0.5rem;
    align-items:   flex-start;
    border-radius: var(--wy-radius-sm);
    padding:       0.6rem 0.85rem;
    font-size:     0.83rem;
    line-height:   1.5;
    margin-bottom: 1rem;
}
.wy-oa-note {
    background: #f4f8f6;
    border:     1px solid var(--wy-teal-bd);
    color:      var(--wy-text);
}
.wy-oa-clarify {
    background: var(--wy-amber-bg);
    border:     1px solid var(--wy-amber-bd);
    color:      #7a4e0e;
}
.wy-oa-note svg, .wy-oa-clarify svg { flex: 0 0 auto; margin-top: 0.1rem; }

.wy-oa-searching {
    display:     flex;
    align-items: center;
    gap:         0.55rem;
    padding:     0.5rem 0;
    font-size:   0.88rem;
    color:       var(--wy-text-muted);
}
.wy-oa-spin {
    width:         15px;
    height:        15px;
    border:        2px solid var(--wy-teal-bd);
    border-top-color: var(--wy-teal);
    border-radius: 50%;
    flex:          0 0 auto;
    animation:     wy-oa-spin 0.7s linear infinite;
}
@keyframes wy-oa-spin { to { transform: rotate(360deg); } }

.wy-oarec {
    display:       flex;
    gap:           0.7rem;
    align-items:   flex-start;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    padding:       0.8rem 0.95rem;
    background:    var(--wy-card-bg);
    cursor:        pointer;
}
.wy-oarec + .wy-oarec { margin-top: 0.55rem; }
.wy-oarec:hover { border-color: var(--wy-teal-bd); background: #fbfaf6; }
.wy-oarec--sel { border-color: var(--wy-teal); background: var(--wy-teal-light); }
.wy-oarec input[type="checkbox"] {
    margin-top:    0.15rem;
    width:         16px;
    height:        16px;
    accent-color:  var(--wy-teal);
    flex:          0 0 auto;
    cursor:        pointer;
}
.wy-oarec__body { min-width: 0; flex: 1; }
.wy-oarec__badges { display: flex; gap: 0.35rem; margin-bottom: 0.35rem; flex-wrap: wrap; }
.wy-oarec__title { font-weight: 700; font-size: 0.95rem; color: var(--wy-text); }
.wy-oarec__meta { font-size: 0.8rem; color: var(--wy-text-hint); margin-top: 0.12rem; }
.wy-oarec__why {
    font-size:  0.83rem;
    color:      var(--wy-text-muted);
    margin-top: 0.4rem;
}
.wy-oarec__view {
    display:         inline-flex;
    align-items:     center;
    gap:             0.3rem;
    margin-top:      0.55rem;
    font-size:       0.82rem;
    font-weight:     700;
    color:           var(--wy-teal-dark);
    text-decoration: none;
}
.wy-oarec__view:hover { text-decoration: underline; }

.wy-oa-bar {
    display:     flex;
    align-items: center;
    gap:         0.8rem;
    margin-top:  1rem;
    padding-top: 1rem;
    border-top:  1px solid var(--wy-border);
}
.wy-oa-count { font-size: 0.85rem; color: var(--wy-text-muted); font-weight: 600; }

/* ── Pagination ── */
.wy-pagination {
    display:         flex;
    align-items:     center;
    justify-content: flex-end;
    gap:             0.75rem;
    padding:         0.65rem 0.25rem 0;
    font-size:       0.8rem;
    color:           var(--wy-text-muted);
}

.wy-pagination[hidden] { display: none; }

.wy-pagination-nav {
    display:     flex;
    align-items: center;
    gap:         0.2rem;
}

.wy-pagination-pages {
    padding:    0 0.3rem;
    font-size:  0.8rem;
    color:      var(--wy-text-muted);
}

.wy-page-btn {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           28px;
    height:          28px;
    border:          1px solid #c9e3de;
    border-radius:   6px;
    background:      #f0f9f6;
    color:           var(--wy-teal-dark);
    cursor:          pointer;
    padding:         0;
    transition:      background 0.15s, border-color 0.15s, color 0.15s;
    /* Override any browser / theme blue defaults */
    outline:         none;
    -webkit-appearance: none;
    appearance:      none;
}

.wy-page-btn:hover:not(:disabled) {
    background:   var(--wy-teal-light);
    border-color: var(--wy-teal);
    color:        var(--wy-teal);
}

.wy-page-btn:focus-visible {
    outline: 2px solid var(--wy-teal);
    outline-offset: 1px;
}

.wy-page-btn:disabled {
    opacity: 0.35;
    cursor:  default;
}

/* ── Responsive ── */
@media (max-width: 768px) {
    .wy-guide-inner {
        padding: 1.25rem 1rem;
    }
    .wy-guide-band--title .wy-course-title {
        font-size: 1.45rem;
    }
}

/* ═══════════════════════════════════════════════════════════════
   Edit Research Guide Wizard
   ═══════════════════════════════════════════════════════════════ */

/* ── Wizard page wrapper ── */
.wy-wizard-page {
    padding: 0;
}

/* ── Wizard header bar ── */
.wy-wizard-header {
    background:    var(--wy-card-bg);
    border-bottom: 1px solid var(--wy-border);
    padding:       1.25rem 2rem;
    display:       flex;
    align-items:   center;
    gap:           2.5rem;
    position:      sticky;
    top:           0;
    z-index:       100;
}

.wy-back-btn {
    display:       flex;
    align-items:   center;
    gap:           0.4rem;
    color:         var(--wy-text-muted);
    font-size:     0.875rem;
    font-weight:   500;
    text-decoration: none;
    white-space:   nowrap;
    flex-shrink:   0;
}
.wy-back-btn:hover { color: var(--wy-teal); }
.wy-back-btn svg   { flex-shrink: 0; }

/* ── Step indicator ── */
.wy-wizard-steps {
    display:     flex;
    align-items: center;
    gap:         0;
    flex:        1;
}

.wy-wizard-step {
    display:       flex;
    align-items:   center;
    gap:           0.6rem;
    text-decoration: none;
    padding:       0.15rem 0;
    flex-shrink:   0;
}

.wy-wizard-step-circle {
    width:         32px;
    height:        32px;
    border-radius: 50%;
    border:        2px solid var(--wy-border);
    background:    transparent;
    display:       flex;
    align-items:   center;
    justify-content: center;
    font-size:     0.875rem;
    font-weight:   600;
    color:         var(--wy-text-muted);
    flex-shrink:   0;
    transition:    background 0.2s, border-color 0.2s, color 0.2s;
}

.wy-wizard-step-labels {
    display:        flex;
    flex-direction: column;
    line-height:    1.2;
}

.wy-wizard-step-title {
    font-size:   0.875rem;
    font-weight: 500;
    color:       var(--wy-text-muted);
}

.wy-wizard-step-sub {
    font-size: 0.75rem;
    color:     var(--wy-text-hint);
}

/* Active step */
.wy-wizard-step--active .wy-wizard-step-circle {
    background:   var(--wy-teal);
    border-color: var(--wy-teal);
    color:        #fff;
}
.wy-wizard-step--active .wy-wizard-step-title { color: var(--wy-text); font-weight: 600; }

/* Done step (clickable) */
.wy-wizard-step--done .wy-wizard-step-circle {
    border-color: var(--wy-teal);
    color:        var(--wy-teal);
}
.wy-wizard-step--done .wy-wizard-step-title { color: var(--wy-teal); }
.wy-wizard-step--done { cursor: pointer; }
.wy-wizard-step--done:hover .wy-wizard-step-circle {
    background: var(--wy-teal-light);
}

/* Locked step */
.wy-wizard-step--locked { cursor: default; pointer-events: none; }

/* Connector line between steps */
.wy-wizard-connector {
    flex:            1;
    height:          1px;
    background:      var(--wy-border);
    margin:          0 0.75rem;
    min-width:       1.5rem;
    max-width:       4rem;
}

/* ── Wizard body (form card) ── */
.wy-wizard-body {
    max-width:  860px;
    margin:     2.5rem auto;
    padding:    0 1.5rem 3rem;
}

/* ── Wizard field groups ── */
.wy-wizard-field {
    margin-bottom: 2rem;
}

.wy-wizard-field > label,
.wy-wizard-field-label {
    display:     block;
    font-size:   1rem;
    font-weight: 600;
    color:       var(--wy-text);
    margin-bottom: 0.35rem;
}

.wy-wizard-field .wy-field-hint {
    font-size:    0.825rem;
    color:        var(--wy-teal);
    margin:       0 0 0.65rem;
    line-height:  1.4;
}

.wy-wizard-field input[type="text"] {
    width:        100%;
    padding:      0.65rem 0.9rem;
    font-size:    0.95rem;
    font-family:  var(--wy-font);
    border:       1.5px solid var(--wy-border-control);
    border-radius: var(--wy-radius-sm);
    background:   var(--wy-card-bg);
    color:        var(--wy-text);
    outline:      none;
    box-sizing:   border-box;
    transition:   border-color 0.15s;
}
.wy-wizard-field input[type="text"]:focus {
    border-color: var(--wy-teal);
    box-shadow:   0 0 0 3px rgba(58,125,110,0.12);
}
.wy-wizard-field input[type="text"].wy-input-error {
    border-color: #ef4444;
}

.wy-wizard-field .wy-error-msg {
    font-size:   0.8rem;
    color:       #ef4444;
    margin-top:  0.3rem;
    display:     none;
}
.wy-wizard-field .wy-error-msg.wy-visible { display: block; }

/* ── Quill editor inside wizard ── */
.wy-wizard-field .ql-toolbar.ql-snow {
    border:        1.5px solid var(--wy-border);
    border-bottom: none;
    border-radius: var(--wy-radius-sm) var(--wy-radius-sm) 0 0;
    font-family:   var(--wy-font);
    background:    #fafafa;
}
.wy-wizard-field .ql-container.ql-snow {
    border:        1.5px solid var(--wy-border);
    border-radius: 0 0 var(--wy-radius-sm) var(--wy-radius-sm);
    font-family:   var(--wy-font);
    font-size:     0.9rem;
    color:         var(--wy-text);
}
.wy-wizard-field .ql-editor {
    min-height: 220px;
    line-height: 1.65;
}
.wy-wizard-field .ql-editor.ql-blank::before {
    color: var(--wy-text-hint);
    font-style: normal;
}
.wy-wizard-field .ql-container.ql-snow:focus-within {
    border-color: var(--wy-teal);
    box-shadow:   0 0 0 3px rgba(58,125,110,0.12);
}
.wy-wizard-field .ql-snow .ql-stroke { stroke: var(--wy-text-muted); }
.wy-wizard-field .ql-snow .ql-fill   { fill:   var(--wy-text-muted); }
.wy-wizard-field .ql-snow.ql-toolbar button:hover .ql-stroke,
.wy-wizard-field .ql-snow.ql-toolbar button.ql-active .ql-stroke { stroke: var(--wy-teal); }
.wy-wizard-field .ql-snow.ql-toolbar button:hover .ql-fill,
.wy-wizard-field .ql-snow.ql-toolbar button.ql-active .ql-fill   { fill:   var(--wy-teal); }

/* ── Wizard action row ── */
.wy-wizard-actions {
    display:         flex;
    justify-content: flex-end;
    margin-top:      2rem;
    padding-top:     1.5rem;
    border-top:      1px solid var(--wy-border);
}

/* ── Add Document stub button ── */
.wy-btn--outline {
    background:    transparent;
    border:        1.5px solid var(--wy-border);
    color:         var(--wy-text-muted);
    padding:       0.55rem 1.1rem;
    border-radius: var(--wy-radius-sm);
    font-size:     0.875rem;
    font-family:   var(--wy-font);
    cursor:        default;
    display:       inline-flex;
    align-items:   center;
    gap:           0.4rem;
    opacity:       0.7;
}

/* ── Analysis / loading state ── */
.wy-analysis-state {
    display:         flex;
    flex-direction:  column;
    align-items:     center;
    justify-content: center;
    min-height:      60vh;
    text-align:      center;
    gap:             0.75rem;
}

.wy-analysis-state h2 {
    font-size:   1.1rem;
    font-weight: 600;
    color:       var(--wy-teal-dark);
    margin:      0.25rem 0 0;
}

.wy-analysis-state p {
    font-size: 0.9rem;
    color:     var(--wy-teal);
    margin:    0;
}

.wy-analysis-state .wy-error-banner {
    color:       #ef4444;
    font-size:   0.9rem;
    margin-top:  0.5rem;
}

/* ── Sparkle animation (3 diamonds) ── */
.wy-sparkle {
    display:  flex;
    position: relative;
    width:    52px;
    height:   44px;
}

.wy-sparkle-diamond {
    position:   absolute;
    width:       14px;
    height:      14px;
    background:  var(--wy-teal-dark);
    transform:   rotate(45deg);
    border-radius: 2px;
    animation:  wy-sparkle-pulse 1.4s ease-in-out infinite;
}
.wy-sparkle-diamond:nth-child(1) { top: 0;   left: 10px; animation-delay: 0s;     width: 10px; height: 10px; }
.wy-sparkle-diamond:nth-child(2) { top: 15px; left: 0;   animation-delay: 0.2s; }
.wy-sparkle-diamond:nth-child(3) { top: 15px; left: 20px; animation-delay: 0.4s; width: 10px; height: 10px; }

@keyframes wy-sparkle-pulse {
    0%, 100% { opacity: 0.35; transform: rotate(45deg) scale(0.8); }
    50%       { opacity: 1;    transform: rotate(45deg) scale(1);   }
}

/* ── Wizard error banner (top of form) ── */
.wy-wizard-error-banner {
    background:    #fef2f2;
    border:        1px solid #fecaca;
    color:         #b91c1c;
    border-radius: var(--wy-radius-sm);
    padding:       0.75rem 1rem;
    font-size:     0.875rem;
    margin-bottom: 1.25rem;
    display:       none;
}
.wy-wizard-error-banner.wy-visible { display: block; }

/* ── Wizard responsive ── */
@media (max-width: 768px) {
    .wy-wizard-header {
        padding:    1rem;
        gap:        1rem;
        flex-wrap:  wrap;
    }
    .wy-wizard-connector { display: none; }
    .wy-wizard-step-sub  { display: none; }
    .wy-wizard-body      { padding: 0 1rem 2rem; margin-top: 1.5rem; }
}

@media (max-width: 480px) {
    .wy-wizard-step-labels { display: none; }
    .wy-wizard-steps       { gap: 0.5rem; }
}

/* ═══════════════════════════════════════════════════════════════
   Curate Resources — Step 2
   ═══════════════════════════════════════════════════════════════ */

/* ── Step indicator: done state (checkmark) ── */
.wy-wizard-step--done .wy-wizard-step-circle {
    background:   var(--wy-teal);
    border-color: var(--wy-teal);
    color:        #fff;
}

/* ── Curate page body ── */
.wy-curate-body {
    max-width:  1100px;
    margin:     2rem auto;
    padding:    0 1.5rem 3rem;
}

.wy-curate-heading {
    font-size:   1.1rem;
    font-weight: 700;
    color:       var(--wy-text);
    margin:      0 0 1.25rem;
}

/* ── Tab bar ── */
.wy-curate-tabs {
    display:       flex;
    gap:           0;
    border-bottom: 2px solid var(--wy-border);
    margin-bottom: 1.25rem;
}

.wy-curate-tab {
    display:         flex;
    align-items:     center;
    gap:             0.45rem;
    padding:         0.6rem 1.1rem;
    font-size:       0.875rem;
    font-weight:     500;
    color:           var(--wy-text-muted);
    cursor:          pointer;
    border-bottom:   3px solid transparent;
    margin-bottom:   -2px;
    background:      none;
    border-top:      none;
    border-left:     none;
    border-right:    none;
    font-family:     var(--wy-font);
    transition:      color 0.15s, border-color 0.15s;
    white-space:     nowrap;
}

.wy-curate-tab:hover { color: var(--wy-text); }

.wy-curate-tab--active {
    color:        var(--wy-teal);
    border-bottom-color: var(--wy-teal);
    font-weight:  600;
}

.wy-curate-tab-icon {
    opacity: 0.6;
}
.wy-curate-tab--active .wy-curate-tab-icon { opacity: 1; }

.wy-curate-tab-count {
    display:       inline-flex;
    align-items:   center;
    justify-content: center;
    min-width:     22px;
    height:        18px;
    padding:       0 5px;
    border-radius: 9px;
    background:    var(--wy-teal-light);
    color:         var(--wy-teal-dark);
    font-size:     0.72rem;
    font-weight:   600;
}

/* ── Tab panels ── */
.wy-curate-panel { display: none; }
.wy-curate-panel--active { display: block; }

/* ── Resource table (reuses existing .wy-guide-table styles, adds row-level actions) ── */
.wy-curate-table-wrap { overflow-x: auto; }

.wy-curate-table {
    width:           100%;
    border-collapse: collapse;
    font-size:       0.875rem;
}

.wy-curate-table thead th {
    text-align:    left;
    padding:       0.6rem 0.75rem;
    font-size:     0.8rem;
    font-weight:   600;
    color:         var(--wy-text-muted);
    border-bottom: 1.5px solid var(--wy-border);
    white-space:   nowrap;
}

.wy-curate-table tbody td {
    padding:       0.65rem 0.75rem;
    border-bottom: 1px solid var(--wy-border);
    vertical-align: middle;
    color:         var(--wy-text);
}

.wy-curate-table tbody tr:last-child td { border-bottom: none; }

.wy-curate-table tbody tr:hover td { background: #fafafa; }

.wy-curate-table .wy-col-title { width: 30%; }
.wy-curate-table .wy-col-actions {
    width:      80px;
    text-align: right;
    white-space: nowrap;
}

.wy-curate-row-being-deleted { opacity: 0.4; pointer-events: none; }

/* Action icon buttons (delete / view / add) */
.wy-row-action-btn {
    display:       inline-flex;
    align-items:   center;
    justify-content: center;
    width:         28px;
    height:        28px;
    border-radius: 6px;
    border:        none;
    background:    transparent;
    cursor:        pointer;
    padding:       0;
    transition:    background 0.15s;
}
.wy-row-action-btn--delete { color: #ef4444; }
.wy-row-action-btn--delete:hover { background: #fef2f2; }
.wy-row-action-btn--view   { color: var(--wy-teal); }
.wy-row-action-btn--view:hover { background: var(--wy-teal-light); }
.wy-row-action-btn--add    { color: var(--wy-teal); }
.wy-row-action-btn--add:hover { background: var(--wy-teal-light); }

/* Pagination (shared with guide page, re-declared for curate context) */
.wy-curate-pagination {
    display:         flex;
    align-items:     center;
    justify-content: flex-end;
    gap:             0.5rem;
    padding:         0.6rem 0.75rem;
    font-size:       0.8rem;
    color:           var(--wy-text-muted);
    border-top:      1px solid var(--wy-border);
}

/* ── Add More Resources panel ── */
.wy-search-panel {
    background:    var(--wy-card-bg);
    border:        1.5px solid var(--wy-border);
    border-radius: var(--wy-radius);
    padding:       1.25rem 1.5rem;
    margin-top:    1.5rem;
}

.wy-search-panel-title {
    display:     flex;
    align-items: center;
    gap:         0.5rem;
    font-size:   0.95rem;
    font-weight: 600;
    color:       var(--wy-text);
    margin:      0 0 0.2rem;
}

.wy-search-panel-hint {
    font-size:   0.8rem;
    color:       var(--wy-teal);
    margin:      0 0 1rem;
}

.wy-search-row {
    display: flex;
    gap:     0.75rem;
}

.wy-search-input {
    flex:        1;
    padding:     0.6rem 0.9rem;
    font-size:   0.9rem;
    font-family: var(--wy-font);
    border:      1.5px solid var(--wy-border-control);
    border-radius: var(--wy-radius-sm);
    color:       var(--wy-text);
    outline:     none;
    transition:  border-color 0.15s;
}
.wy-search-input:focus {
    border-color: var(--wy-teal);
    box-shadow:   0 0 0 3px rgba(58,125,110,0.12);
}

.wy-search-btn {
    padding:       0.6rem 1.25rem;
    background:    var(--wy-card-bg);
    border:        1.5px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    font-size:     0.875rem;
    font-weight:   500;
    font-family:   var(--wy-font);
    color:         var(--wy-text);
    cursor:        pointer;
    transition:    border-color 0.15s, color 0.15s;
    white-space:   nowrap;
}
.wy-search-btn:hover {
    border-color: var(--wy-teal);
    color:        var(--wy-teal);
}
.wy-search-btn:disabled {
    opacity: 0.5;
    cursor:  default;
}

/* Search results area */
.wy-search-results {
    margin-top: 1.25rem;
    display:    none;
}
.wy-search-results--visible { display: block; }

.wy-search-results-heading {
    font-size:   0.8rem;
    font-weight: 600;
    color:       var(--wy-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin:      0 0 0.75rem;
}

.wy-search-no-results {
    font-size:  0.875rem;
    color:      var(--wy-text-hint);
    padding:    1rem 0;
    text-align: center;
}

/* ── Bottom actions ── */
.wy-curate-actions {
    display:         flex;
    justify-content: flex-start;
    margin-top:      2rem;
    padding-top:     1.5rem;
    border-top:      1px solid var(--wy-border);
}

/* ── Curate loading overlay ── */
.wy-curate-loading {
    display:         flex;
    flex-direction:  column;
    align-items:     center;
    justify-content: center;
    min-height:      60vh;
    text-align:      center;
    gap:             0.75rem;
}
.wy-curate-loading h2 {
    font-size:   1.1rem;
    font-weight: 600;
    color:       var(--wy-teal-dark);
    margin:      0.25rem 0 0;
}
.wy-curate-loading p {
    font-size: 0.9rem;
    color:     var(--wy-teal);
    margin:    0;
}

/* ── Responsive ── */
@media (max-width: 768px) {
    .wy-curate-body  { padding: 0 1rem 2rem; margin-top: 1rem; }
    .wy-curate-tab   { padding: 0.5rem 0.75rem; font-size: 0.8rem; }
    .wy-search-row   { flex-direction: column; }
}

/* ═══════════════════════════════════════════════════════════════
   Review and Edit — Step 3
   ═══════════════════════════════════════════════════════════════ */

/* ── Fulfillment lifecycle bar — ported from the MVP's .lifebar
   (flow_55.html `fulfillment` screen), docs/mvp-flow55-alignment-spec.md §5 */
.wy-lifebar {
    display:         flex;
    flex-wrap:       wrap;
    align-items:     center;
    gap:             0.4rem;
    font-size:       0.76rem;
    color:           var(--wy-text-hint);
    background:      var(--wy-card-bg);
    border:          1px solid var(--wy-border);
    border-radius:   var(--wy-radius-sm);
    padding:         0.5rem 0.8rem;
    margin:          0.2rem 0 1.2rem;
}
.wy-lifebar b {
    color:       var(--wy-text-muted);
    font-weight: 700;
}
.wy-lifebar__sep {
    color: var(--wy-border);
}

/* ── Fulfillment: View request disclosure ── */
/* Sits under the title and spans the whole row, rather than inside the
   actions column. .wy-gaprow is `grid-template-columns: auto 1fr auto`, so the
   opened email used to render into a ~15rem track: the subject broke mid-word
   and the body ran outside the card. Spanning 1 / -1 gives it its own
   full-width line, which is how the Review screen's row disclosures behave. */
.wy-gaprow > .wy-view-request {
    grid-column: 1 / -1;
    display:     block;
    margin-top:  0.15rem;
}

.wy-view-request {
    display: inline-block;
}
.wy-view-request summary {
    cursor: pointer;
    list-style: none;
}

/* Matches the Review row disclosure: chevron, then the label. */
.wy-view-request__summary {
    display:     inline-flex;
    align-items: center;
    gap:         0.4rem;
    font-size:   0.85rem;
    color:       var(--wy-teal-dark);
    padding:     0.15rem 0;
}
.wy-view-request__summary:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 2px;
    border-radius:  4px;
}
.wy-view-request__summary .wy-gap-chev {
    display: inline-flex;
    width:   14px;
    height:  14px;
}
.wy-view-request__summary .wy-gap-chev svg { width: 14px; height: 14px; }
.wy-view-request summary::-webkit-details-marker {
    display: none;
}
.wy-view-request__body {
    margin-top:    0.6rem;
    padding:       0.8rem 0.9rem;
    background:    var(--wy-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    font-size:     0.82rem;
    color:         var(--wy-text-muted);
    text-align:    left;
    /* No max-width: the point of moving this out of the actions column was to
       give it the row's full width. A long URL in an ILL body still has to
       break rather than push the card sideways. */
    overflow-wrap: anywhere;
}
.wy-view-request__body > div {
    margin-bottom: 0.4rem;
}
.wy-view-request__msg {
    white-space: pre-wrap;
    color:       var(--wy-text);
}
.wy-view-request__sent {
    color:       var(--wy-text-hint);
    font-size:   0.76rem;
    margin-top:  0.5rem;
}

/* ── Review page body ── */
.wy-review-body {
    max-width:  960px;
    margin:     2rem auto;
    padding:    0 1.5rem 4rem;
}

/* ── Block card ── */
.wy-block-card {
    background:    var(--wy-card-bg);
    border:        1.5px solid var(--wy-border);
    border-radius: var(--wy-radius);
    margin-bottom: 1rem;
    overflow:      hidden;
    transition:    opacity 0.2s, border-color 0.2s;
}

.wy-block-card--hidden {
    opacity:       0.45;
    border-style:  dashed;
}

.wy-block-card--dragging {
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    border-color: var(--wy-teal);
    z-index: 50;
}

/* ── Block header bar ── */
.wy-block-header {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    padding:         0.55rem 0.85rem 0.55rem 0.6rem;
    background:      #f7f8f8;
    border-bottom:   1px solid var(--wy-border);
    gap:             0.5rem;
    min-height:      38px;
}

.wy-block-header-left {
    display:     flex;
    align-items: center;
    gap:         0.5rem;
    min-width:   0;
}

.wy-block-handle {
    color:      var(--wy-text-hint);
    cursor:     grab;
    flex-shrink: 0;
    display:    flex;
    align-items: center;
    padding:    2px;
    border-radius: 4px;
    transition: color 0.15s, background 0.15s;
}
.wy-block-handle:hover { color: var(--wy-text-muted); background: var(--wy-border); }
.wy-block-card--title .wy-block-handle { cursor: default; opacity: 0.3; pointer-events: none; }

.wy-block-label {
    font-size:   0.8rem;
    font-weight: 500;
    color:       var(--wy-text-muted);
    white-space: nowrap;
    overflow:    hidden;
    text-overflow: ellipsis;
}
.wy-block-card--hidden .wy-block-label { text-decoration: line-through; }

.wy-block-header-right {
    display:     flex;
    align-items: center;
    gap:         0.1rem;
    flex-shrink: 0;
}

.wy-save-indicator {
    font-size:   0.72rem;
    color:       var(--wy-text-hint);
    margin-right: 0.4rem;
    min-width:   36px;
    text-align:  right;
}
.wy-save-indicator--saving { color: var(--wy-text-hint); }
.wy-save-indicator--saved  { color: var(--wy-teal); }
.wy-save-indicator--error  { color: #ef4444; }

/* ── Block action icon buttons ── */
.wy-block-action-btn {
    display:       inline-flex;
    align-items:   center;
    justify-content: center;
    width:         28px;
    height:        28px;
    border-radius: 6px;
    border:        none;
    background:    transparent;
    cursor:        pointer;
    padding:       0;
    color:         var(--wy-text-hint);
    transition:    color 0.15s, background 0.15s;
}
.wy-block-action-btn:hover             { background: var(--wy-teal-light); color: var(--wy-teal); }
.wy-block-action-btn--delete:hover     { background: #fef2f2; color: #ef4444; }
.wy-block-action-btn--delete           { color: #f87171; }
.wy-block-action-btn--hidden-active    { color: var(--wy-teal); }

/* ── Block content area ── */
.wy-block-content {
    padding: 0;
}

/* Title block content */
.wy-review-title {
    font-size:   1.6rem;
    font-weight: 700;
    color:       var(--wy-text);
    margin:      0;
    padding:     1rem 1.25rem;
    line-height: 1.3;
}

/* ── Quill editor in review blocks ── */
.wy-block-editor-wrap .ql-toolbar.ql-snow {
    border:      none;
    border-bottom: 1px solid var(--wy-border);
    background:  #fcfcfc;
    font-family: var(--wy-font);
    padding:     6px 10px;
}
.wy-block-editor-wrap .ql-container.ql-snow {
    border:      none;
    font-family: var(--wy-font);
    font-size:   0.9rem;
    color:       var(--wy-text);
}
.wy-block-editor-wrap .ql-editor {
    min-height:  80px;
    padding:     0.9rem 1.1rem;
    line-height: 1.7;
}
.wy-block-editor-wrap .ql-editor p   { margin-bottom: 0.5em; }
.wy-block-editor-wrap .ql-editor h2  { font-size: 1.25rem; font-weight: 700; margin: 0.75em 0 0.35em; color: var(--wy-text); }
.wy-block-editor-wrap .ql-editor h3  { font-size: 1.05rem; font-weight: 600; margin: 0.6em 0 0.3em; color: var(--wy-text); }
.wy-block-editor-wrap .ql-editor ul,
.wy-block-editor-wrap .ql-editor ol  { padding-left: 1.5rem; margin-bottom: 0.5em; }
.wy-block-editor-wrap .ql-snow .ql-stroke { stroke: var(--wy-text-muted); }
.wy-block-editor-wrap .ql-snow .ql-fill   { fill:   var(--wy-text-muted); }
.wy-block-editor-wrap .ql-snow.ql-toolbar button:hover .ql-stroke,
.wy-block-editor-wrap .ql-snow.ql-toolbar button.ql-active .ql-stroke { stroke: var(--wy-teal); }
.wy-block-editor-wrap .ql-snow.ql-toolbar button:hover .ql-fill,
.wy-block-editor-wrap .ql-snow.ql-toolbar button.ql-active .ql-fill   { fill:   var(--wy-teal); }

/* ── Resource table in review blocks (no toolbar, row delete) ── */
.wy-block-resource-table {
    width:           100%;
    border-collapse: collapse;
    font-size:       0.875rem;
}
.wy-block-resource-table thead th {
    text-align:    left;
    padding:       0.55rem 0.75rem;
    font-size:     0.78rem;
    font-weight:   600;
    color:         var(--wy-text-muted);
    border-bottom: 1.5px solid var(--wy-border);
    white-space:   nowrap;
}
.wy-block-resource-table tbody td {
    padding:       0.6rem 0.75rem;
    border-bottom: 1px solid var(--wy-border);
    vertical-align: middle;
}
.wy-block-resource-table tbody tr:last-child td { border-bottom: none; }
.wy-block-resource-table tbody tr:hover td      { background: #fafafa; }
.wy-block-resource-table .wy-col-actions        { width: 70px; text-align: right; }

/* Pagination row inside review blocks */
.wy-block-resource-pagination {
    display:         flex;
    align-items:     center;
    justify-content: flex-end;
    gap:             0.4rem;
    padding:         0.5rem 0.75rem;
    font-size:       0.78rem;
    color:           var(--wy-text-muted);
    border-top:      1px solid var(--wy-border);
}

/* ── Add-block drop zone (visible between cards on hover) ── */
.wy-add-block-row {
    display:         flex;
    justify-content: center;
    padding:         0.25rem 0;
    opacity:         0;
    transition:      opacity 0.2s;
}
.wy-add-block-row:hover,
.wy-block-card:hover + .wy-add-block-row { opacity: 1; }

/* ── Bottom actions ── */
.wy-review-actions {
    display:         flex;
    justify-content: flex-start;
    margin-top:      2rem;
    padding-top:     1.5rem;
    border-top:      1px solid var(--wy-border);
    gap:             1rem;
}

/* ── Responsive ── */
@media (max-width: 768px) {
    .wy-review-body { padding: 0 1rem 3rem; margin-top: 1rem; }
    .wy-review-title { font-size: 1.3rem; }
}

/* ─── Library search + ILL (default guide layout) ─────────────────────────
   Entry link in the public guide's title band, and the resolver-provided
   "Request via Interlibrary Loan" action in resource tables. */
.wy-lib-search-entry { margin: .6rem 0 0; }
.wy-lib-search-link {
  font-size: .875rem;
  font-weight: 500;
  text-decoration: none;
}
.wy-table-ill-link {
  display: inline-block;
  white-space: nowrap;
  font-size: .8125rem;
  margin-left: .5rem;
}

/* ─── Pending page: spinner + generation progress ─────────────────────────
   The spinner arc rotates (it previously had no animation); the progress bar
   fills per pipeline step reported by get-course-status (progress payload). */
.wy-spinner {
    display:         flex;
    align-items:     center;
    justify-content: center;
    margin:          0 auto 1.5rem;
    color:           var(--wy-teal-icon);
}
.wy-spin { animation: wy-rotate 1s linear infinite; }
@keyframes wy-rotate { to { transform: rotate(360deg); } }

.wy-progress { max-width: 340px; margin: 1.25rem auto 0.75rem; }
.wy-progress[hidden] { display: none; }
.wy-progress__track {
    height:        8px;
    border-radius: 999px;
    background:    var(--wy-teal-light);
    overflow:      hidden;
}
.wy-progress__bar {
    height:        100%;
    width:         0%;
    border-radius: 999px;
    background:    var(--wy-teal);
    transition:    width 0.6s ease;
}
.wy-progress__label {
    display:         flex;
    justify-content: space-between;
    gap:             1rem;
    margin:          0.5rem 0 0;
    font-size:       0.8rem;
    color:           var(--wy-text-muted);
}
.wy-progress__pct { font-weight: 600; color: var(--wy-teal-dark); }

@media (prefers-reduced-motion: reduce) {
    .wy-spin { animation: none; }
    .wy-progress__bar { transition: none; }
}

/* ─────────────────────────────────────────────
   Guide Ready Confirmation Screen (pending → ready)
   ───────────────────────────────────────────── */

.wy-card--confirm {
    text-align: center;
}

/* Eyebrow badge: "Guide ready" */
.wy-confirm-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #0c5f4a;
    background: #e1f5ee;
    border-radius: 20px;
    padding: 6px 14px;
    margin-bottom: 1.25rem;
}

.wy-confirm-eyebrow svg {
    width: 14px;
    height: 14px;
}

/* Title */
.wy-confirm-title {
    font-size: 1.6rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.25;
    margin: 0 0 1.75rem;
    color: var(--wy-text);
}

/* Resource Summary stats */
.wy-confirm-summary {
    margin: 0 auto 2rem;
    max-width: 420px;
}

.wy-confirm-summary--populated {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.6rem 1.1rem;
}

@media (min-width: 480px) {
    .wy-confirm-summary--populated {
        grid-template-columns: 1fr 1fr;
    }
}

.wy-summary-stat {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.35rem 0;
    text-align: left;
}

.wy-summary-stat__mark {
    flex: 0 0 26px;
    width: 26px;
    height: 26px;
    border-radius: 7px;
    background: #e1f5ee;
    color: #0c5f4a;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.wy-summary-stat__mark svg {
    width: 16px;
    height: 16px;
}

.wy-summary-stat__num {
    font-size: 1.35rem;
    font-weight: 600;
    line-height: 1;
    color: var(--wy-text);
    min-width: 1.5ch;
}

.wy-summary-stat__lbl {
    font-size: 0.9rem;
    color: var(--wy-text-muted);
    line-height: 1.2;
}

.wy-confirm-summary--empty {
    margin: 0 auto 2rem;
}

.wy-summary-empty {
    font-size: 1rem;
    color: var(--wy-text-muted);
    margin: 1rem 0;
}

/* Fade-in animation for the confirmation card */
.wy-confirm-reveal {
    animation: wy-fade-in 0.5s ease forwards;
}

@keyframes wy-fade-in {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .wy-confirm-reveal {
        animation: none;
    }
    .wy-summary-stat__num {
        transition: none;
    }
}

/* ── Low-confidence hint (Review screen) — the old 'uncertain' tag folded
   into 'recommended', with extraction_confidence='low' as the surviving
   signal. Quiet inline text, not a badge, so it doesn't read as a 4th
   category. Reuses the retired .wy-tag--uncertain amber tone. ── */
.wy-hint {
    font-size: 0.76rem;
}

.wy-hint--low-confidence {
    color: #9a3412;
}

/* "Different edition than assigned" — same-work fallback match (staged
   search in check-reading-availability / check-worldcat-reading-availability),
   not an exact ISBN/DOI hit. Informational, not a warning — the item IS
   available, this just tells the reader (staff or student) not to expect
   an exact page/edition match. Reuses the muted-text token rather than a
   new color, since it's neutral information, not a caution. */
.wy-hint--edition-diff {
    color: var(--wy-text-muted);
}

/* ── Fulfillment badges (Course Collection view) — same palette as Review's
   ILL/purchase buttons + .wy-tag--requested green, plus a new blue for open
   access. Staff-only; students see .wy-gopen links instead. ── */
.wy-badge {
    display:       inline-flex;
    align-items:   center;
    gap:           0.3rem;
    border-radius: 20px;
    padding:       0.2rem 0.6rem;
    font-size:     0.74rem;
    font-weight:   600;
    white-space:   nowrap;
}

.wy-badge--in-collection { background: #e6f2ec; color: #1a6249; }
.wy-badge--open-access   { background: #e8f1fb; color: #1668b8; }
.wy-badge--needs-ill     { background: #eeecfb; color: #5a4fcf; }
.wy-badge--needs-purchase{ background: #fbf0dc; color: var(--wy-amber); }
/* Not a gap colour. "Unconfirmed" is a question waiting for a librarian, not
   an acquisition to fund, and it must not read as either of the two badges
   above it. Slate against the neutral ground; contrast 7.4:1, and the glyph is
   a question mark so tint is never the only cue (WCAG 1.4.1). */
.wy-badge--unconfirmed   { background: #eceef1; color: #41505f; }

/* "Open →" link for a confirmed in-collection/open-access reading, and its
   muted "not yet" counterpart for genuine gaps — everyone sees one of
   these, only staff also see the .wy-badge above it. */
.wy-gopen {
    color:           var(--wy-teal);
    font-weight:     700;
    font-size:       0.82rem;
    text-decoration: none;
    white-space:     nowrap;
}

.wy-gopen:hover {
    text-decoration: underline;
}

.wy-gopen--pending {
    color:       var(--wy-text-hint);
    font-weight: 500;
}

/* Course Collection reading rows: title/byline on the left, tag + badge/link
   stacked on the right (wraps under narrow widths, matching .wy-exitem's
   existing responsive behavior). */
.wy-exitem--collection {
    align-items: flex-start;
}

.wy-exitem__status {
    display:        flex;
    flex-direction: column;
    align-items:    flex-end;
    gap:            0.35rem;
    flex-shrink:    0;
}

/* Preview screen only (rows always sit inside one of these two wrapper
   modifiers there) — .wy-exitem__status now holds just the single
   icon-button "Open" action (the pill moved up into .wy-exitem__title-row),
   so it centers against the row instead of stacking to the top-right the
   way Materials'/Collection Health's badge+link pair still needs to. Scoped
   this way instead of changing .wy-exitem--collection/.wy-exitem__status
   directly, since those two are shared with part-app-materials.php and
   part-app-collection-health.php's own (different) row layouts. */
.wy-exgroup--section .wy-exitem--collection,
.wy-exgroup--sub .wy-exitem--collection {
    align-items: center;
}
.wy-exgroup--section .wy-exitem__status,
.wy-exgroup--sub .wy-exitem__status {
    flex-direction: row;
    align-items:    center;
}

/* ── Materials screen: status-grouped sections + "Add from collection" ──
   Ported from the MVP's Materials screen (flow_55.html, `materials`) —
   .acc-rec's amber accent on the recommendations group, and a lightweight
   inline search+add panel for "Add from collection" (search-guide-
   resources + add-guide-resource; see part-app-materials.php's header
   comment for why "Add manually" isn't here — no backing edge function). */
.wy-exgroup--rec .wy-exgroup__head {
    background: var(--wy-amber-bg);
}

.wy-exgroup__count--amber {
    color:       var(--wy-amber);
    font-weight: 700;
}

.wy-addres-panel {
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       1rem 1.1rem;
    margin-bottom: 1.2rem;
}

.wy-addres-panel__head {
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             0.6rem;
    font-weight:     700;
    font-size:       0.92rem;
    color:           var(--wy-text);
    margin-bottom:   0.7rem;
}

.wy-addres-panel .wy-tx {
    width: 100%;
}

.wy-addres-results {
    margin-top: 0.7rem;
    max-height: 320px;
    overflow-y: auto;
}

.wy-addres-hint {
    font-size: 0.85rem;
    color:     var(--wy-text-hint);
    margin:    0.4rem 0 0;
}

.wy-addres-row {
    display:       flex;
    align-items:   center;
    gap:           0.8rem;
    padding:       0.6rem 0.2rem;
    border-top:    1px solid var(--wy-border);
}

.wy-addres-row:first-child {
    border-top: none;
}

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

.wy-collection-blocks-heading {
    font-size:     1.05rem;
    font-weight:   700;
    margin:        1.6rem 0 0.8rem;
    color:         var(--wy-text);
}

/* ── Content blocks (Course Collection view) — librarian-added blocks,
   "recommended by the library" (.wy-block--recommended-resource) plus 5
   general content types, ported from the MVP mockup's editor screen. ── */
.wy-block {
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       1rem 1.2rem;
    margin-bottom: 0.7rem;
    font-size:     0.9rem;
    color:         var(--wy-text-muted);
    text-align:    left;
}

.wy-block--note {
    display:     flex;
    gap:         0.6rem;
    align-items: flex-start;
    border-left: 3px solid var(--wy-teal);
}

.wy-block__icon {
    flex-shrink: 0;
    color:       var(--wy-teal);
    margin-top:  0.1rem;
}

.wy-block--recommended-resource,
.wy-block--profile {
    display:     flex;
    gap:         0.9rem;
    align-items: flex-start;
}

.wy-block__avatar {
    width:           48px;
    height:          48px;
    border-radius:   50%;
    background:      var(--wy-teal-light);
    color:           var(--wy-teal);
    display:         flex;
    align-items:     center;
    justify-content: center;
    font-weight:     800;
    font-size:       0.95rem;
    flex-shrink:     0;
    overflow:        hidden;
}

.wy-block__avatar img {
    width:      100%;
    height:     100%;
    object-fit: cover;
}

.wy-block__name {
    font-weight: 700;
    font-size:   0.95rem;
    color:       var(--wy-text);
}

.wy-block__role {
    font-size:     0.8rem;
    color:         var(--wy-text-hint);
    margin:        0.1rem 0 0.4rem;
}

.wy-block__desc,
.wy-block__hours {
    font-size: 0.85rem;
}

.wy-block__hours a {
    color:       var(--wy-teal);
    font-weight: 600;
}

.wy-block__linklist {
    list-style: none;
    margin:     0.4rem 0 0;
    padding:    0;
}

.wy-block__linklist li {
    padding: 0.2rem 0;
}

.wy-block__linklist a {
    color:           var(--wy-teal);
    font-weight:     600;
    text-decoration: none;
}

.wy-block__linklist a:hover {
    text-decoration: underline;
}

.wy-block--text {
    line-height: 1.6;
}

.wy-block--embed a {
    display:         inline-flex;
    align-items:     center;
    gap:             0.4rem;
    color:           var(--wy-teal);
    font-weight:     600;
    text-decoration: none;
}

.wy-block--embed a:hover {
    text-decoration: underline;
}

.wy-block--search form {
    display:     flex;
    align-items: center;
    gap:         0.6rem;
}
.wy-block--search .wy-tx {
    flex: 1;
}
.wy-block--search-disabled {
    display:     flex;
    align-items: center;
    gap:         0.6rem;
    color:       var(--wy-text-hint);
    font-style:  italic;
}
.wy-block--search-disabled .wy-block__icon {
    color: var(--wy-text-hint);
}

/* ── Course Collection editor (part-syllabus-collection-edit.php) ── */
.wy-editbar {
    display:       flex;
    gap:           0.5rem;
    align-items:   center;
    background:    var(--wy-teal-light);
    border:        1px solid var(--wy-teal);
    border-radius: var(--wy-radius-sm);
    padding:       0.6rem 0.8rem;
    margin-bottom: 1.2rem;
    font-size:     0.85rem;
    color:         var(--wy-teal-dark);
}

/* ── Customize screen page composer ──────────────────────────────────────
   Replaces the old flat .wy-editblock list (removed — no longer used by
   part-app-editor.php after the page-composer rewrite). Reuses established
   components rather than porting the mock's own parallel CSS: .wy-icon-btn
   for icon buttons, .wy-source-wrap/.wy-source-toggle/.wy-source-popover
   for both the per-block overflow menu and the Add Content dropdown (same
   anchored-popover component already fixed this session for viewport
   clipping), .wy-dash-menuitem (assets/app-shell.css) for popover menu
   rows, .wy-exgroup for resource_section/section student-facing rendering.
   Named .wy-cblock (not .wy-block) deliberately — .wy-block is already the
   STUDENT-facing content-block wrapper class a few hundred lines up
   (wy_render_collection_block()); reusing that name here would silently
   collide since both live in this same stylesheet. */

.wy-editor-bar { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; flex-wrap: wrap; }
.wy-editor-bar h2 { font-size: 1.05rem; margin: 0; letter-spacing: -0.01em; }
.wy-editor-sub { font-size: 0.78rem; color: var(--wy-text-hint); display: flex; align-items: center; gap: 0.35rem; }
.wy-editor-bar__spacer { flex: 1; }

.wy-page { border: 1px solid var(--wy-border); border-radius: 14px; background: var(--wy-bg); padding: 0.7rem; }
.wy-stack { display: flex; flex-direction: column; gap: 0.5rem; }

.wy-cblock { background: var(--wy-card-bg); border: 1px solid var(--wy-border); border-radius: 11px; box-shadow: var(--wy-shadow); transition: box-shadow 0.12s; }
.wy-cblock:hover { box-shadow: 0 2px 6px rgba(20,20,15,.06), 0 9px 22px rgba(20,20,15,.06); }
.wy-cblock--resource { border-left: 3px solid var(--wy-teal); }
/* Draws the eye to Library Recommended Materials after "Add resource from
   collection" — see the ?added=1 handling in the inline <script> below. */
.wy-cblock--flash { animation: wy-cblock-flash 2.2s ease; }
@keyframes wy-cblock-flash {
    0%   { box-shadow: 0 0 0 3px var(--wy-teal-bd); }
    70%  { box-shadow: 0 0 0 3px var(--wy-teal-bd); }
    100% { box-shadow: none; }
}
/* A section that has been asked for but not yet confirmed by the server.
   Shaped like a real block so the list does not jump when the real one
   replaces it, but dashed and muted so it never reads as saved — the librarian
   is being shown WHERE it will land, not told it is done. */
.wy-cblock--pending {
    border-style:   dashed;
    border-color:   var(--wy-teal-bd);
    background:     var(--wy-bg);
    box-shadow:     none;
}

.wy-cblock--pending .wy-handle { cursor: default; }

.wy-pending-title {
    font-weight: 700;
    color:       var(--wy-text-muted);
}

.wy-pending-prev {
    display:     flex;
    align-items: center;
    gap:         0.4rem;
    color:       var(--wy-text-muted);
}

.wy-pending-spinner {
    width:         13px;
    height:        13px;
    flex-shrink:   0;
    border:        2px solid var(--wy-teal-bd);
    border-top-color: var(--wy-teal);
    border-radius: 50%;
    animation:     wy-pending-spin 0.7s linear infinite;
}

@keyframes wy-pending-spin { to { transform: rotate(360deg); } }

/* The spinner is decoration; the sentence beside it carries the meaning, so
   stopping it costs nothing (WCAG 2.3.3 / prefers-reduced-motion). */
@media (prefers-reduced-motion: reduce) {
    .wy-pending-spinner { animation: none; }
    .wy-cblock--flash   { animation: none; box-shadow: 0 0 0 3px var(--wy-teal-bd); }
}

/* A row waiting on the server — removing, or moving out of a section. It stays
   exactly where it is and keeps its size: collapsing it before the answer
   arrives would shift everything below, and a failure would then have to put
   the page back. Muted and non-interactive is enough to say "working". */
.wy-row--busy {
    opacity:        0.62;
    pointer-events: none;
}

.wy-row--busy .wy-handle,
.wy-row--busy .wy-chandle { cursor: default; }

/* Collapse on the way out, so the rows below slide up instead of jumping.
   Height is set explicitly in JS first — `auto` has nothing to animate from. */
.wy-row--collapsing {
    overflow:   hidden;
    transition: height 0.18s ease, margin-bottom 0.18s ease, opacity 0.18s ease;
    opacity:    0;
}

/* "Saving…" / "Saved" beside a section name. Absolutely nothing about the row's
   geometry may depend on it — a tag that appears and disappears must not nudge
   the title field it sits next to. */
.wy-btitle-row { position: relative; }

.wy-savedtag {
    display:     inline-flex;
    align-items: center;
    gap:         0.3rem;
    margin-left: 0.5rem;
    font-size:   0.75rem;
    white-space: nowrap;
}

.wy-savedtag--saving { color: var(--wy-text-muted); }
.wy-savedtag--saved  { color: var(--wy-teal-dark); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
    .wy-row--collapsing { transition: none; }
}

.wy-cblock.dragging,
.wy-child.dragging { opacity: 0.5; outline: 2px dashed var(--wy-teal-bd); outline-offset: 2px; }

.wy-bhead { display: flex; align-items: center; gap: 0.55rem; padding: 0.6rem 0.7rem; }
.wy-handle,
.wy-chandle { flex: 0 0 auto; display: grid; place-items: center; color: var(--wy-text-hint); cursor: grab; border-radius: 6px; }
.wy-handle { width: 24px; height: 28px; }
.wy-chandle { width: 20px; height: 24px; }
.wy-handle:hover,
.wy-chandle:hover { background: var(--wy-bg); color: var(--wy-text-muted); }
.wy-handle:active,
.wy-chandle:active { cursor: grabbing; }

.wy-bmain { min-width: 0; flex: 1; }
.wy-btype { font-size: 0.6rem; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; display: inline-flex; align-items: center; gap: 0.3rem; }
.wy-btype--res { color: var(--wy-teal-dark); }
.wy-btitle-row { margin-top: 0.02rem; }
.wy-titleedit { font: inherit; font-weight: 700; font-size: 0.92rem; width: 100%; max-width: 320px; border: 1px solid var(--wy-teal-bd); border-radius: 7px; padding: 0.25rem 0.45rem; color: var(--wy-text); background: var(--wy-card-bg); }
.wy-titleedit:focus { outline: none; box-shadow: 0 0 0 3px rgba(33,122,92,.12); }
.wy-bprev,
.wy-cprev { font-size: 0.8rem; color: var(--wy-text-hint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 0.02rem; }
.wy-pill { display: inline-block; font-size: 0.62rem; font-weight: 700; border-radius: 6px; padding: 0.02rem 0.34rem; background: var(--wy-bg); color: var(--wy-text-muted); border: 1px solid var(--wy-border); margin-right: 0.25rem; }

/* Overflow menu — .wy-dash-menuitem (assets/app-shell.css, already
   defensively hardened against Neve's global button hover/focus color) for
   rows, plus a separator + danger-color variant only this screen needs. */
.wy-ovsep { height: 1px; background: var(--wy-border); margin: 0.25rem 0.2rem; }
.wy-dash-menuitem.mi-danger,
.wy-dash-menuitem.mi-danger:hover,
.wy-dash-menuitem.mi-danger:focus { color: #b91c1c !important; }
.wy-dash-menuitem:disabled { color: var(--wy-text-hint) !important; cursor: default; }
.wy-dash-menuitem:disabled:hover { background: none !important; }
a.wy-dash-menuitem { text-decoration: none; }

/* Section container + one-level children, connector line matching the
   mock's visual hierarchy cue. */
.wy-children { padding: 0.15rem 0.7rem 0.7rem calc(0.7rem + 24px); position: relative; }
.wy-children::before { content: ""; position: absolute; left: calc(0.7rem + 11px); top: 0; bottom: 2.2rem; width: 2px; background: var(--wy-border); border-radius: 2px; }
.wy-child { display: flex; align-items: center; gap: 0.5rem; background: var(--wy-bg); border: 1px solid var(--wy-border); border-radius: 9px; padding: 0.5rem 0.6rem; margin-top: 0.5rem; position: relative; }
.wy-child::before { content: ""; position: absolute; left: -12px; top: 50%; width: 12px; height: 2px; background: var(--wy-border); }
.wy-child .wy-titleedit { max-width: 280px; }

.wy-addlink { display: inline-flex; align-items: center; gap: 0.35rem; margin-top: 0.55rem; border: 1px dashed var(--wy-teal-bd); background: var(--wy-card-bg); color: var(--wy-teal-dark); border-radius: 8px; padding: 0.4rem 0.7rem; font: inherit; font-size: 0.78rem; font-weight: 700; cursor: pointer; }
/* Explicit color here, not just background — Neve's global button hover
   style turns the text white, and the previous pale --wy-teal-light hover
   background left white-on-near-white with no contrast. Solid dark
   background instead, color pinned to match, same pairing every other
   .wy-btn on this screen already uses on hover. */
.wy-addlink:hover { background: var(--wy-teal-dark); border-color: var(--wy-teal-dark); color: #fff; }

.wy-dropline { height: 0; position: relative; }
.wy-dropline::after { content: ""; position: absolute; left: 2px; right: 2px; top: -3px; height: 3px; background: var(--wy-teal); border-radius: 3px; box-shadow: 0 0 0 3px var(--wy-teal-light); }

/* Add Content dropdown contents (two-step: destination, then type) — the
   anchoring/positioning itself is .wy-source-wrap/.wy-source-popover. */
.wy-addgrp { font-size: 0.66rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--wy-text-hint); padding: 0.5rem 0.55rem 0.25rem; }
.wy-addgrp--head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.45rem 0.4rem 0.5rem 0.55rem; margin-bottom: 0.15rem; border-bottom: 1px solid var(--wy-border); }
.wy-aopt { display: flex; align-items: center; gap: 0.6rem; width: 100%; border: 0; background: none; text-align: left; padding: 0.5rem 0.55rem; border-radius: 9px; font: inherit; cursor: pointer; }
.wy-aopt:hover { background: var(--wy-bg); }
.wy-aicon { width: 28px; height: 28px; border-radius: 7px; background: var(--wy-teal-light); border: 1px solid var(--wy-teal-bd); color: var(--wy-teal-dark); display: grid; place-items: center; flex: 0 0 auto; }
.wy-aopt-t { font-weight: 700; font-size: 0.88rem; color: var(--wy-text); }
.wy-aopt-d { font-size: 0.75rem; color: var(--wy-text-hint); }
.wy-aopt-chev { margin-left: auto; flex: 0 0 auto; color: var(--wy-text-hint); }
/* Close (×) — replaces the old "Back" link now that this menu never has a
   destination step to go back to (it always opens already knowing which
   section it's adding to). Also reused by #wy-addres-close. */
.wy-aclose { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; flex: 0 0 auto; border: 0; background: none; color: var(--wy-text-hint); border-radius: 7px; cursor: pointer; }
.wy-aclose:hover { background: var(--wy-bg); color: var(--wy-text); }

/* Student-facing 'welcome'/'heading' block kinds (inc/collection-blocks-render.php) */
.wy-block-lead { font-size: 1.02rem; color: var(--wy-text); margin: 0 0 1.2rem; line-height: 1.6; }
.wy-block-divider { font-size: 1.15rem; color: var(--wy-teal-dark); letter-spacing: -0.01em; margin: 1.4rem 0 0.8rem; }
.wy-exgroup__empty { color: var(--wy-text-hint); font-size: 0.88rem; margin: 0; }

.wy-blockform {
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       1.1rem 1.2rem;
    margin-bottom: 1rem;
}

.wy-blockform label {
    display:       block;
    font-weight:   700;
    font-size:     0.85rem;
    color:         var(--wy-text);
    margin:        0.7rem 0 0.35rem;
}

.wy-blockform label:first-of-type {
    margin-top: 0;
}

.wy-tx {
    width:         100%;
    padding:       0.65rem 0.9rem;
    font-size:     0.92rem;
    font-family:   var(--wy-font);
    border:        1.5px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-card-bg);
    color:         var(--wy-text);
    outline:       none;
    box-sizing:    border-box;
    transition:    border-color 0.15s;
}

textarea.wy-tx {
    min-height:  110px;
    line-height: 1.6;
    resize:      vertical;
}

.wy-tx:focus {
    border-color: var(--wy-teal);
    box-shadow:   0 0 0 3px rgba(58,125,110,0.12);
}

.wy-blockform__actions {
    display:    flex;
    gap:        0.6rem;
    margin-top: 1rem;
}

/* Base rule so .wy-link-action also works outside .wy-gap-act (the editor's
   "+ Add another link" button) — the existing .wy-gap-act rule above stays
   as-is and simply wins on specificity there. */
/* :not([hidden]) — this button is toggled via the HTML hidden attribute on
   the Course Collection view (wy-resolve-toggle); an unconditional
   `display` here would silently defeat that, same lesson as
   .wy-blockmenu/.wy-addblock/.wy-resolve-form above. */
.wy-link-action:not([hidden]) {
    background:      none;
    border:          0;
    color:           var(--wy-teal);
    font-weight:     600;
    font-size:       0.8rem;
    cursor:          pointer;
    font-family:     var(--wy-font);
    padding:         0;
    text-decoration: none;
    display:         inline-block;
    margin-bottom:   0.7rem;
}

.wy-link-action:hover {
    text-decoration: underline;
}

/* A placement row's title in the composer — static text where every other
   block type has a rename input, because a 'resource_item' has no title of
   its own: it shows the live title of the item it points at. Matched to
   .wy-titleedit's metrics so the rows still line up. */
.wy-ctitle-static {
    display:     block;
    font-weight: 600;
    font-size:   0.92rem;
    color:       var(--wy-text);
    padding:     0.35rem 0;
}

/* The destination section, named in the "Add resource from collection"
   panel header — the panel is shared by every section, so without this it
   never says which one it is adding to. */
.wy-addres-dest {
    font-weight: 500;
    color:       var(--wy-text-muted);
}

/* The "On this collection" list shares .wy-addres-row with the search
   results; it only needs a scroll cap, since a guide can hold dozens of
   items and the search box below it must stay reachable. */
.wy-addres-oncollection {
    max-height: 17rem;
    overflow-y: auto;
}

.wy-links-row {
    display:               grid;
    /* Third column: the row's own up/down/remove controls. Utility-link
       ORDER is what a student reads down the block, so it is edited here
       rather than being an accident of the order they were typed in. */
    grid-template-columns: 1fr 1.4fr auto;
    gap:                   0.5rem;
    margin-bottom:         0.5rem;
    align-items:           center;
}

.wy-links-rowacts {
    display: inline-flex;
    gap:     0.15rem;
}

.wy-link-move {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    width:           1.7rem;
    height:          1.7rem;
    padding:         0;
    border:          1px solid var(--wy-border);
    border-radius:   var(--wy-radius-sm);
    background:      #fff;
    color:           var(--wy-text-muted);
    font-size:       0.85rem;
    line-height:     1;
    cursor:          pointer;
}

.wy-link-move:hover { background: var(--wy-bg); color: var(--wy-text); }

/* Visually hidden but screen-reader-accessible — the dynamically-added
   link-row labels (WCAG 2.1 AA: every input needs a real associated
   <label>, not just a placeholder). */
.wy-visually-hidden {
    position: absolute;
    width:    1px;
    height:   1px;
    padding:  0;
    margin:   -1px;
    overflow: hidden;
    clip:     rect(0, 0, 0, 0);
    white-space: nowrap;
    border:   0;
}

/* ── Wizard progress spine (mandatory_reading_list guide mode) — ported
   from the MVP mockup's compact 1..5 stepper. Distinct from the existing
   .wy-wizard-step-* component (complementary_resources mode's 3-step
   Configure/Curate/Review wizard) — different flow, different page count,
   intentionally not reused. ── */
.wy-spine {
    background:    var(--wy-card-bg);
    border-bottom: 1px solid var(--wy-border);
    padding:       0.55rem 1.6rem;
}

.wy-spine ol {
    display:        flex;
    align-items:    center;
    justify-content: center;
    gap:            0.4rem;
    flex-wrap:      wrap;
    list-style:     none;
    margin:         0;
    padding:        0;
}

.wy-spine__step {
    display:     flex;
    align-items: center;
    gap:         0.45rem;
    font-size:   0.8rem;
    color:       var(--wy-text-hint);
}

.wy-spine__step:not(:last-child)::after {
    content:       '';
    width:         16px;
    height:        2px;
    background:    var(--wy-border);
    border-radius: 2px;
    margin-left:   0.4rem;
    flex-shrink:   0;
}

.wy-spine__n {
    width:           22px;
    height:          22px;
    border-radius:   50%;
    background:      var(--wy-bg);
    border:          1px solid var(--wy-border);
    display:         flex;
    align-items:     center;
    justify-content: center;
    font-size:       0.72rem;
    font-weight:     700;
    color:           var(--wy-text-hint);
    flex-shrink:     0;
}

.wy-spine__step--done .wy-spine__n,
.wy-spine__step--active .wy-spine__n {
    background:   var(--wy-teal);
    border-color: var(--wy-teal);
    color:        #fff;
}

.wy-spine__step--active {
    color:       var(--wy-text);
    font-weight: 600;
}

.wy-spine__step--disabled {
    opacity: 0.55;
}

.wy-spine__link {
    display:         flex;
    align-items:     center;
    gap:             0.45rem;
    color:           inherit;
    text-decoration: none;
    border-radius:   6px;
}

.wy-spine__link:hover .wy-spine__label,
.wy-spine__link:focus-visible .wy-spine__label {
    text-decoration: underline;
}

.wy-spine__link:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 2px;
}

@media (max-width: 640px) {
    .wy-spine__label {
        display: none;
    }
}

/* ── "Mark resolved — add link" (Course Collection view) — a librarian
   found a working link themselves for a needs_ill/needs_purchase gap.
   :not([hidden]) — same lesson as .wy-blockmenu: an unconditional `display`
   here would override the browser's default [hidden]{display:none} rule
   (equal specificity, author CSS wins) and the form would never hide. ── */
/* This form now lives ONLY inside the Mark-received dialog (#wy-fx-modal);
   the per-row inline copies are gone. Its old rules were sized for the row's
   ~15rem actions column — `align-items: flex-end` and a 220px input — which in
   a 40rem dialog right-aligned the label, the field and the hint against the
   panel edge while the title above them stayed left. Full width, left aligned,
   and at the dialog's type size. The ACTIONS row is right-aligned separately
   by .wy-fx-modal__actions, which is the dialog convention and stays. */
.wy-resolve-form:not([hidden]) {
    display:        flex;
    flex-direction: column;
    align-items:    stretch;
    gap:            0.35rem;
    margin-top:     0.3rem;
    text-align:     left;
}

.wy-resolve-form input.wy-tx {
    width:     100%;
    max-width: 100%;
    padding:   0.55rem 0.7rem;
    font-size: 0.9rem;
}

.wy-resolve-form__actions {
    display: flex;
    gap:     0.4rem;
}

/* ── Find OER + Add-a-resource modals (assets/oer-tools.js) ──
   No existing modal pattern elsewhere in this app (other flows use full
   pages), so this is a new, minimal, accessible overlay+dialog pair —
   WyderNet_OER_Build_Spec_1.md §2/§4. */
body.wy-modal-open {
    overflow: hidden;
}

.wy-modal-overlay {
    position:        fixed;
    inset:           0;
    background:      rgba(20, 20, 18, 0.5);
    display:         flex;
    align-items:     center;
    justify-content: center;
    padding:         1rem;
    z-index:         1000;
}

.wy-modal-overlay[hidden] {
    display: none;
}

.wy-modal {
    position:      relative;
    background:    var(--wy-card-bg);
    border-radius: var(--wy-radius);
    box-shadow:    var(--wy-shadow);
    padding:       1.8rem 1.8rem 1.6rem;
    max-width:     480px;
    width:         100%;
    max-height:    90vh;
    overflow-y:    auto;
    text-align:    left;
}

.wy-modal-title {
    margin:      0 0 0.3rem;
    font-size:   1.1rem;
    padding-right: 1.5rem;
}

/* Icon circle + title row — Home screen's Set to Draft confirmation
   (WyderNet_Home_Implementation_Prompt_2.md §6). No existing .wy-modal-*
   instance needed an icon before this; --caution (amber) is deliberately
   distinct from a destructive-red treatment, since this isn't a delete. */
.wy-modal-head {
    display:     flex;
    align-items: center;
    gap:         0.55rem;
    margin-bottom: 0.2rem;
}
.wy-modal-head .wy-modal-title { margin: 0; }
.wy-modal-icon {
    width:           34px;
    height:          34px;
    border-radius:   9px;
    display:         flex;
    align-items:     center;
    justify-content: center;
    flex:            0 0 auto;
}
.wy-modal-icon svg { width: 17px; height: 17px; }
.wy-modal-icon--caution {
    background: var(--wy-amber-bg);
    border:     1px solid var(--wy-amber-bd);
    color:      var(--wy-amber);
}

.wy-modal-subtitle {
    margin:    0 0 1rem;
    color:     var(--wy-text-muted);
    font-size: 0.88rem;
}

.wy-modal-hint {
    margin:    0.8rem 0 0;
    color:     var(--wy-text-hint);
    font-size: 0.78rem;
}

.wy-modal-actions {
    display:       flex;
    gap:           0.6rem;
    flex-wrap:     wrap;
    margin-top:    1.2rem;
}

/* The Edit window's footer: Cancel · Save Changes · Save & Move to Ready to
   Use, left to right, with the confirming action as the only solid button.
   Three actions need a visible hierarchy — the one that makes an item visible
   to students should not look like the one that saves a typo fix. */
/* Wider than the shared 480px, because this one now carries three actions and
   the third names its own consequence. At 480px they wrapped, which put the
   confirming action alone on a second row — the opposite of the hierarchy the
   order is there to express. */
#wy-review-edit-modal .wy-modal {
    max-width: 580px;
}

.wy-modal-actions--edit {
    justify-content: flex-start;
    align-items:     center;
    border-top:      1px solid var(--wy-border);
    padding-top:     1rem;
}

/* Three buttons on one row need less air than one or two do. Still wraps below
   ~560px of dialog, which is the phone case, and the order survives the wrap. */
.wy-modal-actions--edit .wy-btn {
    padding:   0.7rem 1.1rem;
    font-size: 0.9rem;
}

/* Cancel. Neutral rather than the teal outline, so "Save Changes" is the only
   teal-outlined control and reads as the middle of the three.
   --wy-border-control, NOT --wy-border: the border is the only thing
   identifying this button's boundary, which is exactly the 1.4.11 case that
   variable exists for (#e5e7eb is 1.24:1 on white). */
.wy-btn--quiet {
    background:  transparent;
    color:       var(--wy-text);
    border:      1.5px solid var(--wy-border-control);
    font-weight: 600;
}

.wy-btn--quiet:hover:not(:disabled),
.wy-btn--quiet:focus:not(:disabled) {
    background:   var(--wy-bg);
    border-color: var(--wy-text-muted);
    color:        var(--wy-text);
    transform:    translateY(-1px);
}

/* Needs Evaluation's view switch REUSES .wy-revmode — the same control as
   "Show: Entire Course / By Course Schedule" above it, because it does the same job:
   pick one of two views of the same collection. Two controls that behave
   identically should not look different, and a librarian meets these two one
   above the other. Only the placement differs: this one sits inside its
   section, under the heading it belongs to. */
/* Two-class selector, not one. `.wy-revmode--eval` alone ties with
   `.wy-revmode` on specificity and wins only on source order — the same setup
   that twice let an unrelated later rule delete a focus ring in this codebase.
   These declarations are the whole point of the variant, so they are not left
   to which block happens to come last in the file. */
.wy-revmode.wy-revmode--eval {
    /* A real container, not a rule across the card. It used to be a borderless
       strip with a bottom border and 0.6rem of padding, which measured ZERO
       pixels from the section heading above and ZERO from the bulk bar below:
       the row read as part of whatever it touched rather than as its own
       control. The card treatment is .wy-revmode's own — the Show control it
       is meant to match — and the recessed --wy-bg fill is what keeps it from
       reading as a second card floating inside the white one. */
    margin:        0.9rem 1.1rem 1.15rem;
    padding:       0.85rem 1.05rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-bg);

    /* Split, because the two axes answer different questions. The column gap
       separates the label from the options and the options from each other —
       16px was tight enough that the two read as one run of text. The row gap
       only applies once the control wraps at narrow widths, where a 1.75rem
       vertical step would be a hole. */
    column-gap:    var(--wy-eval-gap);
    row-gap:       0.6rem;
    --wy-eval-gap: 1.75rem;
}

/* Narrow: the control wraps to two or three lines, so the generous desktop gap
   buys nothing and the side margins are worth more as content width. */
@media (max-width: 560px) {
    .wy-revmode.wy-revmode--eval {
        --wy-eval-gap: 1.1rem;
        margin:        0.75rem 0.75rem 0.9rem;
        padding:       0.75rem 0.85rem;
    }
}

/* The count travels with its label. Tabular figures so the two rows do not
   shift as counts change under a librarian mid-review. */
.wy-eval-count {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color:       var(--wy-text-muted);
}

.wy-revmode__opt input:checked ~ .wy-eval-count { color: var(--wy-teal-dark); }

/* The row a librarian just acted on, after the page reloads under them.
   A ring rather than a background wash: these rows already use background to
   mean something (the alternate box tints its own), and a ring cannot be
   mistaken for a state the row is now in. It fades out on its own — this says
   "here it is", not "this needs attention". */
@keyframes wy-flash-ring {
    from { box-shadow: 0 0 0 3px var(--wy-teal); }
    to   { box-shadow: 0 0 0 3px rgba(0, 0, 0, 0); }
}

.wy-flash {
    border-radius: var(--wy-radius-sm);
    animation:     wy-flash-ring 2.2s ease-out 1;
}

/* Someone who has asked for less motion still needs to be told WHICH row; they
   get the ring held steady instead of animated away. */
@media (prefers-reduced-motion: reduce) {
    .wy-flash {
        animation:  none;
        box-shadow: 0 0 0 3px var(--wy-teal);
    }
}

/* The syllabus request, kept on screen while the librarian searches: comparing
   a result against the request needs both halves visible at once.
   Deliberately NOT boxed like a form control — sitting above a search input, a
   bordered light-grey box reads as a second, disabled field. */
.wy-catalog-request {
    margin:      0 0 1.1rem;
    padding:     0;
    font-size:   0.9rem;
    line-height: 1.45;
    color:       var(--wy-text-muted);
}

.wy-catalog-request strong {
    color: var(--wy-text);
}

#wy-review-catalog-modal .wy-modal { max-width: 640px; }

#wy-review-catalog-modal .wy-addres-results {
    margin-top: 0.8rem;
    max-height: 46vh;
    overflow-y: auto;
}

/* A discovery service answers with the whole statement of responsibility as
   the title — "Letters from Iwo Jima / Dreamworks Pictures and Warner Bros.
   Pictures present a Malpaso/Amblin Entertainment production ; directed by
   …" — and the contributor list as the description. Unclamped, one result
   fills the window and the list cannot be scanned, which is the only thing
   this list is for. The full text stays in the title attribute. */
#wy-review-catalog-modal .wy-addres-row__text .wy-exitem__title,
#wy-review-catalog-modal .wy-addres-row__text .wy-exitem__meta {
    display:            -webkit-box;
    -webkit-box-orient: vertical;
    overflow:           hidden;
}

#wy-review-catalog-modal .wy-addres-row__text .wy-exitem__title {
    -webkit-line-clamp: 2;
}

#wy-review-catalog-modal .wy-addres-row__text .wy-exitem__meta {
    -webkit-line-clamp: 2;
    margin-top:         0.15rem;
}

/* "View this copy" — inspecting a result before committing to it. Deliberately
   quieter than the "Use this" button beside it: one of the two writes to the
   collection and the other only opens a tab, and that difference has to be
   legible before the click. inline-block so the two-line clamp on the title
   above cannot swallow it. */
#wy-review-catalog-modal .wy-addres-row__view {
    display:         inline-block;
    margin-top:      0.3rem;
    font-size:       0.8125rem;
    color:           var(--wy-teal);
    text-decoration: underline;
}

#wy-review-catalog-modal .wy-addres-row__view:hover,
#wy-review-catalog-modal .wy-addres-row__view:focus-visible {
    color: var(--wy-teal-dark);
}

/* The action must not be squeezed to nothing by a long title. */
#wy-review-catalog-modal .wy-addres-row > .wy-btn {
    flex-shrink: 0;
    align-self:  flex-start;
}

/* Sits directly above the footer, as the prompt asks: the consequence stated
   before the button that causes it, not after. */
.wy-edit-confirm-note {
    display:          block;
    margin-top:       1.2rem;
    padding:          0.85rem 1rem;
    border-radius:    var(--wy-radius-sm);
    background:       var(--wy-teal-light);
    font-size:        0.9rem;
    line-height:      1.45;
}

.wy-edit-confirm-note strong {
    display: block;
    color:   var(--wy-text);
}

.wy-edit-confirm-note span {
    /* --wy-text-muted clears 4.5:1 on --wy-teal-light as well as on white. */
    color: var(--wy-text-muted);
}

.wy-modal-close {
    position:        absolute;
    top:             0.9rem;
    right:           0.9rem;
    width:           28px;
    height:          28px;
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    border:          0;
    background:      none;
    border-radius:   7px;
    font-size:       1.3rem;
    line-height:     1;
    color:           var(--wy-text-muted);
    cursor:          pointer;
}

.wy-modal-close:hover {
    background: var(--wy-bg);
    color:      var(--wy-text);
}

.wy-oer-source-list {
    list-style: none;
    margin:     0;
    padding:    0;
    display:    flex;
    flex-direction: column;
    gap:        0.5rem;
}

.wy-oer-source-link {
    display:       flex;
    align-items:   center;
    justify-content: space-between;
    padding:       0.65rem 0.9rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    color:         var(--wy-text);
    font-weight:   600;
    font-size:     0.9rem;
    text-decoration: none;
}

.wy-oer-source-link:hover {
    background:   var(--wy-bg);
    border-color: var(--wy-teal);
}

.wy-oer-source-link::after {
    content:     '↗';
    color:       var(--wy-text-hint);
    font-weight: 400;
}

/* ── CookieYes consent banner — WCAG AA contrast override ──────────────────
   Third-party markup, but it renders on every page for any visitor who has
   not consented yet — students included — so it is inside our AA scope.

   All three banner buttons (Accept, Reject, Preferences) are solid fills
   with a white label, and the plugin writes that palette as an INLINE style
   attribute (style="color:#FFFFFF;background-color:#1578F7;..."), which
   outranks any normal stylesheet rule — hence !important here.

   #1578f7 behind white text is 4.14:1, under AA 4.5:1. #0d6ce4 is the same
   hue at 4.93:1. The white label is left alone; only the fill moves.

   This duplicates a value that also lives in the CookieYes plugin settings.
   If that palette is changed there, re-check it against AA and update or
   delete this block rather than leaving the two silently disagreeing. */
.cky-btn {
    background-color: #0d6ce4 !important;
    border-color:     #0d6ce4 !important;
}

/* ── OneTap accessibility widget — restore the focus indicator its own CSS
      deletes (WCAG 2.1 2.4.7 Focus Visible, Level AA) ─────────────────────
   Reported by QA: tabbing to the widget's launcher (bottom right, every page
   including the student-facing guide) moves focus there with nothing visible
   to show it. That is not an oversight in our styling — the plugin ships
   these rules (accessibility-onetap-front-end.min.css, v2.13.0):

       .onetap-toggle:focus,
       .onetap-container-toggle .onetap-toggle:hover {
           outline: 0 !important; box-shadow: none !important;
           border: 2px solid transparent !important; }
       .onetap-toggle.design-border1:focus { outline: 0 !important; }

   An accessibility overlay suppressing its own focus ring is worth noting
   plainly, and it is not configurable from the plugin's settings screens —
   the rules are hardcoded, so CSS is the only lever. Same situation and same
   technique as the CookieYes contrast override above.

   SPECIFICITY, not source order: the vendor rule is (0,2,0) with !important,
   and its stylesheet is enqueued by a plugin whose load order relative to the
   theme is not ours to guarantee. Repeating the class (.onetap-toggle
   .onetap-toggle) takes this to (0,3,0), so it wins wherever it lands. The
   ring is drawn on the button, offset clear of the 60px circular icon: the
   3px gap means the ring's adjacent colour is the page ground, not the
   widget's own blue, so --wy-teal-dark measures 6.90:1 on --wy-bg (#f9f9f8)
   and 7.27:1 on white — both well over 1.4.11's 3:1. (Against the icon's
   #0048fe it would be 1.16:1, which is why the offset is not decorative.)

   The widget remains a candidate for REMOVAL rather than repair (VPAT backlog
   A2): an overlay is not a substitute for code-level conformance, and several
   university accessibility offices read its presence as a negative signal.
   This makes the criterion pass in the meantime; delete this block with the
   plugin. */
.onetap-toggle.onetap-toggle:focus-visible,
.onetap-toggle.onetap-toggle.design-border1:focus-visible,
.onetap-container-toggle .onetap-toggle.onetap-toggle:focus-visible {
    outline:        3px solid var(--wy-teal-dark) !important;
    outline-offset: 3px !important;
    border-radius:  50% !important;
}

/* The panel's own controls are styled by the vendor too; the launcher is what
   QA reported and what a keyboard user meets first, but a focus ring that
   stops at the door is worse than none. These are the panel's close button,
   its stepper buttons and its language combobox. */
.onetap-accessibility .onetap-close:focus-visible,
.onetap-accessibility .onetap-btn:focus-visible,
.onetap-accessibility #onetap-language-list:focus-visible {
    outline:        2px solid var(--wy-teal-dark) !important;
    outline-offset: 2px !important;
}

/* ── Review header actions + source-document menu ──
   The Review screen's title block gained a right-hand action ("View the
   syllabus"), so crumb+title move into a flex row. Same shape as
   .wy-ch-coursehead in app-shell.css; align-items:flex-start keeps the button
   level with the crumb rather than centred against a two-line title, and
   flex-wrap drops it below the title on a narrow viewport instead of
   squeezing it. */
.wy-review-head {
    display:         flex;
    align-items:     flex-start;
    justify-content: space-between;
    gap:             1rem;
    flex-wrap:       wrap;
}

.wy-review-head__titles {
    /* flex-basis 0 + grow: the titles take whatever the button leaves and
       WRAP inside it. min-width:0 alone was not enough — with basis auto a
       long crumb ("FOUNDATIONS OF … AND THEORY · REVIEW") measured wider than
       the row and flex-wrap dropped the button onto a line of its own, at the
       left (2026-10-01). */
    flex:          1 1 0;
    min-width:     0;
    overflow-wrap: anywhere;
}
/* The action keeps its size and its place in the top-right corner. */
.wy-review-head > :not(.wy-review-head__titles) { flex: 0 0 auto; }
/* Wrapping is for phones only, where the button genuinely has no room. */
@media (min-width: 641px) {
    .wy-review-head { flex-wrap: nowrap; }
}

/* PRO-265 — the one Send to instructor, covering all four boxes. A plain form
   rather than a button so the selection can be posted; the form must not
   introduce a block of its own into the flex row. */
.wy-review-send {
    margin: 0;
    flex:   0 0 auto;
}

/* Disabled is the resting state — nothing is selected when the page loads —
   so it has to read as "not yet" rather than as broken. Dimmed, not greyed
   flat, and the label still says what it will do. */
.wy-review-send__btn[disabled] {
    opacity: 0.55;
    cursor:  not-allowed;
}

/* Restored at higher specificity than any :hover rule on .wy-btn. A rule
   written for the mouse has deleted the keyboard focus ring twice in this
   codebase, both times by winning on source order.
   
   Scoped to .wy-review-send, its own wrapper — NOT to .wy-review-head, which
   is where the button used to live. Moving it into the bulk bar took it out
   of that ancestor and silently took the focus ring with it: a third instance
   of this same failure, caused this time by relocating the element rather
   than by a later rule. Key a focus ring on something the control carries
   itself. */
.wy-review-send .wy-review-send__btn:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 2px;
}

/* Smaller than a wizard's primary action — this is a secondary affordance
   sitting beside a heading, not a step in the flow. */
.wy-doc-btn {
    padding:     0.5rem 0.9rem;
    font-size:   0.88rem;
    white-space: nowrap;
}

/* The multi-document case: a <details> disclosure, so it opens from the
   keyboard with no JS and no focus management of its own. */
.wy-doc-menu {
    position: relative;
}

.wy-doc-menu > summary {
    list-style: none;   /* the caret is our own SVG, not the UA marker */
}
.wy-doc-menu > summary::-webkit-details-marker {
    display: none;
}

.wy-doc-menu[open] .wy-doc-menu__caret {
    transform: rotate(180deg);
}

.wy-doc-menu__caret {
    transition: transform 0.15s ease;
}

.wy-doc-menu__panel {
    position:      absolute;
    right:         0;
    z-index:       20;
    min-width:     15rem;
    max-width:     22rem;
    margin-top:    0.35rem;
    padding:       0.4rem;
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    box-shadow:    var(--wy-shadow);
    text-align:    left;
}

.wy-doc-menu__label {
    padding:        0.4rem 0.55rem 0.2rem;
    font-size:      0.72rem;
    font-weight:    700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color:          var(--wy-text-hint);
}

.wy-doc-menu__item {
    display:         block;
    padding:         0.45rem 0.55rem;
    border-radius:   var(--wy-radius-sm);
    font-size:       0.9rem;
    color:           var(--wy-text);
    text-decoration: none;
    overflow-wrap:   anywhere;   /* uploaded filenames can be long and unbroken */
}

.wy-doc-menu__item:hover,
.wy-doc-menu__item:focus {
    background: var(--wy-teal-light);
    color:      var(--wy-teal-dark);
}

/* ── Skip link (WCAG 2.4.1) ────────────────────────────────────────────────
   Mirrors the rule in app-shell.css, which the public/LTI header never loads.
   Off-screen until focused, then the first thing a keyboard user lands on. */
.wy-skip-link{
    position:absolute;top:-40px;left:.5rem;z-index:1000;
    background:var(--wy-card-bg);color:var(--wy-text);
    padding:.6rem 1rem;border-radius:6px;
    font-size:.9rem;font-weight:700;text-decoration:none;
    box-shadow:0 2px 8px rgba(0,0,0,.15);transition:top .15s;
}
.wy-skip-link:focus{top:.5rem;outline:2px solid var(--wy-teal-dark);outline-offset:2px;}

/* ═══════════════════════════════════════════════════════════════════════
   Review — Course Materials: Possible / Alternate Match
   ═══════════════════════════════════════════════════════════════════════
   The box between Ready to Use and Need Sourcing (lti-parts/part-app-
   review.php, classification in inc/alt-match.php). Replaces two warning
   triangles whose popovers described a mismatch in prose; this shows the
   mismatch itself, both sides side by side.

   Every color pairing below was picked against WCAG 2.1 AA 4.5:1 for text
   and 3:1 for the pill borders, using the same palette tokens the rest of
   the sheet does — the verdict pills are the risky ones, since they are
   small text on a tint, and their ratios are noted inline.  */

/* The row wrapper. Its inner .wy-exitem is a Ready to Use row verbatim, so
   the wrapper must not restyle it — it only stacks the comparison panel
   underneath and draws the shared separator the .wy-exitem would have. */
.wy-altrow {
    border-bottom: 1px solid var(--wy-border);

    /* How far the expanded content is indented so it lines up with the row
       TITLE rather than the row edge. The panel is a sibling of <summary>, so
       it starts UNDER the chevron while the title sits to the chevron's
       right — every table, sentence and button in it otherwise hangs a full
       chevron's width left of the heading it belongs to.

       calc() over the same values .wy-gap-chev and .wy-gap-summary use, so it
       tracks them instead of drifting as a magic number. The +2px is
       .wy-gap-chev's 1px border per side: neither Neve nor this sheet sets a
       universal box-sizing reset, so its `width: 30px` is content-box and the
       border sits OUTSIDE it. Checked against the deployed Neve build rather
       than assumed — under border-box this would be 2px too deep. */
    --wy-alt-indent: calc(30px + 2px + 0.5rem);
}
.wy-hasalt {
    --wy-alt-indent: calc(30px + 2px + 0.5rem);
}
.wy-altrow:last-child {
    border-bottom: none;
}
/* .wy-exitem centres its children, which is right for a one-line row and
   wrong the moment the disclosure opens — the action icons would drift to the
   vertical middle of an expanded comparison table. Top-align, and nudge the
   icons down to sit level with the title. */
.wy-altrow .wy-exitem {
    border-bottom: none;
}

/* Any row carrying a comparison panel, in EITHER box — .wy-exitem centres its
   children, which is right for a one-line row and wrong the moment the
   disclosure opens: the action icons would drift to the vertical middle of an
   expanded table. A dedicated class rather than :has(), and rather than
   double-classing the row as .wy-altrow, which would have drawn its border
   twice. */
.wy-hasalt {
    align-items: flex-start;
}

.wy-altrow__actions {
    margin-top: 0.1rem;
}

/* Indent the expanded content to the row TITLE, not the row edge.

   The panel is a sibling of <summary>, so it starts at the disclosure's left
   edge — under the chevron, while the title sits to the chevron's right. That
   left every table, sentence and button in the panel hanging a full chevron's
   width left of the heading they belong to.

   The offset is the chevron's outer width plus the summary's flex gap,
   written as calc() over the same values .wy-gap-chev and .wy-gap-summary
   use so it tracks them instead of drifting as a magic number. The +2px is
   .wy-gap-chev's 1px border on each side: neither Neve nor this sheet sets a
   universal box-sizing reset, so its `width: 30px` is content-box and the
   border sits OUTSIDE it. Verified against the deployed Neve build rather
   than assumed — border-box would make this 2px too deep. */
.wy-hasalt .wy-alt-panel {
    padding: 0.55rem 0 0.35rem var(--wy-alt-indent);
}

/* Wide content scrolls INSIDE its own container — the page body must never
   scroll sideways because a long publisher name arrived in one cell. */
.wy-alt-tablewrap {
    overflow-x:                  auto;
    border:                      1px solid var(--wy-border);
    border-radius:               var(--wy-radius-sm);
    -webkit-overflow-scrolling:  touch;
}

.wy-alt-table {
    width:           100%;
    border-collapse: collapse;
    font-size:       0.84rem;
    background:      var(--wy-card-bg);
}

/* Reset FIRST. The parent theme styles bare `td`/`th` with its own heavier
   border-bottom, and setting only a border-top here left both drawn — a thick
   dark rule under the data cells and a thin one under the row header, in the
   same table. Zero the shorthand, then draw exactly one 1px line. */
.wy-alt-table th,
.wy-alt-table td {
    border:         0;
    border-top:     1px solid var(--wy-border);
    background:     none;
    padding:        0.55rem 0.75rem;
    vertical-align: top;
    text-align:     left;
}

.wy-alt-table thead th {
    padding:        0.5rem 0.75rem;
    background:     var(--wy-bg);
    border-top:     0;
    border-bottom:  1px solid var(--wy-border);
    color:          var(--wy-text-hint);
    font-size:      0.68rem;
    font-weight:    700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space:    nowrap;
}

/* The header row already draws the line above the first body row. */
.wy-alt-table tbody tr:first-child th,
.wy-alt-table tbody tr:first-child td {
    border-top: 0;
}

/* The row header (TITLE / AUTHOR / …) is a <th scope="row">, so it carries
   the same quiet treatment as the column headers rather than reading as
   data. */
.wy-alt-table tbody th.wy-alt-table__field {
    width:          6.5rem;
    color:          var(--wy-text-hint);
    font-size:      0.68rem;
    font-weight:    700;
    letter-spacing: 0.06em;
    white-space:    nowrap;
}

.wy-alt-table__verdict {
    width:      3.5rem;
    text-align: right !important;
}

.wy-alt-val {
    color:       var(--wy-text);
    font-weight: 600;
    word-break:  break-word;
}

/* "Not on syllabus" / "—". Deliberately quiet AND italic: absence must not
   be mistaken for a value, and the italic carries that without relying on
   the lighter color alone. */
.wy-alt-val--absent {
    color:       var(--wy-text-hint);
    font-style:  italic;
    font-weight: 400;
}

/* PRO-246: the same ISBN, written the other way.
   A library stating 0393322572 against a syllabus asking for 9780393322576
   has stated the SAME number, and the row otherwise shows two unequal-looking
   strings under a green tick. Quiet and on its own line, because the record's
   own string is what the librarian will find in the catalogue and must stay
   the thing they read first — this explains the tick, it does not replace the
   value. Not italic: --absent owns italic for "there is no value here", and
   this row has one. */
.wy-alt-val__same {
    display:     block;
    margin-top:  .15rem;
    color:       var(--wy-text-hint);
    font-size:   .78rem;
    font-weight: 400;
    line-height: 1.35;
}

/* Verdict pills. The GLYPH is the carrier (WCAG 1.4.1 — never color alone):
   Y / N / —, with visually-hidden "Matches exactly" / "Matches after
   normalization" / "Differs" / "Not compared" behind it for anyone who can't
   see the glyph's tint either. */
.wy-alt-pill {
    display:         inline-flex;
    align-items:     center;
    justify-content: center;
    min-width:       1.65rem;
    padding:         0.15rem 0.35rem;
    border-radius:   5px;
    border:          1px solid transparent;
    font-size:       0.76rem;
    font-weight:     700;
    line-height:     1.3;
}

/* #217a5c on #e6f2ec — 4.57:1. Both states of agreement share the palette
   AND the glyph: a normalized match IS a match, and neither a caution tint nor
   a hedged mark should say otherwise. The two classes are kept apart only so
   the difference stays addressable in CSS if it ever needs to be shown again;
   the screen-reader label is where it is actually still carried. */
.wy-alt-pill--exact,
.wy-alt-pill--yes {
    background:   var(--wy-teal-light);
    border-color: var(--wy-teal-bd);
    color:        var(--wy-teal);
}

/* #b91c1c on #fdeaea — 5.59:1. Same red family as .wy-btn--danger and
   .wy-alert--error, so "differs" reads in the language the rest of the app
   already uses for a problem. */
.wy-alt-pill--no {
    background:   #fdeaea;
    border-color: #f4c9c9;
    color:        #b91c1c;
}

/* #6b6b6b on #f2f2f2 — 4.76:1 */
.wy-alt-pill--none {
    background:   #f2f2f2;
    border-color: var(--wy-border);
    color:        var(--wy-text-hint);
}

/* ── Format chooser (inc/reading-formats.php) ─────────────────────────────
   The forms the library holds one work in. A real <fieldset>/<legend> with
   real radios: the browser's own grouping semantics and arrow-key movement
   are the accessible behaviour, and restyling a radio into a card would have
   meant rebuilding both. The native control is kept visible and simply
   aligned — a 2px offset is cheaper than a roving-tabindex widget. */
/* Lives inside the FORMAT row's own <td>, so the fieldset carries no border or
   padding of its own — the row is the container. */
.wy-fmt {
    margin:  0;
    padding: 0;
    border:  0;
}

.wy-fmt__lead,
.wy-fmt__helper,
.wy-fmt__grouped {
    margin:    0;
    color:     var(--wy-text-hint);
    font-size: 0.78rem;
}

.wy-fmt__lead { margin-bottom: 0.5rem; }

.wy-fmt__helper { margin-top: 0.45rem; }

.wy-fmt__grouped {
    margin-top:  0.4rem;
    padding-top: 0.4rem;
    border-top:  1px solid var(--wy-border);
    font-size:   0.74rem;
}

/* Each format is a card, so the selected one can be shown by more than a dot.
   NOT tint alone: the name also goes bold on the checked option, through a
   plain sibling selector that works everywhere — :has() only adds the ground
   tint, so a browser without it still shows the selection (WCAG 1.4.1). */
.wy-fmt__opt {
    display:               grid;
    grid-template-columns: auto 1fr;
    align-items:           baseline;
    gap:                   0.15rem 0.55rem;
    margin-bottom:         0.4rem;
    padding:               0.5rem 0.65rem;
    border:                1px solid var(--wy-border);
    border-radius:         8px;
}

.wy-fmt__opt:last-of-type { margin-bottom: 0; }

.wy-fmt__opt:has(.wy-fmt__radio:checked) {
    border-color: var(--wy-teal-bd);
    background:   var(--wy-teal-light);
}

/* The librarian's OWN resource, first in the list. Tinted apart from the
   library's copies because it is a different kind of thing — a decision, not a
   holding — and because the row directly under it may be a teal "Available
   online" that would otherwise read as the same category. Blue rather than
   teal for that separation; the "Selected by library" label carries the
   meaning, so tint is never the only cue (WCAG 1.4.1). */
.wy-fmt__opt--librarian {
    border-color: #bfdbfe;
    background:   #eff6ff;
}

.wy-fmt__opt--librarian:has(.wy-fmt__radio:checked) {
    border-color: #93c5fd;
    background:   #dbeafe;
}

/* The URL identifies the chosen resource, and a long one must not stretch the
   comparison table it sits inside. */
.wy-fmt__detail--url {
    grid-column:   2;
    font-size:     0.75rem;
    color:         var(--wy-text-muted);
    overflow-wrap: anywhere;
}

.wy-fmt__radio:checked + .wy-fmt__label .wy-fmt__name { font-weight: 800; }

.wy-fmt__radio {
    margin:    0;
    transform: translateY(2px);
}

/* "DEFAULT" — on the FIRST option, never on the selected one: it states what
   happens if the librarian does nothing, which stays true after they choose. */
.wy-fmt__default {
    margin-left:    auto;
    padding:        0.05rem 0.35rem;
    border-radius:  4px;
    background:     rgba(0, 0, 0, 0.06);
    color:          var(--wy-text-hint);
    font-size:      0.64rem;
    font-weight:    700;
    letter-spacing: 0.04em;
}

.wy-fmt__label {
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    gap:         0.3rem 0.5rem;
    cursor:      pointer;
}

.wy-fmt__name {
    color:       var(--wy-text);
    font-size:   0.85rem;
    font-weight: 600;
}

/* The availability word carries a tint AND its own text, never a tint alone
   (WCAG 1.4.1) — "In the library" and "No copy held" are different words
   before they are different colours. */
.wy-fmt__avail {
    padding:       0.05rem 0.4rem;
    border-radius: 999px;
    font-size:     0.72rem;
    font-weight:   600;
}

.wy-fmt__avail--full_text,
.wy-fmt__avail--on_shelf {
    background: var(--wy-teal-light);
    color:      var(--wy-teal);
}

.wy-fmt__avail--central_index,
.wy-fmt__avail--unknown {
    background: #f2f2f2;
    color:      var(--wy-text-hint);
}

/* #b91c1c on #fdeaea — 5.59:1, the same red the rest of the app uses for a
   problem. A catalogue record with no copy behind it IS a problem. */
.wy-fmt__avail--no_copy {
    background: #fdeaea;
    color:      #b91c1c;
}

/* Quieter, never hidden: an unobtainable copy still explains why a gap is a
   gap, and it stays selectable — a librarian may be about to order it. */
.wy-fmt__opt--unavailable .wy-fmt__name { font-weight: 500; }

.wy-fmt__detail {
    display:     flex;
    flex-wrap:   wrap;
    gap:         0.25rem 0.7rem;
    flex-basis:  100%;
    color:       var(--wy-text-hint);
    font-size:   0.75rem;
}

.wy-fmt__field-label {
    font-weight:    600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    font-size:      0.68rem;
}

/* Outside the <label> in the markup, so it cannot swallow the click that
   selects the radio. */
/* Second column, under the label — outside the <label> in the markup so it
   cannot swallow the click that selects the radio. */
.wy-fmt__link {
    grid-column: 2;
    font-size:   0.75rem;
    white-space: nowrap;
}

/* The FORMAT row is the one row whose cells are not two short values, so it
   gets to breathe where the compared rows stay compact. */
.wy-alt-table__formatrow > td { padding-top: 0.6rem; padding-bottom: 0.6rem; }

.wy-fmt__status {
    margin:    0.45rem 0 0;
    min-height: 1.1em;
    color:     var(--wy-text-hint);
    font-size: 0.76rem;
}

.wy-alt-summary {
    display:     flex;
    gap:         0.45rem;
    margin:      0.7rem 0 0.85rem;
    color:       var(--wy-text);
    font-size:   0.84rem;
    line-height: 1.5;
}

.wy-alt-summary__icon {
    width:       15px;
    height:      15px;
    flex-shrink: 0;
    margin-top:  0.15rem;
    color:       var(--wy-text-hint);
}

.wy-alt-summary__body {
    color: var(--wy-text-muted);
}

/* "View the library's copy →". The evidence sentence tells a librarian to go
   look; without this there was nothing to look at. */
.wy-alt-summary__link {
    color:           var(--wy-teal);
    font-weight:     600;
    text-decoration: underline;
    white-space:     nowrap;
}
.wy-alt-summary__link:hover {
    color: var(--wy-teal-dark);
}

/* The library record a librarian's own link has SUPERSEDED. Still reachable —
   the holdings evidence is history worth keeping, and this is the only way
   back to it from Review — but visibly secondary, so the row has exactly one
   link that looks like the access path and it is the access path. Muted
   rather than teal, and normal weight; the label says which is which, so
   colour is never carrying the distinction on its own (WCAG 1.4.1). */
.wy-alt-summary__link--super {
    color:       var(--wy-text-muted);
    font-weight: 400;
}
.wy-alt-summary__link--super:hover {
    color: var(--wy-text);
}

.wy-alt-actions {
    display:   flex;
    gap:       0.6rem;
    flex-wrap: wrap;
}

/* Both actions are row-scale, not page-scale — .wy-btn's default 1.75rem
   padding is sized for a wizard's Continue button. */
.wy-alt-actions .wy-btn,
.wy-alt-btn {
    padding:   0.5rem 1rem;
    font-size: 0.85rem;
}

/* Find Alternative is the sourcing dropdown behind a label instead of the
   lightning bolt. .wy-source-wrap is inline-block-ish by default; inside the
   action row it must sit flush with the button beside it. */
.wy-alt-actions .wy-source-wrap {
    display: inline-flex;
}

/* ── Toast with an action (Use Library Copy → Undo) ──────────────────────
   The existing wyQueueToast()/wyShowQueuedToast() pair still handles the
   reload-based actions; this is the live variant, and unlike that one it is
   a real element with a class rather than a string of inline cssText,
   because it now has interactive contents to style.

   NOT role="status": an aria-live region containing a button announces as
   flat text and buries the action. The sentence goes through the shared
   wyAnnounce() region (inc/live-region.php) instead, leaving this a plain
   container with a genuinely focusable button. */
.wy-toast {
    position:      fixed;
    left:          50%;
    bottom:        1.6rem;
    transform:     translateX(-50%) translateY(0.4rem);
    display:       flex;
    align-items:   center;
    gap:           0.6rem;
    max-width:     min(30rem, calc(100vw - 2rem));
    padding:       0.7rem 0.8rem 0.7rem 0.95rem;
    background:    var(--wy-card-bg);
    border:        1px solid var(--wy-teal-bd);
    border-radius: 10px;
    box-shadow:    0 6px 22px rgba(0,0,0,.14);
    font-size:     0.86rem;
    line-height:   1.35;
    color:         var(--wy-text);
    opacity:       0;
    transition:    opacity .2s ease, transform .2s ease;
    z-index:       9999;
}

.wy-toast--in {
    opacity:   1;
    transform: translateX(-50%) translateY(0);
}

.wy-toast__icon {
    width:       17px;
    height:      17px;
    flex-shrink: 0;
    color:       var(--wy-teal);
}

.wy-toast__text strong {
    color: var(--wy-teal-dark);
}

.wy-toast__action {
    flex-shrink: 0;
    padding:     0.35rem 0.8rem;
    font-size:   0.8rem;
}

@media (prefers-reduced-motion: reduce) {
    .wy-toast { transition: opacity .2s ease; transform: translateX(-50%); }
    .wy-toast--in { transform: translateX(-50%); }
}

@media (max-width: 640px) {
    /* On a narrow viewport the indent costs more than the alignment buys. */
    .wy-altrow, .wy-hasalt { --wy-alt-indent: 0px; }
    .wy-alt-table { font-size: 0.8rem; }
    .wy-alt-actions .wy-btn,
    .wy-alt-btn { flex: 1 1 auto; justify-content: center; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Review — WyderNet Recommendations: "Why recommended" / "How these were found"
   ═══════════════════════════════════════════════════════════════════════
   Two blocks, rendered by inc/recommendation-why.php:

     .wy-recwhy    per item, inside the row's disclosure body
     .wy-recfound  once per recommendation box, above the item list

   Both sit on --wy-teal-light rather than the box's own --wy-amber-bg, so they
   read as an explanation OF the box rather than another item IN it.

   Contrast on --wy-teal-light (#e6f2ec), all measured, all clearing AA 4.5:1
   with little room to spare — nudge any of these three and re-measure:
   --wy-text-muted 4.60:1 (body), --wy-teal-icon 4.57:1 (labels),
   --wy-text 15.15:1 (the bolded subjects). Chip text is --wy-text-muted on
   --wy-card-bg, 5.28:1.  */

.wy-recwhy {
    background:    var(--wy-teal-light);
    border:        1px solid var(--wy-teal-bd);
    border-radius: var(--wy-radius-sm);
    padding:       0.5rem 0.65rem;
}
.wy-recwhy__label,
.wy-recfound__label,
.wy-recfound__sub {
    font-size:      0.66rem;
    font-weight:    700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color:          var(--wy-teal-icon);
}
.wy-recwhy__text {
    margin:    0.25rem 0 0;
    font-size: 0.78rem;
    color:     var(--wy-text-muted);
    /* .wy-exitem-expand's own 0.78rem/hint styling reaches here too; the
       explicit values keep the block readable if it is ever rendered outside
       that container. */
    line-height: 1.45;
}
/* The matched course subjects, bolded in place by wy_rec_emphasize(). Colour,
   not just weight: bold alone is nearly invisible at 0.78rem on a tint. */
.wy-recwhy__text strong {
    color:       var(--wy-text);
    font-weight: 600;
}

/* The recorded subjects, named outright. Smaller and lighter than the reason:
   it is evidence for the sentence above, not a second sentence. */
.wy-recwhy__matched {
    margin:    0.35rem 0 0;
    font-size: 0.72rem;
    color:     var(--wy-teal-icon);
}

/* The box-level panel. Full width of the box body, above the toolbar, so the
   first thing a librarian reads is how the list was produced. */
.wy-recfound {
    background:    var(--wy-teal-light);
    border:        1px solid var(--wy-teal-bd);
    border-radius: var(--wy-radius-sm);
    padding:       0.7rem 0.85rem;
    margin-bottom: 0.75rem;
}
.wy-recfound__label {
    display:     flex;
    align-items: center;
    gap:         0.4rem;
}
.wy-recfound__label svg {
    width:       14px;
    height:      14px;
    flex-shrink: 0;
}
.wy-recfound__sub {
    margin-top: 0.6rem;
}
.wy-recfound__chips {
    display:   flex;
    flex-wrap: wrap;
    gap:       0.35rem;
    margin-top: 0.35rem;
}
.wy-recfound__chip {
    display:       inline-block;
    padding:       0.15rem 0.5rem;
    border:        1px solid var(--wy-teal-bd);
    border-radius: 20px;
    background:    var(--wy-card-bg);
    font-size:     0.72rem;
    color:         var(--wy-text-muted);
}
/* The "+N" overflow is a count, not a keyword — no border, so it never reads
   as one more chip the librarian could look up. */
.wy-recfound__chip--more {
    border:     none;
    background: transparent;
}
.wy-recfound__text {
    margin:      0.3rem 0 0;
    font-size:   0.78rem;
    line-height: 1.45;
    color:       var(--wy-text-muted);
}

/* ── Faculty review (PRO-235) ────────────────────────────────────────────
   The faculty page IS the published student page (inc/student-guide-render.php,
   WY_STUDENT_VIEW_FACULTY), so it needs no list, section or row rules of its
   own — it inherits .wy-exgroup/.wy-exitem (default layout) or .wn-card
   (compact) unchanged. What is left here is the three things only this view
   adds: the banner, the per-row comment control's "sent" note, and the dialog
   further down.

   The rules deleted with the bespoke list (.wy-fr-section, .wy-fr-item*,
   .wy-fr-actions, .wy-fr-count, .wy-fr-sub, .wy-fr-suggest*, .wy-fr-iconbtn)
   had one consumer between them; the comment above claiming they were shared
   with the librarian's resolve screen was stale — that screen is .wy-fac-*. */

/* ── PRO-237: content type filter on the Recommendations boxes ───────────
   Same control as the Needs Evaluation tabs and the "Show" radios — two
   controls that behave identically must not look different, and the radio
   gives arrow-key navigation and the roving tabindex from the browser instead
   of hand-rolled JS.

   Two-class selector for the same reason .wy-revmode--eval uses one:
   `.wy-revmode--rectype` alone ties with `.wy-revmode` on specificity and wins
   only on source order, which is the arrangement that twice deleted a focus
   ring in this codebase. */
.wy-revmode.wy-revmode--rectype {
    /* Sits INSIDE the box body, under the bulk bar, so it lines up with the
       rows it filters rather than with the section heading. Lighter than the
       eval variant's card treatment: that one is a top-level view switch, this
       one narrows a list already in front of you, and giving it the same weight
       would make two different jobs look like one. */
    margin:        0 0 0.9rem;
    padding:       0.6rem 0.8rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-bg);

    column-gap:    var(--wy-rectype-gap);
    row-gap:       0.5rem;
    --wy-rectype-gap: 1.4rem;
}

@media (max-width: 560px) {
    .wy-revmode.wy-revmode--rectype {
        --wy-rectype-gap: 0.95rem;
        padding: 0.55rem 0.65rem;
    }
}

/* The per-type count, beside its own label. Tabular figures so the row does
   not shift as a librarian deletes recommendations under it. */
.wy-revmode__n {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color:       var(--wy-text-muted);
}

.wy-revmode__opt input:checked ~ .wy-revmode__n { color: var(--wy-teal-dark); }

/* The live region only: the counts are already visible on every chip, so
   repeating "Showing 7 of 33" on screen would be the same fact twice. It exists
   so a screen-reader user gets told the list changed length, which is the one
   thing hiding rows does not announce on its own. */
.wy-rec-filter-status {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    clip-path: inset(50%);
    overflow: hidden; white-space: nowrap;
}

/* Restored at higher specificity so a later rule written for :hover cannot
   delete it on source order — the failure this stylesheet has already had
   twice, on the sign-in inputs and the public guide's section headers. */
.wy-revmode.wy-revmode--rectype .wy-revmode__opt input:focus-visible {
    outline: 2px solid var(--wy-teal);
    outline-offset: 2px;
}

/* ── PRO-235: faculty comments ───────────────────────────────────────────
   The amber marker on a Review row. A BUTTON, because it opens a dialog — it
   used to be tempting to make it a span with a click handler, which gives a
   keyboard user nothing to tab to. */
.wy-fac-badge {
    display: inline-flex; align-items: center; gap: .4rem;
    margin-left: .55rem; padding: .1rem .5rem .1rem .15rem;
    border: 0; border-radius: 999px; background: transparent;
    font: inherit; font-size: .82rem; color: var(--wy-text-muted);
    cursor: pointer; vertical-align: middle;
}
.wy-fac-badge__dot {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--wy-amber); color: #fff;
    font-size: .72rem; font-weight: 800; line-height: 1;
}
/* DECLARED, NOT ASSUMED — third time in this file (see .wy-icon-btn's note on
   padding and .wy-fr-sent's on colour). The parent theme restyles every
   <button> and its :hover sets a white colour, so "Faculty comment" vanished
   into the amber pill the moment you pointed at it. Hover changes ONE thing
   here: the underline. */
.wy-fac-badge:hover,
.wy-fac-badge:focus,
.wy-fac-badge:focus-visible,
.wy-fac-badge:active {
    color:      var(--wy-text-muted);
    background: transparent;
    box-shadow: none;
    transform:  none;
    opacity:    1;
}
.wy-fac-badge:hover .wy-fac-badge__lbl { text-decoration: underline; }

/* PRO-267 — the two halves read as one object. The border and radius live on
   the wrapper so the divider between them is a hairline and not two adjacent
   edges; each half keeps its own hit area and its own accessible name. */
.wy-fac-ctl {
    display:       inline-flex;
    align-items:   center;
    margin-left:   .55rem;
    border:        1px solid var(--wy-amber-border, #f0d9ab);
    border-radius: 999px;
    background:    var(--wy-amber-bg, #fdf8ee);
    overflow:      hidden;
    vertical-align: middle;
}
/* Same trap as .wy-revmode__opt[hidden] above, and this one is worse: the
   control is hidden from JS the moment its last comment is resolved, so
   without this the row kept offering "Resolve" for a comment that was already
   settled — and pressing it would 404 on an id the server had closed. */
.wy-fac-ctl[hidden] { display: none; }

.wy-fac-ctl .wy-fac-badge { margin-left: 0; border-radius: 0; }
.wy-fac-ctl__rule { width: 1px; align-self: stretch; background: var(--wy-amber-border, #f0d9ab); }

/* Green, because it is the only affirmative action in an amber control and the
   colour is the fastest way to tell the two halves apart. Never colour ALONE:
   the tick and the word both say what it does. */
.wy-fac-resolve {
    display:     inline-flex;
    align-items: center;
    gap:         .28rem;
    padding:     .1rem .6rem .1rem .45rem;
    border:      0;
    background:  transparent;
    font:        inherit;
    font-size:   .82rem;
    font-weight: 600;
    color:       var(--wy-teal-dark, #0f6f4c);
    cursor:      pointer;
}
.wy-fac-resolve__tick { font-weight: 800; }
/* The other half of the same control, and the same rule: the green has to
   survive the pointer, because it is what tells the two halves apart. */
.wy-fac-resolve:hover,
.wy-fac-resolve:focus,
.wy-fac-resolve:focus-visible,
.wy-fac-resolve:active {
    color:      var(--wy-teal-dark, #0f6f4c);
    background: transparent;
    box-shadow: none;
    transform:  none;
    opacity:    1;
}
.wy-fac-resolve:hover .wy-fac-resolve__lbl { text-decoration: underline; }
.wy-fac-resolve[disabled] { opacity: .55; cursor: default; }

/* Higher specificity than the :hover rules above, for the same reason the
   sibling rule says: this stylesheet has lost a focus ring to source order
   twice, and an outline inside an overflow:hidden wrapper needs the inset
   style or it is clipped away. */
.wy-exitem .wy-fac-resolve:focus-visible,
.wy-fac-ctl .wy-fac-resolve:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: -2px;
}
.wy-fac-ctl .wy-fac-badge:focus-visible { outline: 2px solid var(--wy-teal); outline-offset: -2px; border-radius: 0; }
/* Higher specificity than any later :hover rule — this stylesheet has lost a
   focus ring to source order twice. */
.wy-exitem .wy-fac-badge:focus-visible { outline: 2px solid var(--wy-teal); outline-offset: 2px; border-radius: 999px; }

/* The vertical rule separating the comment filter from the Show radios. */
.wy-revmode__rule { width: 1px; align-self: stretch; background: var(--wy-border); margin: 0 .2rem; }
.wy-facfilter { gap: .45rem; }

/* Dialogs — one shared shell for the faculty modal and the librarian's. */
.wy-fr-modal, .wy-fac-modal, .wy-fx-modal, .wy-mm-modal { position: fixed; inset: 0; z-index: 1000; }
.wy-fr-modal[hidden], .wy-fac-modal[hidden], .wy-fx-modal[hidden], .wy-mm-modal[hidden] { display: none; }
.wy-fr-modal__backdrop, .wy-fac-modal__backdrop, .wy-fx-modal__backdrop, .wy-mm-modal__backdrop { position: absolute; inset: 0; background: rgba(17,24,39,.55); }
.wy-fr-modal__panel, .wy-fac-modal__panel, .wy-fx-modal__panel, .wy-mm-modal__panel {
    position: relative; margin: 8vh auto 0; max-width: 40rem; width: calc(100% - 2rem);
    background: var(--wy-card-bg); border-radius: var(--wy-radius);
    padding: 1.6rem 1.7rem; box-shadow: var(--wy-shadow); text-align: left;
    max-height: 80vh; overflow: auto;
}
.wy-fr-modal__x, .wy-fac-modal__x, .wy-fx-modal__x, .wy-mm-modal__x {
    position: absolute; top: .9rem; right: .9rem;
    border: 0; background: transparent; cursor: pointer; color: var(--wy-text-muted); padding: .25rem;
}
.wy-fr-modal__x:focus-visible, .wy-fac-modal__x:focus-visible, .wy-fx-modal__x:focus-visible, .wy-mm-modal__x:focus-visible { outline: 2px solid var(--wy-teal); outline-offset: 2px; }
.wy-fr-modal__title, .wy-fac-modal__title, .wy-fx-modal__title, .wy-mm-modal__title { margin: 0 2rem .3rem 0; font-size: 1.15rem; font-weight: 700; color: var(--wy-text); }
.wy-fr-modal__sub { margin: 0 0 1rem; color: var(--wy-text-muted); font-size: .9rem; }
.wy-fr-modal__err { margin: .6rem 0 0; color: #b3261e; font-size: .88rem; }
.wy-fr-modal__actions, .wy-fac-modal__actions, .wy-fx-modal__actions { display: flex; gap: .6rem; justify-content: flex-end; flex-wrap: wrap; margin-top: 1.1rem; }

.wy-fac-modal__k { margin: 1rem 0 .2rem; font-size: .72rem; font-weight: 700; letter-spacing: .06em; color: var(--wy-text-hint); }
.wy-fac-modal__v { margin: 0; font-weight: 600; color: var(--wy-text); }
.wy-fac-modal__mail { display: block; font-size: .9rem; }
.wy-fac-modal__when { margin: .15rem 0 0; font-size: .85rem; color: var(--wy-text-muted); }
.wy-fac-modal__body { margin: 0; color: var(--wy-text); white-space: pre-wrap; }
.wy-fac-modal__note {
    margin: 1rem 0 0; padding-top: .8rem; border-top: 1px solid var(--wy-border);
    font-size: .85rem; color: var(--wy-text-muted);
}

/* The outcome line, shown only when the Resolve button is withdrawn. Without
   it an already-resolved comment would open stating no outcome at all and read
   as though nothing had been done about it. */
.wy-fac-modal__resolved {
    margin: .9rem 0 0;
    font-size: .85rem;
    font-weight: 600;
    color: var(--wy-teal-dark);
}

/* ── Faculty feedback history (PRO-235) ──────────────────────────────────
   The record of every faculty review round on a collection. It exists because
   a resolved comment is invisible on Review by design — the badge renders for
   a pending comment and nothing else — so once the last one was resolved there
   was no screen in the product where the exchange could be read back. */

.wy-ffround__head {
    display: flex; flex-wrap: wrap; align-items: baseline;
    justify-content: space-between; gap: .5rem 1rem;
    margin-bottom: .9rem;
}
.wy-ffround__title { margin: 0; font-size: 1.02rem; }
.wy-ffround__meta  { font-size: .84rem; color: var(--wy-text-muted); }

/* The table is the one thing on this screen allowed to outrun the page, and
   only inside its own scroller — the page body must never scroll sideways. */
.wy-fftable-wrap { overflow-x: auto; }

.wy-fftable {
    width: 100%; border-collapse: collapse; font-size: .9rem;
}
.wy-fftable th {
    text-align: left; padding: .5rem .7rem .55rem;
    border-bottom: 1px solid var(--wy-border);
    font-size: .72rem; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; color: var(--wy-text-hint);
    white-space: nowrap;
}
.wy-fftable td {
    padding: .7rem; border-bottom: 1px solid var(--wy-border);
    vertical-align: top;
}
.wy-fftable tr:last-child td { border-bottom: 0; }

/* A recessed fill rather than a border or an accent rail: the row is inside a
   white card already, and a second bordered object would read as a card
   floating in a card. Tint is not the only cue — the status cell carries its
   own glyph and word. */
.wy-ffrow--open > td { background: var(--wy-bg); }

.wy-ffrow__title { display: block; font-weight: 600; color: var(--wy-text); }
/* The linked variant. Underline on hover only, per the house rule that a
   rollover adds an underline rather than changing the colour — the parent
   theme turns link text white on hover, which is invisible on this card. */
.wy-ffrow__title--link { text-decoration: none; cursor: pointer; }
.wy-ffrow__title--link:hover,
.wy-ffrow__title--link:focus { color: var(--wy-text); text-decoration: underline; }
/* Restored at higher specificity: the :hover rule above touches this element's
   decoration, and a later parent-theme rule has twice taken the ring with it. */
.wy-ffrow .wy-ffrow__title--link:focus-visible {
    outline: 2px solid var(--wy-teal); outline-offset: 2px; border-radius: 3px;
}
.wy-ffrow__sub   { display: block; font-size: .84rem; color: var(--wy-text-muted); }
.wy-ffrow__gone  { color: var(--wy-text-hint); font-style: italic; font-weight: 400; }

/* The comment is the control: it is what opens the window, so it has to be a
   real button rather than a click handler on the cell — that gives a keyboard
   user something to tab to. Styled as text so the table still reads as a
   table. */
.wy-ffrow__comment {
    width: 100%; margin: 0; padding: 0;
    background: none; border: 0; border-radius: var(--wy-radius-sm);
    font: inherit; color: var(--wy-text); text-align: left; cursor: pointer;
    /* Same reason as .wy-btn: this is a control, and the full comment is
       selectable in the window it opens. */
    -webkit-user-select: none; user-select: none;
    /* Two lines, with the full text in the window a click away. An unclamped
       comment sets the row height for the whole table and the list stops being
       scannable, which is the only thing the list is for. The box display IS
       the clamp — declared once, not after a `display: block` it would
       immediately override. */
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
}
.wy-ffrow__comment:hover { color: var(--wy-teal-dark); text-decoration: underline; }

/* Higher specificity than the :hover rule above, so source order can never
   decide it — a rule written for hover has twice deleted a focus ring here. */
.wy-fftable .wy-ffrow__comment:focus-visible {
    outline: 2px solid var(--wy-teal);
    outline-offset: 2px;
    text-decoration: underline;
}

.wy-ffstatus { white-space: nowrap; }

/* At phone width the table becomes one block per comment, each cell labelled
   from its own data-th — a four-column table at 400px is a horizontal scroll
   through a header row the reader cannot see. */
@media (max-width: 640px) {
    .wy-fftable thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    .wy-fftable, .wy-fftable tbody, .wy-fftable tr, .wy-fftable td { display: block; width: 100%; }
    .wy-fftable tr {
        border: 1px solid var(--wy-border); border-radius: var(--wy-radius-sm);
        padding: .2rem .1rem; margin-bottom: .7rem;
    }
    .wy-fftable td { border-bottom: 0; padding: .45rem .7rem; }
    .wy-fftable td::before {
        content: attr(data-th);
        display: block; margin-bottom: .15rem;
        font-size: .68rem; font-weight: 700; letter-spacing: .06em;
        text-transform: uppercase; color: var(--wy-text-hint);
    }
    .wy-ffrow__comment { -webkit-line-clamp: 4; }
}

/* Faculty screen: the comment control on a shared student row. The BUTTON is
   .wy-icon-btn — the same control the row's own "Open" action uses — so it
   cannot drift in size or weight from the thing it sits beside; only the
   teal tint on hover is this view's. */
.wy-fr-comment-btn:hover { color: var(--wy-teal-dark); border-color: var(--wy-teal-bd); }
/* A rule written for :hover has twice deleted the keyboard focus ring in this
   codebase, both times by also matching :focus-visible and winning on source
   order. Declared at higher specificity so a later sheet cannot decide it. */
.wy-fr .wy-fr-comment-btn:focus-visible { outline: 2px solid var(--wy-teal); outline-offset: 2px; }

/* A button, so the comment behind it is reachable by keyboard as well as by
   hover — but it is a status first, so it keeps the flat look it had as a span
   and picks up an underline only when it is being pointed at. */
.wy-fr-sent {
    font-size:   .78rem;
    color:       var(--wy-teal-dark);
    font-weight: 600;
    white-space: nowrap;
    border:      0;
    background:  transparent;
    padding:     0;
    font-family: inherit;
    cursor:      pointer;
}
/* DECLARED, NOT ASSUMED — the same lesson .wy-icon-btn records two rules up.
   The parent theme restyles every <button>, and its :hover sets a white
   colour and a background of its own. This was a <span> until it needed to be
   keyboard-reachable; as a <button> it inherited all of that, and "Comment
   sent" turned white-on-white the moment you pointed at it. Every state it can
   be in now says what it looks like. */
.wy-fr-sent:hover,
.wy-fr-sent:focus,
.wy-fr-sent:focus-visible,
.wy-fr-sent:active {
    color:      var(--wy-teal-dark);
    background: transparent;
    box-shadow: none;
    transform:  none;
    opacity:    1;
}
.wy-fr-sent:hover { text-decoration: underline; text-underline-offset: 2px; }
/* Higher specificity than the :hover above — this stylesheet has lost a focus
   ring to source order twice. */
.wy-fr .wy-fr-sent:focus-visible { outline: 2px solid var(--wy-teal); outline-offset: 2px; }

/* What they actually wrote. Full width of the status column and left-aligned,
   because a quoted sentence ragged-right against the row's right edge is
   unreadable at 0.8rem. */
.wy-fr-sentnote {
    flex-basis:  100%;
    margin:      .3rem 0 0;
    font-size:   .8rem;
    line-height: 1.45;
    color:       var(--wy-text-muted);
    text-align:  left;
    white-space: pre-wrap;
}
.wy-fr-sentnote[hidden] { display: none; }
/* On the default layout the note drops onto its own line UNDER the row's
   action icons. Sharing their line pushed them left by the width of the words,
   so a commented row's controls no longer lined up with the rows below it —
   the one row you had just acted on became the odd one out. Scoped to .wy-fr
   because .wy-exitem__status is shared with Materials and Collection Health. */
.wy-fr .wy-exitem__status { flex-wrap: wrap; justify-content: flex-end; }
.wy-fr .wy-exitem__status .wy-fr-sent { flex-basis: 100%; text-align: right; }
/* The row keeps saying a comment was sent — the dialog closes, and a toast
   would be gone before someone scrolling a long list looked back at the row. */
.wy-fr-commented { background: var(--wy-bg); }

/* The banner sits ABOVE a page that owns its own full-width bands (default
   layout) or its own .wn-root shell (compact), so it cannot live inside either
   container. It BORROWS the layout's own inner element instead of declaring a
   width of its own — .wy-guide-inner is 1200px and .wn-page is
   var(--wn-max-width), and a third number here would line up with neither the
   first time one of them changed. These two rules only trim the vertical
   padding those containers carry for a page body. */
.wy-fr-bannerband .wy-guide-inner { padding-bottom: 0; }
.wy-fr-bannerband .wn-page { padding-top: 1.4rem; padding-bottom: 0; }
.wy-fr-banner {
    display: flex; gap: .6rem; align-items: center;
    padding: .9rem 1.1rem;
    border: 1px solid var(--wy-border); border-left: 3px solid var(--wy-teal);
    border-radius: var(--wy-radius-sm); background: var(--wy-card-bg);
    color: var(--wy-text);
}
.wy-fr-banner > svg { flex-shrink: 0; }

/* ── The control you just clicked ────────────────────────────────────────
   Most actions here are a full page load costing 2–3 seconds, and the only
   feedback was the browser tab's throbber — which says something is happening
   but not WHICH control is doing it. Driven by assets/wy-click-busy.js.

   Drawn as a bar travelling inside the control's bottom edge, via ::after, so
   it adds no element and changes no layout: an icon-only button, a nav link
   and a wide primary button all take it without reflowing. The earlier idea —
   swapping the label for a spinner — moves text and resizes the button under
   the cursor at the exact moment someone might click again. */
.wy-clicked {
    /* No cursor change: the bar is the indication. A progress cursor competes
       with it, and on a control that is about to be replaced by a new page it
       reads as the pointer being stuck rather than as work happening. */
    position: relative;
}

.wy-clicked::after {
    content:       "";
    position:      absolute;
    left:          8%;
    bottom:        2px;
    width:         26%;
    height:        2px;
    border-radius: 2px;
    background:    currentColor;
    opacity:       .55;
    pointer-events: none;
    animation:     wy-clicked-slide .85s ease-in-out infinite alternate;
}

@keyframes wy-clicked-slide {
    from { transform: translateX(0); }
    to   { transform: translateX(215%); }
}

/* A row action sits inside a bordered icon button where a bar at the very
   bottom collides with the border radius; lift it clear. */
.wy-iconbtn.wy-clicked::after {
    left:   18%;
    width:  64%;
    bottom: 4px;
}

/* Reduced motion keeps the INDICATION and drops only the movement — the
   point of the bar is that the control is working, and that is exactly what
   someone who turned off animation still needs to know. */
@media (prefers-reduced-motion: reduce) {
    .wy-clicked::after {
        animation: none;
        left:      8%;
        width:     84%;
        opacity:   .4;
    }
}


/* ── Fulfillment: the Mark-received dialog's own bits ────────────────────
   It borrows the shared modal shell above; only the item line, the hint and
   the error are specific to it. The form it replaces lived in the row's
   actions column, where a url field and two buttons wrapped the label onto
   two lines and squeezed the row beside it. */
.wy-fx-modal__sub {
    margin:      0 0 1.1rem;
    font-weight: 600;
    color:       var(--wy-text);
    font-size:   0.95rem;
}
.wy-fx-modal__hint {
    margin:    0.45rem 0 0;
    font-size: 0.8rem;
    color:     var(--wy-text-hint);
}
.wy-fx-modal__err {
    margin:    0.7rem 0 0;
    font-size: 0.85rem;
    color:     #b3261e;
}

/* ── Materials list rows (PRO-239) ─────────────────────────────────────
   A row highlight on hover, on the two tables whose rows lead somewhere:
   the uploaded lists and the resources on one list. Scoped to those two
   classes rather than to .wy-fftable, which the faculty-feedback table also
   uses — its rows are not navigable, and a hover response on a row that
   does nothing when clicked is a promise the screen does not keep. */
.wy-cm-row:hover > td,
.wy-cml-row:hover > td {
    background: var(--wy-bg);
}

/* The one cell in a resource row that leads out of this screen. It carries
   an arrow as well as a colour, so it is identifiable as a link without
   relying on hue (WCAG 1.4.1) — the same reason every badge carries a glyph. */
.wy-cml-link {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    color: var(--wy-teal-dark);
    font-weight: 600;
    text-decoration: none;
}
.wy-cml-link:hover { text-decoration: underline; }

/* The course cell is two stacked lines plus the arrow, so it aligns to the
   TOP rather than centring the arrow against a two-line block, and it keeps
   the cell's own colour — the code and course name are the row's identity,
   not a link label, and colouring them teal would make the course read as
   the less important of the two. The arrow carries the affordance. */
.wy-cml-link--course {
    align-items: flex-start;
    color: inherit;
    font-weight: inherit;
}
.wy-cml-link--course .wy-ffrow__title { color: var(--wy-text); }
.wy-cml-link--course:hover .wy-ffrow__title { color: var(--wy-teal-dark); }
.wy-cml-link--course svg { margin-top: .15rem; }
.wy-cml-link svg { flex-shrink: 0; opacity: .75; }

/* Higher specificity than the :hover rule above, so source order cannot
   decide it — this stylesheet has lost a focus ring that way twice. */
.wy-cml-row .wy-cml-link:focus-visible {
    outline: 2px solid var(--wy-teal);
    outline-offset: 2px;
    border-radius: var(--wy-radius-sm);
}

/* ── Review: search inside the controls bar (PRO-239 follow-on) ───────
   It lives in .wy-revmode with the display radios and the faculty filter —
   everything in that row changes what is on screen. margin-left:auto puts
   it at the right end of whatever else the row happens to contain, which on
   a collection with no assignments and no comments is nothing at all.

   ── Why the row aligns to the TOP when the search is in it ─────────────
   With all four controls present the row is wider than the card, and
   centring them meant the faculty label held its one long line, forced the
   search onto a second row, and then centred THAT against a mostly empty
   line — a tall box with the search stranded at the bottom right.

   Aligning to the top and letting the long label wrap keeps everything on
   one line of controls: the label becomes two short lines, the search sits
   beside it, and their first lines share a baseline. */
/* Two classes, not one: .wy-revmode alone would tie on specificity with the
   base rule and win only on source order — the arrangement that has twice
   deleted a focus ring in this file. The modifier also keeps this off
   .wy-revmode--eval, the Needs Evaluation tabs, which are a single short
   line and are correct centred. */
.wy-revmode.wy-revmode--main {
    /* Centred, like the Needs Evaluation row (2026-09-30): top-aligned, the
       SHOW label, the radios and the faculty links sat at visibly different
       heights. The faculty label still wraps (max-width below), so the row
       stays one line of controls; it is now centred against it. */
    align-items: center;
}

/* The one label long enough to need it. Capped so it breaks after roughly
   half its length rather than wrapping a single trailing word, and its
   checkbox pinned to the first line instead of floating to the middle of a
   two-line block. */
.wy-revmode__opt.wy-facfilter {
    align-items: flex-start;
    max-width: 17rem;
}
.wy-revmode__opt.wy-facfilter input { margin-top: .18rem; }

/* The separator was sized for a one-line row; against a two-line label it
   would stop short of the taller neighbour. */
.wy-revmode--main .wy-revmode__rule { align-self: stretch; }

.wy-revmode__search {
    margin-left: auto;
}
.wy-revmode__search .wy-dash-search input {
    /* Slightly tighter than the dashboard's 230px: this row already carries
       two radios and a checkbox, and the box has to survive beside them
       before wrapping. */
    width: 200px;
}

/* The live region, directly under the controls bar. It occupies no space
   when empty, so an unused search does not push the first box down. */
.wy-revsearch__status {
    margin: -.4rem 0 .9rem;
    text-align: right;
    font-size: .84rem;
    color: var(--wy-text-muted);
}
.wy-revsearch__status:empty { display: none; }

@media (max-width: 720px) {
    /* Once the row wraps, the search takes the full width rather than
       sitting alone against the right edge of its own line. */
    .wy-revmode__search { margin-left: 0; width: 100%; }
    .wy-revmode__search .wy-dash-search input { width: 100%; }
    .wy-revsearch__status { text-align: left; }
}

/* ── Materials Lists: no adapter configured ────────────────────────────
   An informational card, not an error: nothing has gone wrong, this
   customer has simply not been set up yet. So it takes the blue
   informational tint rather than amber (which means "needs a human
   decision") or red (destructive and real errors). */
.wy-noadapter {
    display: flex;
    align-items: flex-start;
    gap: .9rem;
    padding: 1.3rem 1.5rem;
    /* .wy-card centres its content; this one is a sentence beside an icon and
       reads as left-aligned prose. */
    text-align: left;
    background: var(--wy-blue-bg);
    /* .wy-card draws no border at all, so border-color alone would have done
       nothing — the width and style have to be stated here. */
    border: 1px solid var(--wy-blue-bd);
    /* The card that follows (the list, or its empty state) was flush against
       this one. */
    margin-bottom: 1.1rem;
}
.wy-noadapter__icon {
    flex-shrink: 0;
    color: var(--wy-blue);
}
.wy-noadapter__icon svg { width: 22px; height: 22px; display: block; }
.wy-noadapter__head {
    margin: 0 0 .3rem;
    font-size: 1rem;
    color: var(--wy-text);
}
.wy-noadapter__body {
    margin: 0;
    color: var(--wy-text-muted);
    max-width: 44em;
}

/* ── A format failure, laid out so it can be acted on ─────────────────────
   The card used to hold one run-on sentence: "This list could not be read.
   Column problem (course): no column identifies the course — … Columns seen:
   #, Week of, D/B, …". Every fact was in there and none of them was findable,
   least of all the list of the file's own columns, which is the part a
   customer recognises their wrong export from.

   It is a <dl> now — label, value — because these really are four separate
   answers to four separate questions, and a definition list is what says so
   to a screen reader as well as to the eye. Deliberately NOT a .wy-alert:
   this is the card's whole content, not a banner above it, and a red panel
   the height of the viewport reads as a system fault rather than as a file
   that needs a column adding. */
.wy-fmterr__head {
    margin:      0 0 .45rem;
    font-size:   1.05rem;
    font-weight: 600;
    color:       var(--wy-text);
}

.wy-fmterr__why {
    margin:    0 0 1rem;
    color:     var(--wy-text-muted);
    font-size: .93rem;
    line-height: 1.55;
    max-width: 62ch;
}

.wy-fmterr {
    display:               grid;
    /* The label column sizes to its longest label and stops. A fixed width
       either clips "Columns found in your file" or leaves a hole beside
       "Where to look". */
    grid-template-columns: max-content minmax(0, 1fr);
    gap:                   .5rem 1.5rem;
    margin:                0 0 1.2rem;
    padding:               .9rem 1.1rem;
    background:            var(--wy-bg);
    border-radius:         8px;
    font-size:             .9rem;
    line-height:           1.55;
}

.wy-fmterr dt {
    margin:      0;
    color:       var(--wy-text-hint);
    font-weight: 600;
    /* Labels, not headings — the size step is what keeps them from competing
       with the values they introduce. */
    font-size:   .82rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    padding-top: .08rem;
}

.wy-fmterr dd {
    margin:     0;
    color:      var(--wy-text);
    /* A 40-column export lists 40 headings; it wraps rather than scrolling
       the page sideways. */
    overflow-wrap: anywhere;
}

/* The cell reference is the one value the customer retypes into Excel's Name
   Box, so it is set in the same monospace the comparison keys use and given
   a little ground of its own. */
.wy-fmterr__cell {
    font-family:   ui-monospace, Menlo, monospace;
    font-weight:   600;
    background:    #fef2f2;
    color:         #b91c1c;
    border:        1px solid #fecaca;
    border-radius: 4px;
    padding:       .05rem .35rem;
}

.wy-fmterr__example {
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    gap:         .6rem 1rem;
    margin:      0;
}

.wy-fmterr__hint {
    flex:      1 1 22ch;
    color:     var(--wy-text-hint);
    font-size: .86rem;
    line-height: 1.5;
}

/* At phone width the label column would leave the values four characters
   wide, so the pairs stack instead. */
@media (max-width: 560px) {
    .wy-fmterr {
        grid-template-columns: minmax(0, 1fr);
        gap: .15rem;
    }
    .wy-fmterr dd { margin-bottom: .7rem; }
    .wy-fmterr dd:last-child { margin-bottom: 0; }
}


/* ── Mismatch window (existing vs incoming) ──────────────────────────────
   Joins the shared .wy-f*-modal shell above; everything below is only what a
   HEADER / SCROLLING BODY / FOOTER needs, which neither existing dialog system
   has — both scroll the whole panel, so a long list would take the progress
   bar and the Apply button off screen with it. */
/* ── The mismatch dialog ──────────────────────────────────────────────────
   Wider than the other four modals on purpose: they ask ONE question about one
   thing, and this one lays two whole versions of a record side by side. At
   46rem the two cards stacked and a librarian had to scroll between the values
   they are comparing, which is the one thing the screen exists to make easy.

   min(), not a bare max-width: the cap is what keeps a long course title from
   running to 2000px on a wide monitor, and 94vw is what keeps the panel clear
   of the viewport edge below that. */
.wy-mm-modal .wy-mm-modal__panel {
    display: flex; flex-direction: column; padding: 0;
    width: min(64rem, 94vw); max-width: min(64rem, 94vw);
    margin-top: 6vh; max-height: 86vh;
}
.wy-mm-modal__head { padding: 1.5rem 1.7rem 1rem; border-bottom: 1px solid var(--wy-border); }
.wy-mm-modal__body { overflow: auto; flex: 1 1 auto; padding: .9rem 1.7rem 1.2rem; }
.wy-mm-modal__foot {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    padding: 1rem 1.7rem 1.3rem; border-top: 1px solid var(--wy-border);
}
.wy-mm-modal__sub { margin: 0 0 .9rem; color: var(--wy-text-muted); font-size: .9rem; }
.wy-mm-modal__err { margin: 0; color: #b3261e; font-size: .85rem; flex-basis: 100%; }
/* Pushes the two buttons to the right without a spacer element. */
.wy-mm-foot__note { font-size: .8rem; color: var(--wy-text-hint); margin-right: auto; }

/* .wy-progress centres itself and caps at 340px for the pending screen; in a
   dialog header it should sit left and use the width it has. */
.wy-mm-progress { max-width: none; margin: 0; }
.wy-mm-progress .wy-progress__label { justify-content: flex-start; }

.wy-mm-item { border-bottom: 1px solid var(--wy-border); }
.wy-mm-item:last-child { border-bottom: none; }
.wy-mm-val--absent { color: var(--wy-text-hint); font-style: italic; }

/* ── One version per card, chosen by clicking it ──────────────────────────
   Two columns while both fit, stacked below that. auto-fit + minmax rather
   than a media query: what decides is whether a VERSION still reads at that
   width, which depends on the panel, not on the device. */
.wy-mm-srcs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
    gap: .9rem;
    margin: .2rem 0 1rem;
}

.wy-mm-src {
    display: block; cursor: pointer;
    border: 1px solid var(--wy-border); border-radius: var(--wy-radius);
    background: var(--wy-card-bg); padding: .85rem 1rem 1rem;
    transition: border-color .12s ease, background-color .12s ease;
}
.wy-mm-src:hover { border-color: var(--wy-teal); }

/* The chosen one. Tint AND a heavier border AND the button's own state change,
   so the selection is never carried by colour alone (WCAG 1.4.1). */
.wy-mm-src--kept {
    border-color: var(--wy-teal); border-width: 2px;
    /* The extra border pixel comes out of the padding, or the card grows by 2px
       when chosen and the row jumps. */
    padding: calc(.85rem - 1px) calc(1rem - 1px) calc(1rem - 1px);
    background: var(--wy-teal-light);
}

/* The radio is visually hidden, so the ring has to be drawn on the card it
   stands for — otherwise a keyboard user moving between the two versions sees
   nothing move. */
.wy-mm-src:has(input:focus-visible) {
    outline: 2px solid var(--wy-teal); outline-offset: 2px;
}

.wy-mm-src__head {
    display: flex; align-items: center; gap: .45rem;
    padding-bottom: .6rem; margin-bottom: .6rem;
    border-bottom: 1px solid var(--wy-border);
}
.wy-mm-src__icon { flex: 0 0 auto; color: var(--wy-text-muted); }
.wy-mm-src__name {
    font-weight: 600; font-size: .92rem; color: var(--wy-text);
    /* A filename is arbitrary and can be long; it must not push the action off
       the card. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Styled as a button, and deliberately NOT one: it sits inside the <label>
   that owns the radio, where nested interactive content is invalid. */
.wy-mm-keep {
    margin-left: auto; flex: 0 0 auto;
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .3rem .6rem; border-radius: 999px;
    border: 1px solid var(--wy-teal); background: transparent;
    color: var(--wy-teal); font-size: .78rem; font-weight: 600; white-space: nowrap;
}
.wy-mm-src--kept .wy-mm-keep { background: var(--wy-teal); color: #fff; }

.wy-mm-fields { display: grid; gap: .55rem; }
.wy-mm-field { display: block; }
.wy-mm-field__label {
    display: block; font-size: .68rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--wy-text-hint); margin-bottom: .1rem;
}
.wy-mm-field__val { display: block; font-size: .9rem; color: var(--wy-text); line-height: 1.4; }

/* The Review notification. .wy-alert is display:flex and centres its children,
   which puts the button beside the text; the text takes the room. */
.wy-mm-notice { align-items: flex-start; gap: .7rem; }
.wy-mm-notice__text { flex: 1 1 auto; }
.wy-mm-notice .wy-btn { flex: 0 0 auto; }
.wy-mm-notice svg { flex-shrink: 0; margin-top: .15rem; }

/* The parent theme styles ANY programmatically-focused element like a form
   field: `[tabindex="-1"]:focus, input:read-write:focus, select:focus,
   textarea:focus { outline:0; box-shadow:0 0 3px 0 var(--nv-secondary-accent) }`.

   A dialog's heading is given tabindex="-1" and focused on open so a screen
   reader announces what just appeared — it is not interactive, and the ring
   made it read as an editable field. Measured in the browser: a 3px accent
   shadow spanning the full width of the <h2>, which is exactly what a text
   input looks like on this screen.

   The shadow is dropped, the focus is kept. Nothing is lost: the heading takes
   no keyboard input, and every real control in the dialog keeps its own
   focus-visible ring. Scoped to the dialog titles so no other tabindex="-1"
   target is affected. Same class of parent-theme-cascade problem as the two
   :hover rules that deleted focus rings — that one removed an indicator, this
   one added a misleading one. */
.wy-mm-modal__title:focus,
.wy-fr-modal__title:focus,
.wy-fac-modal__title:focus,
.wy-fx-modal__title:focus { box-shadow: none; }

/* ── A course code that already has a collection ──────────────────────────
   The two answers to "a collection already exists for this code", on the
   upload screen. A row of buttons rather than a dialog: nothing is lost by
   leaving the page (the files are held server-side), so trapping focus would
   be asserting a stake the situation does not have.

   Wraps at phone width, with the row gap declared separately from the column
   gap — the column gap separates two buttons, the row gap only applies once
   they stack, where a 0.75rem horizontal step would be a hole. */
.wy-join-choice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: .75rem;
    row-gap: .6rem;
    margin: 1rem 0 0;
}
/* Same treatment for the other collection's name in the Edit Course Details
   code-clash notice (part-app-collections.php). */
.wy-dash-merge a.wy-dash-merge__open,
.wy-dash-merge a.wy-dash-merge__open:visited,
.wy-dash-merge a.wy-dash-merge__open:hover { color: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
.wy-dash-merge a.wy-dash-merge__open:hover { text-decoration-thickness: 2px; }
.wy-dash-merge a.wy-dash-merge__open:focus-visible { color: inherit; outline: 2px solid currentColor; outline-offset: 2px; border-radius: 2px; }

/* The existing collection's name in the "already exists" notice is a link to
   it. Coloured from the notice's own text so it keeps the amber notice's
   checked contrast; underlined so it reads as a link; hover and focus named
   explicitly so no parent rule can turn it white or remove the ring. */
.wy-alert--notice a.wy-join-choice__open,
.wy-alert--notice a.wy-join-choice__open:visited {
    color: inherit;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.wy-alert--notice a.wy-join-choice__open:hover {
    color: inherit;
    text-decoration-thickness: 2px;
}
.wy-alert--notice a.wy-join-choice__open:focus-visible {
    color: inherit;
    outline: 2px solid currentColor;
    outline-offset: 2px;
    border-radius: 2px;
}
.wy-join-choice__note {
    /* Its own line at every width: it qualifies BOTH buttons, and sitting
       beside one of them would read as belonging to that one. */
    flex-basis: 100%;
    margin: 0;
    font-size: .82rem;
    color: var(--wy-muted, #64748b);
}

/* The merge warning inside Edit Course Details. Reuses .wy-alert--notice's own
   colours; only the spacing is local, because it sits between a field list and
   an action row rather than at the top of a card. */
.wy-dash-merge {
    margin: .2rem 0 .7rem;
    font-size: .84rem;
    line-height: 1.45;
}

/* ── PRO-247: the resource's own status and controls ──────────────────────
   A librarian asked to keep one of two versions could not see where the
   resource itself sits, or whether there was a catalogue record to look at.
   The pill and the two controls answer that — and they live on the GROUP, not
   inside either version card, because there is one resource however many
   source documents disagree about it. */

/* .wy-gap-summary is align-items:flex-start, so the pill would hang off the
   top of a two-line title. Pushed right and centred against the first line. */
.wy-mm-head { flex: 1 1 auto; min-width: 0; }

.wy-mm-status {
    flex:        0 0 auto;
    margin-left: auto;
    align-self:  center;
}

.wy-mm-actions {
    display:     flex;
    flex-wrap:   wrap;
    gap:         .5rem;
    /* Indented to the title, clearing the chevron — the same 38px the rest of
       this screen lines up on (.wy-gap-chev 30px + the summary's 0.5rem gap). */
    margin:      .15rem 0 .6rem 38px;
}

.wy-mm-action {
    display:        inline-flex;
    align-items:    center;
    gap:            .35rem;
    padding:        .3rem .65rem;
    border:         1px solid var(--wy-border-control);
    border-radius:  var(--wy-radius-sm);
    background:     #fff;
    color:          var(--wy-text);
    font-size:      .82rem;
    font-weight:    600;
    line-height:    1.3;
    text-decoration: none;
    cursor:         pointer;
}

.wy-mm-action:hover:not(:disabled),
.wy-mm-action:focus-visible:not(:disabled) {
    background: var(--wy-bg);
}

.wy-mm-action:disabled { opacity: .55; cursor: default; }

/* Destructive, but not shouting: it sits beside a neutral control and a
   librarian reaches it deliberately. Tint on hover only, so the row does not
   read as a warning at rest. */
.wy-mm-action--delete { color: #b91c1c; border-color: #fecaca; }
.wy-mm-action--delete:hover:not(:disabled),
.wy-mm-action--delete:focus-visible:not(:disabled) { background: #fef2f2; }

@media (max-width: 560px) {
    /* The pill drops under the title rather than squeezing it to a word a
       line. */
    .wy-mm-status { margin-left: 38px; align-self: flex-start; }
    .wy-mm-actions { margin-left: 0; }
}

/* ── Academic terms on Settings (PRO-250) ──────────────────────────────── */
.wy-terms-current{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;margin:1rem 0 .2rem;padding:.6rem .75rem;background:var(--wy-bg);border:1px solid var(--wy-border);border-radius:10px;}
.wy-terms-current__label{font-size:.68rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--wy-text-hint);}
.wy-terms-current__basis{font-size:.78rem;color:var(--wy-text-hint);}
.wy-terms-table{width:100%;border-collapse:collapse;margin-top:1rem;font-size:.86rem;}
.wy-terms-table th,.wy-terms-table td{text-align:left;padding:.5rem .4rem;border-bottom:1px solid var(--wy-border);vertical-align:middle;}
.wy-terms-table thead th{font-size:.68rem;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--wy-text-hint);}
.wy-terms-table tbody th{font-weight:700;color:var(--wy-text);}
.wy-terms-count{text-align:right;font-variant-numeric:tabular-nums;}
.wy-terms-acts{text-align:right;white-space:nowrap;}
.wy-terms-date{border:1px solid var(--wy-border-control);border-radius:7px;padding:.25rem .4rem;font:inherit;font-size:.82rem;background:var(--wy-card-bg);color:var(--wy-text);}
.wy-terms-date:focus-visible{outline:2px solid var(--wy-teal);outline-offset:1px;}
/* Two badges, and neither carries its meaning by colour alone: each states
   its own word (WCAG 1.4.1). */
.wy-terms-badge{display:inline-block;margin-left:.4rem;font-size:.68rem;font-weight:700;padding:.05rem .4rem;border-radius:20px;background:var(--wy-teal-light);color:var(--wy-teal-dark);border:1px solid var(--wy-teal-bd);}
.wy-terms-badge--review{background:var(--wy-amber-bg);color:var(--wy-amber);border-color:var(--wy-amber);}
.wy-terms-add{margin-top:1.2rem;}
.wy-terms-addrow{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;}
.wy-terms-addrow .wy-tx{width:auto;flex:1 1 11rem;min-width:9rem;}
.wy-terms-addrow input[type="date"]{flex:0 1 10rem;}
@media (max-width:640px){
  /* The table's action column is the first thing to run out of room; the
     dates wrap under the name rather than forcing a horizontal scroll on the
     whole card. */
  .wy-terms-table thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);}
  .wy-terms-table tr{display:block;padding:.5rem 0;border-bottom:1px solid var(--wy-border);}
  .wy-terms-table th,.wy-terms-table td{display:block;border:0;padding:.2rem 0;}
  .wy-terms-acts,.wy-terms-count{text-align:left;}
}

/* ── Session expired ──────────────────────────────────────────────────────
   Raised by inc/session-notice.php when any AJAX call comes back
   code: 'session_expired'. Fixed to the top rather than placed in the flow
   because the request that failed may have been triggered from anywhere on a
   long screen, and a notice the librarian has to scroll to find explains
   nothing — it was reported as a broken feature precisely because the real
   reason was invisible.

   Amber, not red, and the same vocabulary as .wy-alert--notice: nothing the
   librarian did is wrong and nothing they saved is lost. The clock icon
   carries the meaning as well as the tint (WCAG 1.4.1). */
.wy-session-notice {
    position:   fixed;
    top:        0;
    left:       0;
    right:      0;
    z-index:    9999;
    background: #fffbeb;
    color:      #78350f;
    border-bottom: 1px solid #fde68a;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.wy-session-notice__in {
    max-width:   var(--wy-maxw, 1100px);
    margin:      0 auto;
    padding:     0.7rem 1rem;
    display:     flex;
    align-items: center;
    gap:         0.65rem;
    font-size:   0.9rem;
}
.wy-session-notice__in p { margin: 0; flex: 1; }
.wy-session-notice svg   { flex-shrink: 0; }
.wy-session-notice .wy-btn { flex-shrink: 0; }

/* Below 560px the sentence and the button stop fitting on one line; wrapping
   keeps the button at a full tap target instead of squeezing it. */
@media (max-width: 560px) {
    .wy-session-notice__in { flex-wrap: wrap; }
    .wy-session-notice__in p { flex-basis: 100%; }
}

/* ── PRO-265: what a scoped faculty review covers ──
   Shown above the editable fields on Request faculty review, because it is the
   thing being confirmed; the fields below change the wording, not the contents. */
.wy-fr-scope {
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-bg);
    padding:       .9rem 1.1rem;
    margin-bottom: 1.1rem;
}

.wy-fr-scope__lede { margin: 0 0 .5rem; font-size: .92rem; }

.wy-fr-scope__list {
    margin:     0 0 .6rem;
    padding:    0;
    list-style: none;
    display:    grid;
    gap:        .2rem;
}

.wy-fr-scope__list li {
    display:     flex;
    gap:         .5rem;
    align-items: baseline;
    font-size:   .86rem;
    color:       var(--wy-text-muted);
}

/* The count leads, because "3 Needs Evaluation" is read as a quantity first
   and a category second. Tabular figures so two rows line up. */
.wy-fr-scope__n {
    min-width:            1.6rem;
    font-weight:          800;
    color:                var(--wy-text);
    font-variant-numeric: tabular-nums;
}

.wy-fr-scope__note { margin: 0; font-size: .82rem; color: var(--wy-text-muted); }

/* ── PRO-266: Needs your input ──
   The one section of the faculty review page with no student equivalent. It
   sits AFTER the collection, amber rather than teal, because it is a question
   and everything above it is an answer. The cards inside are the student
   cards — only this frame is new. */
/* .wy-lti-page reserves a full viewport so a short guide still pushes the
   footer down. With a section AFTER the guide that becomes a blank screen
   between the collection and the questions — reported as "the items are
   missing", because they were below it. The guide is not the last thing on
   the page here, so it does not need to fill one. */
.wy-fr--hasquestions .wy-lti-page { min-height: 0; }

/* The wrapper carries the guide's own gutter; this only needs vertical space.
   Its width used to be set here (max-width 900px, auto margins) against a
   guide whose bands are 1200px wide — so it sat narrower than the card above
   it and inset on both sides. */
.wy-fr-needsinput-wrap { padding-top: 0; padding-bottom: 0; }

.wy-fr-needsinput {
    margin:        1.25rem 0 2rem;
    padding:       1.1rem 1.2rem 1.3rem;
    border:        1px solid #f0d9ab;
    border-left:   4px solid #d99a2b;
    border-radius: var(--wy-radius);
    background:    #fdf8ee;
}

.wy-fr-needsinput__title {
    margin:      0 0 .35rem;
    scroll-margin-top: 1rem;   /* the banner link lands on the heading, not under it */
    font-size:   1.05rem;
    font-weight: 700;
    color:       #8a5a12;
}

.wy-fr-needsinput__lede {
    margin:      0 0 .9rem;
    font-size:   .9rem;
    line-height: 1.5;
    color:       var(--wy-text-muted);
}

/* The question itself. Its own line under the citation rather than a badge:
   it is a sentence someone has to read and answer, and a pill would shrink it
   to a label. */
.wy-fr-question {
    display:     block;
    width:       100%;
    margin-top:  .3rem;
    font-size:   .86rem;
    line-height: 1.45;
    color:       #8a5a12;
    font-weight: 600;
}

/* ── PRO-267: the resolve toast ──
   Holds the only route back from a resolve, so it is generous (12s) and is
   replaced rather than stacked: two toasts would mean two Undos and no way to
   tell which comment each belonged to. */
.wy-fac-toast {
    position:      fixed;
    left:          50%;
    bottom:        1.4rem;
    transform:     translateX(-50%);
    z-index:       1100;
    display:       inline-flex;
    align-items:   center;
    gap:           .9rem;
    padding:       .7rem 1.1rem;
    border-radius: var(--wy-radius-sm);
    background:    var(--wy-text);
    color:         #fff;
    font-size:     .88rem;
    font-weight:   600;
    box-shadow:    0 12px 30px rgba(0, 0, 0, .25);
}

.wy-fac-toast[hidden] { display: none; }

.wy-fac-toast__undo {
    border:          0;
    background:      transparent;
    padding:         0;
    font:            inherit;
    font-weight:     700;
    color:           #7ee0b5;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor:          pointer;
}

/* Undo is the only route back from a resolve, so it has to stay legible on the
   dark toast whatever the parent theme does to a hovered <button>. */
.wy-fac-toast__undo:hover,
.wy-fac-toast__undo:focus,
.wy-fac-toast__undo:focus-visible,
.wy-fac-toast__undo:active {
    color:      #7ee0b5;
    background: transparent;
    box-shadow: none;
    transform:  none;
    opacity:    1;
}

.wy-fac-toast__undo[disabled] { opacity: .55; cursor: default; }

/* On a dark surface the teal ring disappears; white is the only outline with
   contrast against it. */
.wy-fac-toast__undo:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* The scope line in the faculty banner. The count belongs on the first screen:
   a review of three collection items and five questions opened on a heading
   reading "3 items", and the five below the fold were reported as missing. */
.wy-fr-banner__scope a {
    color:                 var(--wy-teal-dark, #0f6f4c);
    font-weight:           700;
    text-underline-offset: 2px;
}

/* "See all faculty comments" — the way back to a comment once resolving has
   taken it off the row. Sits in the controls row beside the unresolved filter,
   as a link rather than a button because it goes somewhere. */
.wy-facfilter__history {
    font-size:             .84rem;
    font-weight:           600;
    color:                 var(--wy-teal-dark);
    text-decoration:       underline;
    text-underline-offset: 2px;
    white-space:           nowrap;
    align-self:            center;
}

.wy-facfilter__history:hover { color: var(--wy-text); }
.wy-facfilter__history:focus-visible { outline: 2px solid var(--wy-teal); outline-offset: 2px; }

/* ── Select mode on Review ────────────────────────────────────────────────
   Checkboxes used to sit on every row permanently, and five boxes each
   carried their own "Select all shown" bar. A librarian who never bulk-
   deletes anything looked at all of it anyway. Now one toggle under the
   search reveals the column and one page-wide bar.

   The whole mode hangs off ONE class on the container. Each component asking
   the same question of the same class is what keeps them from drifting out
   of step with each other. */

.wy-review-toolbar {
    display:         flex;
    align-items:     center;
    justify-content: flex-end;
    /* The gap ABOVE is the controls bar's own 1rem bottom margin, so the gap
       below matches it: 0.15rem/0.5rem left the button visibly closer to the
       first box than to the bar (2026-09-30). */
    margin:          0 0 1rem;
}

/* Quiet by default — it is an occasional action sitting beside a search box
   that is used constantly, so it must not compete with it. */
.wy-review-selectbtn {
    display:       inline-flex;
    align-items:   center;
    gap:           0.4rem;
    height:        34px;
    padding:       0 0.75rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm, 8px);
    background:    #fff;
    color:         var(--wy-text-muted);
    font-size:     0.82rem;
    font-weight:   700;
    cursor:        pointer;
}
.wy-review-selectbtn:hover { background: var(--wy-bg); color: var(--wy-text); }
.wy-review-selectbtn[aria-pressed="true"] {
    background:   var(--wy-teal-light);
    border-color: var(--wy-teal-bd);
    color:        var(--wy-teal-dark);
}
/* Restored at higher specificity than the hover/pressed rules above, because
   a :hover rule has twice deleted a focus ring in this file by winning on
   source order alone. */
.wy-review-toolbar .wy-review-selectbtn:focus-visible {
    outline:        2px solid var(--wy-teal);
    outline-offset: 2px;
}

/* The one bulk bar. STICKY: Review runs to 85 rows across five boxes, and a
   bar that scrolls away leaves a librarian holding a selection with no button
   to apply it to. */
.wy-rectools--page {
    position:        sticky;
    top:             0;
    z-index:         5;
    justify-content: flex-start;
    gap:             0.75rem;
    margin-bottom:   0.75rem;
    padding:         0.6rem 0.75rem;
    border:          1px solid var(--wy-teal-bd);
    border-radius:   var(--wy-radius, 10px);
    background:      var(--wy-teal-light);
}
.wy-rectools--page[hidden] { display: none; }
.wy-rectools__count {
    font-weight: 700;
    font-size:   0.85rem;
    color:       var(--wy-teal-dark);
}
/* Pushes the action group to the right edge of the bar. */
.wy-rectools--page .wy-bulk-actions { margin-left: auto; }
/* The two-scopes sentence. Full width so it sits under the row rather than
   squeezing the buttons; the row gap only applies once something wraps, so a
   bar with no note has no hole in it. */
.wy-rectools__note {
    flex-basis: 100%;
    margin:     0;
    font-size:  0.78rem;
    color:      var(--wy-teal-dark);
}
.wy-rectools__note[hidden] { display: none; }

/* The mode class is .wy-selecting, not .wy-review--selecting: the controller
   is shared with Course Collections, so the class that drives it cannot name
   one screen. The rules below that name ROWS stay per-screen, because a
   collection row and a resource row are different elements.

   The checkbox column: absent at rest, present while selecting. NOT a swap
   with the chevron, as the mockup does — on Review .wy-rec-pick is a sibling
   of <details> while the chevron lives inside <summary>, and moving a checkbox
   into a summary makes clicking it toggle the disclosure. */
.wy-rec-pick { display: none; }
.wy-selecting .wy-rec-pick { display: flex; }

/* Row actions go inert, as in the mockup. The JS also drops their tabindex —
   pointer-events alone still leaves them reachable by keyboard.

   .wy-review-icons is the wrapper wy_app_review_icon_actions() always emits.
   Keying on the CONTAINER instead missed most rows: the recommendations boxes
   put the icons in a bare styled <div>, not in .wy-exitem__status. */
.wy-selecting .wy-review-icons,
.wy-selecting .wy-altrow__source,
.wy-selecting .wy-exitem__status,
.wy-selecting .wy-gap-act {
    opacity:        0.28;
    pointer-events: none;
}

/* Selected tint. The checkbox itself still carries the state, so tint is
   never the only cue (WCAG 1.4.1). */
.wy-selecting .wy-exitem:has(.wy-bulk-chk:checked),
.wy-selecting .wy-gaprow:has(.wy-bulk-chk:checked) {
    background: var(--wy-teal-light);
}

@media (max-width: 640px) {
    /* Buttons wrap rather than overflow, and the bar stops being sticky —
       at this height a sticky bar would cover the rows being chosen. */
    .wy-rectools--page { position: static; }
    .wy-rectools--page .wy-bulk-actions { margin-left: 0; flex-basis: 100%; }
}

/* ── Select mode on Course Collections ────────────────────────────────────
   Same mode class, same bar, different rows. A collection row is the <li>
   itself and its controls live in .wy-dash-iconset / the status button. */

.wy-selecting .wy-dash-iconset,
.wy-selecting .wy-dash-statusbtn,
.wy-selecting .wy-dash-row__badge {
    opacity:        0.28;
    pointer-events: none;
}

/* The checkbox sits before the row's own chevron, both 30px tall, so the two
   controls and the title stay on one axis. */
.wy-selecting .wy-dash-row__main > .wy-rec-pick { display: flex; }

.wy-selecting .wy-dash-row:has(.wy-bulk-chk:checked) {
    background: var(--wy-teal-light);
}

/* A collection still generating cannot be selected — its run is mid-step, and
   deleting the guide underneath it leaves the run writing to a row that is
   gone. Dimmed so the reason is visible rather than the click just failing. */
.wy-selecting .wy-bulk-chk:disabled {
    opacity: 0.35;
    cursor:  not-allowed;
}

/* The bulk-delete modal's list of what is about to go. Scrolls rather than
   growing the dialog past the viewport — a selection can be a whole page. */
.wy-dash-bulkdel__list {
    max-height:    11rem;
    overflow-y:    auto;
    margin:        0 0 0.9rem;
    padding:       0.6rem 0.75rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm, 8px);
    background:    var(--wy-bg);
    font-size:     0.85rem;
    list-style:    none;
}
.wy-dash-bulkdel__list li { margin: 0 0 0.3rem; }
.wy-dash-bulkdel__list li:last-child { margin-bottom: 0; }
/* Published ones are listed first and marked, because deleting one takes down
   a page students may be reading right now. */
.wy-dash-bulkdel__live {
    display:       inline-block;
    margin-right:  0.4rem;
    padding:       0.05rem 0.4rem;
    border-radius: 999px;
    background:    #fffbeb;
    color:         #78350f;
    border:        1px solid #fde68a;
    font-size:     0.72rem;
    font-weight:   700;
}
.wy-dash-bulkdel__consequences {
    margin:    0 0 0.9rem 1.1rem;
    padding:   0;
    font-size: 0.85rem;
    color:     var(--wy-text-muted);
}
.wy-dash-bulkdel__consequences li { margin-bottom: 0.3rem; }
.wy-dash-bulkdel__confirm {
    display:       block;
    margin-top:    0.35rem;
    padding:       0.45rem 0.6rem;
    border:        1px solid var(--wy-border);
    border-radius: var(--wy-radius-sm, 8px);
    font-size:     0.9rem;
    width:         6rem;
}

/* ── Select mode on Materials Lists ───────────────────────────────────────
   The third screen, and the first that is a TABLE. Four things the flex-row
   blocks above take for granted do not hold here, and each is a rule below. */

/* 1. The global `.wy-rec-pick { display: none }` hides the checkbox, and only
      the two flex screens re-show it. A table needs its own. */
.wy-selecting .wy-fftable .wy-rec-pick { display: flex; }

/* 2. The cell is rendered ALWAYS, header and body together — toggling the
      cells is how a table's header desyncs from its body. At rest it is a
      1px column carrying nothing, which costs a hair of padding and no
      layout. `width: 1px` on a table cell means "as narrow as the content
      allows", not one pixel. */
.wy-cm-pickcell {
    width:         1px;
    padding-right: 0;
    white-space:   nowrap;
}

/* 3. The selected tint must paint the CELLS. A background on a <tr> is painted
      underneath its cells and loses to any cell background — and
      `.wy-ffrow--open > td` already sets one, so a row tint would simply not
      appear on an expanded row. */
.wy-selecting .wy-cm-row:has(.wy-bulk-chk:checked) > td {
    background: var(--wy-teal-light);
}

/* The row's only control is its title link, so that is what goes inert. The
   JS drops its tabindex too — pointer-events alone still leaves it reachable
   from the keyboard. */
.wy-selecting .wy-fftable .wy-ffrow__title {
    opacity:        0.28;
    pointer-events: none;
}

/* A list still being read cannot be selected: the import is mid-parse, and
   this screen has no poll, so it stays disabled until a reload. */
.wy-selecting .wy-fftable .wy-bulk-chk:disabled {
    opacity: 0.35;
    cursor:  not-allowed;
}

/* 4. At 640px .wy-fftable stacks each row into a block and prints
      td::before { content: attr(data-th) }. The checkbox cell carries no
      data-th, so that content is empty — this only removes the padding the
      stacked layout would otherwise put under it. */
@media (max-width: 640px) {
    .wy-cm-pickcell { padding-bottom: 0; }
}

/* The faculty banner's text column. "Your librarian would like your input on
   N items" used to be a SIBLING of .wy-fr-banner, so it rendered under the
   card rather than in it — the count and the sentence explaining it read as
   two separate objects. It joins the TEXT column rather than becoming a third
   flex child, or it would line up under the eye icon instead of under the
   sentence it continues. */
.wy-fr-banner__text {
    display:        flex;
    flex-direction: column;
    gap:            .35rem;
}
/* A <span> made block: it lives inside .wy-fr-banner, which is a <span>, and
   a <p> there is block content inside phrasing content — browsers repair that
   by closing the span early, which would put the line straight back outside
   the box. Same reason .wy-bib is a span inside <summary>.
   
   It sets NO font-size and NO colour, so it reads exactly like the sentence
   above it. It used to be .88rem and muted grey, which was right while it was
   a caption sitting OUTSIDE the card — a smaller, quieter line beneath a
   box. Inside the box, addressed to the same reader and carrying the more
   actionable of the two sentences, the contrast said the opposite of what it
   meant. */
.wy-fr-banner__scope {
    display: block;
    margin:  0;
}

/* ── PRO-284: the reviewer panel on the faculty review page ───────────────
   Replaces the one-line banner. Addressed to the reviewer, above the guide,
   and on the SAME column as the guide (it lives in .wy-fr-bannerband, which
   borrows the layout's own container). The guide below is not restyled. */
.wy-fr-panel {
    padding: 1.3rem 1.4rem 1.2rem;
    border: 1px solid var(--wy-border); border-left: 3px solid var(--wy-teal);
    border-radius: var(--wy-radius-sm); background: var(--wy-card-bg);
    color: var(--wy-text);
    margin-bottom: 1.4rem;
}
.wy-fr-panel__title { font-size: 1.2rem; line-height: 1.3; margin: 0 0 .5rem; font-weight: 700; }
.wy-fr-panel__scope { margin: 0 0 .6rem; }
.wy-fr-panel__steps { margin: .2rem 0 .6rem; padding-left: 1.3rem; display: grid; gap: .3rem; }
.wy-fr-panel__fine  { margin: 0 0 1rem; color: var(--wy-text-muted); font-size: .9rem; }

.wy-fr-search { margin: 0; }
.wy-fr-search__label { display: block; font-weight: 700; margin-bottom: .35rem; }
.wy-fr-search__row { display: flex; gap: .5rem; flex-wrap: wrap; }
.wy-fr-search__input { flex: 1 1 16rem; min-width: 0; }
.wy-fr-search__status { margin: .5rem 0 0; font-size: .9rem; color: var(--wy-text-muted); min-height: 1.2em; }

.wy-fr-results { list-style: none; margin: .6rem 0 0; padding: 0; border-top: 1px solid var(--wy-border); }
.wy-fr-result {
    display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: flex-start;
    padding: .7rem 0; border-bottom: 1px solid var(--wy-border);
}
.wy-fr-result__main { flex: 1 1 18rem; min-width: 0; display: flex; flex-direction: column; gap: .15rem; }
.wy-fr-result__title { font-weight: 600; overflow-wrap: anywhere; }
a.wy-fr-result__title { color: var(--wy-teal-dark); text-underline-offset: 2px; }
.wy-fr-result__meta { font-size: .86rem; color: var(--wy-text-muted); }
.wy-fr-result__side { flex: 0 0 auto; }
.wy-fr-result__done { font-size: .88rem; font-weight: 600; color: var(--wy-teal-dark); }
.wy-fr-result__box { flex: 1 1 100%; display: flex; flex-direction: column; gap: .4rem; }
.wy-fr-result__notelabel { font-size: .88rem; font-weight: 600; }
.wy-fr-result__acts { display: flex; gap: .5rem; flex-wrap: wrap; }
.wy-fr-result__err { margin: 0; color: #b42318; font-size: .88rem; }

.wy-fr-mine { margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--wy-border); }
.wy-fr-mine__title { font-size: 1rem; margin: 0 0 .4rem; }
.wy-fr-mine__n { font-weight: 400; color: var(--wy-text-muted); }
.wy-fr-mine__list { margin: 0; padding: 0; list-style: none; display: grid; gap: .35rem; }
/* One suggestion, collapsed to its title and status; opens to when it was
   sent, the resource and the note. */
.wy-fr-mine__item { border: 1px solid var(--wy-border); border-radius: var(--wy-radius-sm); background: var(--wy-card-bg); }
.wy-fr-mine__sum {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .6rem;
    padding: .55rem .8rem; cursor: pointer; list-style: none;
}
.wy-fr-mine__sum::-webkit-details-marker { display: none; }
.wy-fr-mine__sum::before {
    content: ""; width: .45rem; height: .45rem; flex: 0 0 auto; align-self: center;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(-45deg); transition: transform .15s; margin-right: .2rem;
}
.wy-fr-mine__item[open] > .wy-fr-mine__sum::before { transform: rotate(45deg); }
.wy-fr-mine__sum:hover .wy-fr-mine__t { text-decoration: underline; text-underline-offset: 2px; }
.wy-fr-mine__sum:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; border-radius: var(--wy-radius-sm); }
.wy-fr-mine__body {
    display: grid; grid-template-columns: max-content 1fr; gap: .35rem 1rem;
    margin: 0; padding: .2rem .8rem .75rem 2rem; font-size: .92rem;
}
.wy-fr-mine__body dt { font-weight: 600; color: var(--wy-text-muted); }
.wy-fr-mine__body dd { margin: 0; overflow-wrap: anywhere; }
.wy-fr-mine__body a { color: var(--wy-teal-dark); text-underline-offset: 2px; }
.wy-fr-mine__meta { display: block; font-size: .86rem; color: var(--wy-text-muted); }
.wy-fr-mine__note { white-space: pre-line; }
.wy-fr-mine__none { color: var(--wy-text-muted); font-style: italic; }
@media (max-width: 560px) {
    .wy-fr-mine__body { grid-template-columns: 1fr; padding-left: .8rem; }
    .wy-fr-mine__body dd { margin-bottom: .3rem; }
}
.wy-fr-mine__t { font-weight: 600; margin-right: .5rem; }
.wy-fr-mine__s { font-size: .86rem; color: var(--wy-text-muted); }

.wy-fr-panel a:focus-visible,
.wy-fr-panel button:focus-visible,
.wy-fr-panel input:focus-visible,
.wy-fr-panel textarea:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; }

/* Faculty feedback screen — suggested resources (PRO-284). */
.wy-ffsuggest__list { list-style: none; margin: 0; padding: 0; }
.wy-ffsuggest__item {
    display: flex; flex-wrap: wrap; gap: .6rem 1rem; align-items: flex-start;
    padding: .8rem 0; border-top: 1px solid var(--wy-border);
}
.wy-ffsuggest__main { flex: 1 1 20rem; min-width: 0; display: flex; flex-direction: column; gap: .15rem; }
.wy-ffsuggest__side { flex: 0 0 auto; display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.wy-ffsuggest__note { margin: .3rem 0 0; font-style: italic; }
.wy-ffsuggest__err  { flex-basis: 100%; margin: 0; color: #b42318; font-size: .88rem; }
.wy-facfilter__suggested { font-weight: 600; }

/* Faculty feedback — "Approved by faculty" (2026-10-01). Glyph + words, never
   tint alone (WCAG 1.4.1). */
.wy-ffapproved {
    display: flex; gap: .6rem; align-items: center;
    margin: 0 0 1.1rem; padding: .9rem 1.1rem;
    border: 1px solid var(--wy-teal-bd, #b7d9c9); border-radius: var(--wy-radius-sm);
    background: var(--wy-teal-light); color: var(--wy-teal-dark);
}
.wy-ffapproved svg { flex-shrink: 0; }

/* ── Reviewer panel, 2026-10-01 redesign ─────────────────────────────────
   One sentence of context and four actions. Everything else (search, the
   list of what was sent) lives in windows, so the panel stays the size of
   its job and the collection starts on the first screen. */
.wy-fr-panel__lede { margin: 0 0 1rem; color: var(--wy-text-muted); font-size: .94rem; line-height: 1.55; }
.wy-fr-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem 1rem; }
.wy-fr-actions .wy-btn { min-width: 12rem; justify-content: center; }
.wy-fr-actions__status { margin: .7rem 0 0; font-size: .9rem; color: var(--wy-teal-dark); min-height: 1.2em; }
.wy-fr-actions__status:empty { margin: 0; min-height: 0; }
/* Pressed "Looks good": a record of what was sent, not a second invitation. */
.wy-fr-approve.is-done { background: var(--wy-teal-light); color: var(--wy-teal-dark); border-color: var(--wy-teal-bd, #b7d9c9); cursor: default; }
/* "View my suggestions (3)" and the inline link in the suggest window: a
   button (it opens a window, it does not navigate) dressed as a link. */
.wy-fr-linkbtn {
    border: 0; background: none; padding: 0; cursor: pointer; font: inherit;
    color: var(--wy-teal-dark); font-weight: 600; text-decoration: underline; text-underline-offset: 3px;
}
.wy-fr-modal__sub .wy-fr-linkbtn { font-weight: 500; }
.wy-fr-linkbtn:focus-visible,
.wy-fr-panel .wy-fr-linkbtn:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; border-radius: 2px; }

.wy-fr-modal__panel.wy-fr-modal__panel--wide { max-width: 46rem; }

/* "Search and recommend" — the collapsible box inside the suggest window. */
.wy-fr-sbox { border: 1px solid var(--wy-border); border-radius: var(--wy-radius-sm); background: var(--wy-bg); }
.wy-fr-sbox__sum {
    display: flex; align-items: center; gap: .5rem; padding: .7rem .9rem;
    cursor: pointer; list-style: none; font-weight: 700; color: var(--wy-teal-dark);
    background: var(--wy-teal-light); border-radius: var(--wy-radius-sm);
}
.wy-fr-sbox[open] > .wy-fr-sbox__sum { border-radius: var(--wy-radius-sm) var(--wy-radius-sm) 0 0; }
.wy-fr-sbox__sum::-webkit-details-marker { display: none; }
.wy-fr-sbox__sum::before {
    content: ""; width: .45rem; height: .45rem; flex: 0 0 auto;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(-45deg); transition: transform .15s; margin-right: .15rem;
}
.wy-fr-sbox[open] > .wy-fr-sbox__sum::before { transform: rotate(45deg); }
.wy-fr-sbox__sum:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; }
.wy-fr-sbox__body { padding: .9rem; background: var(--wy-card-bg); border-radius: 0 0 var(--wy-radius-sm) var(--wy-radius-sm); }
.wy-fr-sbox .wy-fr-result__err { flex-basis: 100%; }

/* The type as a small tag AFTER the title, so the arrow, the title and the
   status stay on one line (a full-width label pushed the arrow above it). */
.wy-fr-mine__kind {
    order: 2; font-size: .7rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--wy-text-muted); background: var(--wy-bg); border: 1px solid var(--wy-border);
    border-radius: 999px; padding: .05rem .5rem;
}
.wy-fr-modal .wy-fr-mine__sum { flex-wrap: nowrap; align-items: flex-start; }
.wy-fr-modal .wy-fr-mine__sum::before { margin-top: .45rem; }
.wy-fr-mine__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .55rem; min-width: 0; }
.wy-fr-mine__head > .wy-fr-mine__t { order: 1; overflow-wrap: anywhere; }
.wy-fr-mine__head > .wy-fr-mine__s { order: 3; }
.wy-fr-mine__empty { margin: .5rem 0 0; color: var(--wy-text-muted); font-style: italic; }
.wy-fr-modal .wy-fr-mine__list { margin-top: .3rem; }

/* ── Hover never turns text white (docs/ux-guidelines.md §Hover) ─────────
   Neve's `button:hover, button:focus { color: var(--primarybtnhovercolor) }`
   is #fff, and `html button:hover` above clears only the BACKGROUND. So any
   <button> that is not a .wy-btn and declares no hover COLOUR of its own goes
   white on a white card the moment it is pointed at — reported for "View my
   suggestions" and "view the suggestions you've already made". Each one here
   names its hover colour at (0,2,0), which outranks Neve's (0,1,1). The house
   rule: a rollover adds an underline (links) or a tint (controls); it never
   changes the text colour away from the resting one. */
.wy-fr-linkbtn:hover,
.wy-fr-linkbtn:focus,
.wy-fr-linkbtn:active {
    color: var(--wy-teal-dark); background: none;
    text-decoration-thickness: 2px;
}
/* The × on every dialog shell (faculty AND librarian windows): muted at rest,
   darker on hover — never white. */
.wy-fr-modal__x:hover, .wy-fac-modal__x:hover, .wy-fx-modal__x:hover, .wy-mm-modal__x:hover,
.wy-fr-modal__x:focus, .wy-fac-modal__x:focus, .wy-fx-modal__x:focus, .wy-mm-modal__x:focus {
    color: var(--wy-text); background: var(--wy-bg); border-radius: 6px;
}
/* A pressed "Looks good" is a record, not a control: hover changes nothing. */
.wy-btn.wy-fr-approve.is-done:hover,
.wy-btn.wy-fr-approve.is-done:focus {
    background: var(--wy-teal-light); color: var(--wy-teal-dark); border-color: var(--wy-teal-bd, #b7d9c9);
}

/* ── "My suggestions and comments" in the theme's green ─────────────────
   Same treatment as the suggest window's "Search and recommend" box: a
   tinted header row per item, the body on white beneath it. */
.wy-fr-modal .wy-fr-mine__item { border-color: var(--wy-teal-bd, #b7d9c9); overflow: hidden; }
.wy-fr-modal .wy-fr-mine__sum { background: var(--wy-teal-light); color: var(--wy-teal-dark); }
.wy-fr-modal .wy-fr-mine__sum:hover { background: #dcece4; }
.wy-fr-modal .wy-fr-mine__sum:hover .wy-fr-mine__t { color: var(--wy-teal-dark); }
.wy-fr-modal .wy-fr-mine__item[open] > .wy-fr-mine__sum { border-bottom: 1px solid var(--wy-teal-bd, #b7d9c9); }
.wy-fr-modal .wy-fr-mine__t { color: var(--wy-teal-dark); font-weight: 700; }
.wy-fr-modal .wy-fr-mine__kind { background: var(--wy-card-bg); border-color: var(--wy-teal-bd, #b7d9c9); color: var(--wy-teal-dark); }
.wy-fr-modal .wy-fr-mine__s { color: var(--wy-text-muted); }
.wy-fr-modal .wy-fr-mine__body { padding-top: .7rem; background: var(--wy-card-bg); }
.wy-fr-modal .wy-fr-mine__body dt { color: var(--wy-teal-dark); }

/* ── Review: the Faculty feedback box (2026-10-01) ───────────────────────
   Under the summary card, only when the reviewer sent something. Theme green,
   no icon: title + one line, two counts that are links, and the row filter. */
.wy-facbox {
    display: flex; flex-wrap: wrap; align-items: center; gap: .8rem 0;
    margin: 0 0 1rem; padding: .9rem 1.2rem;
    background: var(--wy-teal-light); border: 1px solid var(--wy-teal-bd);
    border-radius: var(--wy-radius);
}
.wy-facbox__text { flex: 1 1 16rem; min-width: 0; padding-right: 1.2rem; }
.wy-facbox__title { margin: 0; font-size: 1.02rem; font-weight: 700; color: var(--wy-teal-dark); }
.wy-facbox__sub { margin: .15rem 0 0; font-size: .86rem; color: var(--wy-text-muted); }
.wy-facbox__stat {
    display: flex; flex-direction: column; align-items: center; min-width: 7.5rem;
    padding: .2rem 1.2rem; border-left: 1px solid var(--wy-teal-bd);
    text-decoration: none; color: var(--wy-teal-dark);
}
.wy-facbox__n { font-size: 1.35rem; font-weight: 700; line-height: 1.2; }
.wy-facbox__lbl { font-size: .8rem; color: var(--wy-text-muted); text-decoration: underline; text-underline-offset: 2px; }
/* docs/ux-guidelines.md §3 Hover: a rollover underlines, never recolours. */
.wy-facbox .wy-facbox__stat:hover,
.wy-facbox .wy-facbox__stat:focus { color: var(--wy-teal-dark); background: none; }
.wy-facbox .wy-facbox__stat:hover .wy-facbox__lbl { color: var(--wy-teal-dark); text-decoration-thickness: 2px; }
.wy-facbox .wy-facbox__stat:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; border-radius: 6px; }
.wy-facbox__filter { padding-left: 1.2rem; border-left: 1px solid var(--wy-teal-bd); color: var(--wy-text); font-weight: 500; }
@media (max-width: 640px) {
    .wy-facbox__text { flex-basis: 100%; padding-right: 0; }
    .wy-facbox__stat:first-of-type { border-left: 0; padding-left: 0; }
}

/* Faculty feedback screen: the All / Unresolved / Resolved chips. */
.wy-ff-filter { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 1rem; }
.wy-ff-filter .wy-dash-chip { text-decoration: none; display: inline-flex; gap: .35rem; align-items: center; }
.wy-ff-filter__n { font-weight: 700; }
.wy-ff-filter .wy-dash-chip:hover, .wy-ff-filter .wy-dash-chip:focus { text-decoration: none; }
.wy-ff-filter .wy-dash-chip--on:hover, .wy-ff-filter .wy-dash-chip--on:focus { color: #fff; }

/* ── Textbook cost and savings (inc/textbook-cost.php) ─────────────────────
   Review only — prices never reach a student page. Every control here is a
   .wy-btn, so Neve's button:hover cannot whiten one (invariant 13); the link
   rules restore their own focus ring. */
.wy-tc-line {
    display: block; margin-top: .3rem;
    font-size: .86rem; color: var(--wy-text-muted); font-variant-numeric: tabular-nums;
}
.wy-tc-line strong { color: var(--wy-text); font-weight: 600; }
.wy-tc-line--none { font-style: italic; }
.wy-tc-line--saved s { color: var(--wy-text-hint); margin-right: .15rem; }
.wy-tc-pill {
    display: inline-block; padding: .05rem .55rem; border-radius: 999px;
    background: var(--wy-teal-dark); color: #fff; font-weight: 600; font-size: .8rem;
}
.wy-tc-panel {
    /* On the comparison table's own left edge (.wy-hasalt .wy-alt-panel
       indents by --wy-alt-indent); 0 where a row has no indent. */
    margin: .6rem 0 .2rem var(--wy-alt-indent, 0px); padding: .8rem 1rem;
    border: 1px solid var(--wy-border); border-radius: var(--wy-radius-sm); background: var(--wy-card-bg);
}
.wy-tc-panel__head { font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--wy-text-muted); }
.wy-tc-panel__hint { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--wy-text-hint); }
.wy-tc-none { margin: .4rem 0 0; font-style: italic; color: var(--wy-text-muted); }
/* Full panel width, like the table above: every amount ends on the panel's
   right edge and no label wraps. */
.wy-tc-opts { list-style: none; margin: .5rem 0 0; padding: 0; }
.wy-tc-opts li {
    display: flex; justify-content: space-between; gap: 1rem;
    padding: .25rem 0; border-bottom: 1px solid var(--wy-border); font-size: .9rem;
}
.wy-tc-opts li:last-child { border-bottom: 0; }
.wy-tc-amt { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Same column as the option prices above: label left, figure right-aligned,
   so every amount in the panel lines up on one edge. */
.wy-tc-figs { margin: .6rem 0 0; display: grid; gap: .25rem; }
.wy-tc-figs > div { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; font-size: .9rem; }
.wy-tc-figs dt { color: var(--wy-text-muted); }
.wy-tc-figs dt::after { content: ':'; }
.wy-tc-figs dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.wy-tc-figs dd s { color: var(--wy-text-hint); font-weight: 500; margin-right: .3rem; }
.wy-tc-note { margin: .5rem 0 0; font-size: .8rem; color: var(--wy-text-hint); }
.wy-tc-alt { margin-top: .8rem; padding-top: .7rem; border-top: 1px solid var(--wy-border); }
.wy-tc-alt__res { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; font-weight: 600; }
.wy-tc-alt__res a { color: var(--wy-teal-dark); }
.wy-tc-alt__res a:hover, .wy-tc-alt__res a:focus { color: var(--wy-teal-dark); text-decoration: underline; }
.wy-tc-alt__res a:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; }
.wy-tc-alt__sentence, .wy-tc-alt__state { margin: .5rem 0 0; font-size: .9rem; }
.wy-tc-actions { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-top: .6rem; }
.wy-tc-msg { font-size: .85rem; color: var(--wy-text-muted); }
.wy-tc-oer-note { margin: 1rem 0 .4rem; font-size: .88rem; color: var(--wy-text-muted); }

.wy-tc-sum {
    display: grid; grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
    gap: 1rem; align-items: center; margin: 0 0 1rem; padding: 1rem 1.2rem; text-align: left;
}
.wy-tc-sum__title { margin: 0; font-size: 1.02rem; font-weight: 700; color: var(--wy-text); }
.wy-tc-sum__sub { margin: .25rem 0 0; font-size: .85rem; line-height: 1.45; color: var(--wy-text-muted); }
.wy-tc-sum__sub a { color: var(--wy-teal-dark); }
.wy-tc-sum__sub a:hover, .wy-tc-sum__sub a:focus { color: var(--wy-teal-dark); text-decoration: underline; }
.wy-tc-sum__sub a:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; }
.wy-tc-stat { border-left: 1px solid var(--wy-border); padding-left: 1rem; }
.wy-tc-stat__k { font-size: .74rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--wy-text-muted); }
.wy-tc-stat__v { font-size: 1.45rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--wy-text); }
.wy-tc-stat__v--g { color: var(--wy-teal-dark); }
.wy-tc-stat__x { font-size: .8rem; color: var(--wy-text-muted); }
@media (max-width: 760px) {
    .wy-tc-sum { grid-template-columns: 1fr 1fr; }
    .wy-tc-sum__intro { grid-column: 1 / -1; }
    .wy-tc-stat { border-left: 0; padding-left: 0; }
}
.wy-tc-proposal a { color: var(--wy-teal-dark); }
.wy-tc-proposal a:hover, .wy-tc-proposal a:focus { color: var(--wy-teal-dark); text-decoration: underline; }
.wy-tc-proposal a:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; }

/* ── "Also delete the course collections this list created" ──────────────
   The opt-in inside both materials-list delete dialogs
   (wy_materials_collections_option_html). The list and the typed word reuse
   the Course Collections bulk-delete classes, so the two read as one act. */
.wy-mlc { margin: 0.8rem 0 0; }
.wy-mlc__status { margin: 0 0 0.4rem; }
.wy-mlc__opt {
    display:     flex;
    align-items: flex-start;
    gap:         0.5rem;
    padding:     0.6rem 0.75rem;
    border:      1px solid #fecaca;
    border-radius: var(--wy-radius-sm, 8px);
    background:  #fef2f2;
    color:       #7f1d1d;
    font-size:   0.88rem;
    font-weight: 600;
    cursor:      pointer;
}
.wy-mlc__opt input { margin-top: 0.15rem; flex: none; }
.wy-mlc__detail { margin-top: 0.8rem; }
.wy-mlc__meta { color: var(--wy-text-muted); }
.wy-mlc__flagsfailed {
    margin:    0 0 0.6rem;
    font-size: 0.82rem;
    color:     #78350f;
}
.wy-mlc .wy-dash-bulkdel__confirm { width: 8rem; }
/* "Library copy of the replaced book" — the comparison folded under a book
   that a free resource has replaced. A <summary>, not a <button>, but it gets
   its own hover/focus colours all the same (invariant 13). */
.wy-tc-libcopy { margin: .6rem 0 0 var(--wy-alt-indent, 0px); }
.wy-tc-libcopy__sum {
    cursor: pointer; font-size: .86rem; font-weight: 600; color: var(--wy-text-muted);
    padding: .3rem 0; list-style-position: inside;
}
.wy-tc-libcopy__sum:hover, .wy-tc-libcopy__sum:focus { color: var(--wy-teal-dark); background: none; }
.wy-tc-libcopy__sum:focus-visible { outline: 2px solid var(--wy-teal-dark); outline-offset: 2px; }
.wy-hasalt .wy-tc-libcopy .wy-alt-panel { padding-left: 0; }
.wy-tc-restore { margin: .4rem 0 .6rem; }
.wy-tc-restore__hint { font-size: .85rem; color: var(--wy-text-muted); }
/* No Match Found rows (.wy-gaprow) have no --wy-alt-indent: line the panel up
   with the title text the way .wy-exitem-expand does (chevron 30px + gap). */
.wy-gaprow .wy-tc-panel, .wy-gaprow .wy-tc-libcopy { margin-left: 38px; }
/* Automatic OER matching — a suggested free textbook (not adopted). */
.wy-tc-sug__k { font-size: .74rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--wy-text-muted); margin-bottom: .3rem; }
.wy-tc-sugflag { color: var(--wy-teal-dark); font-weight: 600; }
.wy-tc-auto { font-size: .8rem; font-weight: 500; color: var(--wy-text-muted); }
