:root {
    /* HeinOnline brand palette (2024-03-21 style guide) */
    /* Brand */
    --brand-primary: #123150;
    --brand-primary-tint: #eaeef3;
    --link-color: #365996;
    --link-color-strong: #2b54b9;
    /* Surfaces / borders */
    --surface: #ffffff;
    --surface-subtle: #f5f5f5;
    --border-color: #e6e6e6;
    --input-border: #d0d0d0;
    /* Text */
    --text-color: #000000;
    /* #6c757d cleared 4.5:1 on pure white (4.69) and failed on every tinted surface the
       app actually uses -- 4.30 on --neutral-100, 4.02 on --brand-primary-tint, 3.90 on
       #eaeaea. Since this one token carries most secondary text, a dashboard row repeats
       the failure per row: 78 instances on the researcher dashboard alone.
       #5c656d is the same hue, darkened until the WORST surface clears 4.5 (4.93). */
    --text-muted: #5c656d;
    /* Neutrals (Phase 1B targets) */
    --neutral-100: #f5f5f5;
    --neutral-200: #e6e6e6;
    --neutral-300: #dddddd;
    --neutral-400: #cccccc;
    --neutral-500: #999999;
    --neutral-600: #666666;
    /* Misc */
    /* WCAG 1.4.11 (AA) wants 3:1 for a focus indicator against whatever sits behind it,
       and this one token has to clear that on BOTH the white pages and the navy sidebar
       -- which pull opposite ways. Only luminance 0.187-0.300 satisfies both, so the ring
       has to be a mid-tone: brand navy itself vanishes on the sidebar, and --link-color
       (#365996) only reaches 1.91:1 there.
       #5187bd is --brand-primary's own hue (210deg) lightened into that band. Worst case
       3.25:1 (on --brand-primary-tint); 3.78 white, 3.51 navy, 3.53 auth field fill.
       The previous #a7a77b olive was 2.48:1 on white -- i.e. it failed on nearly every
       screen in the app. */
    --focus-ring: #5187bd;
    --danger: #b20101;
    /* Marketing's red (Lauren, Basecamp 2026-08-31: "#d22c2e is our preferred red"):
       the counts and flags meant to catch the eye -- the folder page's Unread number
       and its New tag. It is NOT an error colour; --danger above is that one, and is
       darker because white-on-it has to clear 4.5:1 too. This one is only ever text
       on a light surface: 5.07:1 on white and 4.74 on --table-row-hover, the two it
       lands on. It does NOT clear 4.5 on --brand-primary-tint (4.35), so the test
       that guards it names the surfaces rather than the token. */
    --attention: #d22c2e;
    /* Status (AA on light surfaces; --danger above is the error/closed color) */
    --success: #157347;
    --warning: #8a6500;
    /* Offer-extended / informational. The raw #17a2b8 it replaces was 3.04:1 as text. */
    --info: #0f6674;
    /* Text on dark/navy surfaces */
    --text-inverse: #ffffff;
    --text-inverse-muted: #ced4da;
}

/* Bootstrap's colour utilities are declared !important, so they beat every rule below and
   every var(--…) in the palette. Three of them fail 1.4.3 outright as text on white --
   .text-primary #007bff 3.98:1, .text-success #28a745 3.13:1, .text-info #17a2b8 3.04:1
   -- and .text-muted pins #6c757d, so the 26 elements using the CLASS never saw the token
   being darkened. Between them these appear 260+ times across the templates, so remapping
   them here is the difference between fixing one link and fixing all of them.
   !important is required to win against Bootstrap's own !important. */
.text-primary   { color: var(--link-color) !important; }
.text-success   { color: var(--success) !important; }
.text-info      { color: var(--info) !important; }
.text-warning   { color: var(--warning) !important; }
.text-danger    { color: var(--danger) !important; }
.text-muted,
.text-secondary { color: var(--text-muted) !important; }
/* .text-black-50 is rgba(0,0,0,.5) -- 3.98:1 on white, so every character counter
   ("9/150") sat just under the 4.5:1 line. */
.text-black-50  { color: var(--text-muted) !important; }

/* Bootstrap paints these backgrounds from its own raw palette and carries white text
   on them, which lands at 3.0-4.0:1 -- under the 4.5:1 WCAG 1.4.3 asks for. The tokens
   are the same hues darkened until white clears (5.9-6.9:1). !important for the same
   reason as the colour utilities above: Bootstrap declares its own. */
.badge-success, .bg-success { background-color: var(--success) !important; }
.badge-info,    .bg-info    { background-color: var(--info) !important; }
.badge-primary, .bg-primary { background-color: var(--link-color) !important; }
.badge-danger,  .bg-danger  { background-color: var(--danger) !important; }


/* Outline buttons were missed by both blocks above: .btn-outline-success paints its
   label in the same raw #28a745 (3.13:1 on white), and fills with that hue on hover,
   which puts white text at the same failing ratio. Remapped to the token that the
   utilities already use. Not !important -- Bootstrap does not declare these, so
   source order is enough. */
.btn-outline-success {
    color: var(--success);
    border-color: var(--success);
}

.btn-outline-success:hover,
.btn-outline-success:focus,
.btn-outline-success:active,
.btn-outline-success:not(:disabled):not(.disabled).active {
    background-color: var(--success);
    border-color: var(--success);
}

a {
    color: var(--link-color);
    text-decoration: none;
}

a:hover {
    color: var(--brand-primary);
    text-decoration: none;
}

/* WCAG 1.4.1 (Use of Color): a link sitting inside a block of text can't be set
   apart from that text by colour alone, so links in body copy carry an underline.
   Standalone links -- nav items, cards, whole clickable rows -- are exempt under
   1.4.1 because their position identifies them, and they keep the plain style.
   The :hover repeats are load-bearing: `a:hover` above outranks `p a` on
   specificity and would strip the underline the moment you point at the link. */
p a,
p a:hover,
small a,
small a:hover,
.feedback-header a,
.feedback-header a:hover,
#learn-more-content li a,
#learn-more-content li a:hover {
    text-decoration: underline;
}

/* Exceptions, in any context: a link wrapping only an icon has no text to
   underline, a button-styled link already reads as a control, and .popup-profile
   names sit alone in their block so 1.4.1 never applied to them. !important
   because these must outrank the :hover selectors above wherever they land --
   .popup-profile appears both inside a <p> (notes) and a <small> (review header). */
a.btn,
a.popup-profile,
/* The folder header's bookmark toggles are the one icon-only link that lands inside an
   underlined block. This used to read `a:has(> i:only-child)`, but :only-child counts
   ELEMENTS -- so "Joseph Avery <i>" matched as icon-only and silently lost its underline
   along with 12 other links that have text. Name the case instead of guessing at it. */
.folder-info-title > a {
    text-decoration: none !important;
}

/* ...except in the review header, where the author name sits mid-sentence after
   "Submitted by" rather than alone. The exemption above assumed otherwise; that
   assumption was never testable, because the review page's snapshot was silently a
   copy of the dashboard until the capture harness stopped following redirects. */
small a.popup-profile,
small a.popup-profile:hover,
small .popup-profile,
small .popup-profile:hover {
    text-decoration: underline !important;
}

.grey-info-text {
    color: var(--text-muted);
}

.overflow-justify {
    white-space: normal;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* BTNs,dropdown,select2 boxes */
.btn-primary, .btn-primary.disabled, .btn-primary:disabled {
    background-color: var(--brand-primary) !important;
    border-color: var(--brand-primary) !important;
}
/* Bootstrap fades disabled buttons to opacity .65, which lands white-on-navy at
   4.49:1 -- one hundredth under WCAG 1.4.3's line (Siteimprove: the gated "Upload
   Files" step button). .75 composites to ~6.1:1 and is barely distinguishable. */
.btn-primary.disabled,
.btn-primary:disabled {
    opacity: 0.75;
}

.btn-outline-primary{
    color: var(--brand-primary);
    border-color: var(--brand-primary);
}

.show>.btn-outline-primary.dropdown-toggle{
    color: var(--surface-subtle);
    background-color: var(--brand-primary);
    border-color: var(--brand-primary);
}

.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--selected,
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[aria-selected=true]:not(.select2-results__option--highlighted){
    color: var(--brand-primary);
    background: rgb(183, 181, 229);
}

.select2-container--bootstrap-5 .select2-dropdown{
    border-color: var(--brand-primary);
}

.btn-primary:not(:disabled):not(.disabled).active:focus,
.btn-primary:not(:disabled):not(.disabled):active:focus,
.btn-primary.focus,
.btn-primary:focus,
.btn:focus,
.show>.btn-primary.dropdown-toggle:focus,
.btn-outline-primary:not(:disabled):not(.disabled).active:focus,
.btn-outline-primary:not(:disabled):not(.disabled):active:focus,
.btn-outline-primary.focus,
.btn-outline-primary:focus,
.show>.btn-outline-primary.dropdown-toggle:focus{
    box-shadow: 0 0 0 0.2rem rgb(194, 204, 243)
}

.form-control:focus,
.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field:focus,
.select2-container--bootstrap-5.select2-container--focus .select2-selection,
.select2-container--bootstrap-5.select2-container--open .select2-selection{
    box-shadow: 0 0 0 0.2rem rgb(194, 204, 243);
    border: 1px solid var(--brand-primary);
}

.dropdown-item.active, .dropdown-item:active{
    color: black;
    background-color: var(--brand-primary-tint);
}

.btn-outline-primary:hover,
.btn-outline-primary:not(:disabled):not(.disabled):active,
.btn-outline-primary:not(:disabled):not(.disabled).active,
.btn-outline-primary:not(:disabled):not(.disabled):active:focus,
.btn-outline-primary:not(:disabled):not(.disabled).active:focus,
.show > .btn-outline-primary.dropdown-toggle:focus {
    background-color: var(--brand-primary);
    border-color: var(--brand-primary);
}

/* Prevent chrome button borders */
button, button:active, button:hover, button:focus, input, input:active, input:hover, input:focus, a, a:active, a:hover, a:focus {
    outline: None;
}

/*Spinners*/
.smt-spinner-circle {
    width: 50px;
    height: 50px;
    position: relative;
    margin: 20px;
    border-radius: 50%;
}

.smt-spinner {
    height: 100%;
    width: 100%;
    border-radius: 50%;
    border-right: 2px solid rgba(255, 255, 255, 0.6);
    border-top: 2px solid var(--brand-primary);
    border-left: 2px solid var(--brand-primary);
    border-bottom: 2px solid var(--brand-primary);
    animation: rotate--spinner 1.6s infinite;
}

.message-spinner {
    border: 4px solid rgba(0, 0, 0, 0.1);
    border-radius: 50%;
    border-top: 4px solid var(--brand-primary);
    width: 30px;
    height: 30px;
    animation: rotate--spinner linear 1.6s infinite;
}

.message-spinner-text {
    text-align: center;
    font-size: 14px;
    /* Bare `gray` is 3.95:1 on white and 3.62:1 on --neutral-100 -- it never
       reaches 4.5:1 on any surface this app uses. */
    color: var(--text-muted);
}

@keyframes rotate--spinner {
    from {
        transform: rotate(0);
    }
    to {
        transform: rotate(360deg);
    }
}

#load-spinner {
    position: fixed;
    z-index: 999999; /* make sure it's on top of everything else */
    left: 0;
    top: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(239, 240, 241, 0.95);
    display: flex;
    justify-content: center; /* center horizontally */
    align-items: center; /* center vertically */
}

#load-spinner .load-spinner-loader {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    animation: rotate 1s linear infinite
}

#load-spinner .load-spinner-loader::before, #load-spinner .load-spinner-loader::after {
    content: "";
    box-sizing: border-box;
    position: absolute;
    inset: 0px;
    border-radius: 50%;
    border: 5px solid #FFF;
    animation: prixClipFix 2s linear infinite;
}

#load-spinner .load-spinner-loader::after {
    border-color: var(--brand-primary);
    animation: prixClipFix 2s linear infinite, rotate 0.5s linear infinite reverse;
    inset: 6px;
}

@keyframes rotate {
    0% {
        transform: rotate(0deg)
    }
    100% {
        transform: rotate(360deg)
    }
}

@keyframes prixClipFix {
    0% {
        clip-path: polygon(50% 50%, 0 0, 0 0, 0 0, 0 0, 0 0)
    }
    25% {
        clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 0, 100% 0, 100% 0)
    }
    50% {
        clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 100% 100%, 100% 100%)
    }
    75% {
        clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 100%)
    }
    100% {
        clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 0)
    }
}

/* ===== Shared page components (consolidated from evaluation/paper_detail/review) ===== */
/* Comment / chat widget */
.single-comment-container.users-own-comment-modifier {
    align-items: flex-end;
}

.users-own-comment-modifier {
    align-self: flex-end;
}

.single-comment-name {
    font-size: 12px;
    margin: 0;
}

.comment-time-container{
    display: flex !important;
    flex-direction: row !important;
    align-items: end;
    font-size: 12px;
}

.comment-timestamp {
    display: none;
    opacity: 50%;
    margin-right: 5px;
    margin-left: 5px;
}

.remove-comment-btn {
    display: inline-block;
    color: inherit;
    cursor: pointer;
}

.remove-comment-btn:hover {
    font-weight: 700;
}

.comment-section-text-submit {
    height: 40px;
    border-radius: 10px;
}


/* nav tabs / phase pills / paper-files / pagination (consolidated) */
.paper-files {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    justify-content: flex-start;
    align-items: center;
    width: 100%;
    height: fit-content;
}

.paper-files a {
    padding: 10px 20px 10px 20px;
    border-radius: 10px;
    border: 1px solid rgba(0, 0, 0, 0.2);
    color: var(--brand-primary);
}

.paper-files a:hover {
    color: var(--surface-subtle);
    background-color: var(--brand-primary);
    border: 1px solid rgba(0, 0, 0, 0.2);
}

.page-link, .page-link:hover {
    color: var(--brand-primary);
}

.page-item input {
    width: 45px;
}

.page-item.active .page-link{
    color: black;
    background-color: var(--brand-primary-tint);
    border-color: var(--brand-primary-tint);
}

/* Bootstrap's warning amber #ffc107 fails contrast on white; use the accessible token */
.text-warning { color: var(--warning) !important; }

/* ===== Interactive-element primitives (B2 conversions) ===== */
/* Make a semantically-correct <button>/<a> render like the bare element it replaces */
.btn-reset {
    appearance: none;
    -webkit-appearance: none;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    text-align: inherit;
    cursor: pointer;
}

/* The row action menus ("...") were <div>s, which no keyboard could reach. As <button>s
   they default to inline-block and pick up baseline alignment, which nudged them a few
   pixels down inside their flex row; block keeps them exactly where the div sat. */
.action-options {
    display: block;
}

/* WCAG 2.5.8 (AA): a pointer target has to be at least 24x24 CSS px unless it sits
   inline in a sentence. Almost every button here measured 21-23px -- short by a pixel
   or three -- so a minimum box is enough. Deliberately min-* and not height/padding:
   nothing that already clears 24px moves at all.
   .popup-profile is excluded because those are author names set inline in running text
   (the inline exception applies), and a minimum box would push the lines apart. */
button:not(.popup-profile),
[role="button"]:not(.popup-profile) {
    min-height: 24px;
    min-width: 24px;
}

/* Links that act as controls rather than as words in a sentence -- tab strips, row
   titles, close affordances -- are targets under the same rule, but a blanket `a`
   would catch inline prose links that the inline exception already covers. */
.nav-tabs .nav-item > .nav-link,
.nav-tabs > .nav-link,
.recent-chat-tabs > .nav-item > .nav-link,
.folder-row-title,
.close-button-mini,
.popup-profile.editor-name,
.single-recent-message .popup-profile,
#messages-list > .link-unstyled {
    min-height: 24px;
    display: inline-block;
}

/* Anchor that should render like the bare element it replaces (e.g. a whole clickable row);
   neutralizes the global a{color:var(--link-color)} that would otherwise blue the row text. */
.link-unstyled, .link-unstyled:hover, .link-unstyled:focus {
    color: inherit;
    text-decoration: none;
}
/* F1 row-anchors that replaced a block <div>/<h*> need block display back (an <a> is inline
   by default, which collapses the card/row layout). Scoped to the converted containers that set
   no display of their own; .row/.folder-card/.deadline-card/.single-offer-container already flex. */
a.single-question-container,
a.evaluation-name,
a.journal-name,
a.new-message-paper-title {
    display: block;
}
a#submit-button {
    display: inline-block;
}

/* Canonical "back / exit this view" control — ONE icon (fa-arrow-left) + ONE style, docked at the
   leading edge of a page toolbar. Replaces the assorted arrow-left/exit-bracket/chevron-left
   variants across folder, forum, paper detail, evaluation, bulk-reject, review, journal. */
.back-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    background: none;          /* bare arrow, no circle/fill */
    color: var(--brand-primary);
    font-size: 1.35rem;
    line-height: 1;
    cursor: pointer;
    transition: opacity 0.15s ease, transform 0.1s ease;
}
.back-btn:hover,
.back-btn:focus {
    color: var(--brand-primary);
    opacity: 0.65;
    text-decoration: none;
}
.back-btn:active {
    transform: translateX(-2px);
}

/* File-upload rows: Bootstrap sizes .input-group-text off 1rem (16px) but the custom-file off
   `1.5em`, which resolves against the 14px body font — so file inputs render shorter than their
   sibling .form-control / prepend label. Pin both sides to the same 1rem basis APP-WIDE (covers
   plain .input-group as well as .form-group.input-group) so heights line up everywhere. */
.input-group > .input-group-prepend > .input-group-text,
.input-group > .custom-file,
.input-group > .custom-file > .custom-file-input,
.input-group > .custom-file > .custom-file-label {
    font-size: 1rem;
    height: calc(1.5em + 0.75rem + 2px);
}
.input-group > .custom-file > .custom-file-label::after {
    font-size: 1rem;
    height: calc(1.5em + 0.75rem);
}

/* Bootstrap squares the field's right corners whenever the .custom-file is not the last
   child, on the assumption something is butted against it. With the download button now
   sitting in its own space (see the gap rule below) nothing is, so that edge is free and
   should match the radius on the group's left end. The Browse cap is an ::after flush
   with the same edge, so it has to follow or a square grey corner shows through the
   rounded one.

   Mirrors Bootstrap's own selector rather than a shorter one: theirs is
   `.input-group > .custom-file:not(:last-child) .custom-file-label`, and anything less
   specific loses to it outright. This wins on source order, components.css being loaded
   after bootstrap.min.css. The :last-child case needs no override -- Bootstrap already
   leaves it rounded. */
.input-group > .custom-file:not(:last-child) > .custom-file-label,
.input-group > .custom-file:not(:last-child) > .custom-file-label::after {
    border-top-right-radius: 0.25rem;
    border-bottom-right-radius: 0.25rem;
}

/* Bootstrap 4 leaves this label unclipped -- clipping only arrived in 5 -- so a long file
   name wraps inside a fixed-height box and paints below it, outside the field and its tile.
   The Browse cap is an ::after pinned inside the same label (measured 72px), so the padding
   has to keep the name out from under it.

   This has to be its own rule. Folded into the list above it terminated that selector
   early, so .input-group-text / .custom-file / .custom-file-input silently swapped their
   font-size and height for overflow:hidden and an 80px right padding. The wrapper then
   sized off the 14px body font (35px) while the label kept its 38px, and clipped the
   label's bottom border off; the "CV" prepend inherited the padding and stretched to
   114px for two letters. */
.custom-file-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-right: 5rem;
}

/* Brianna: the download button sat flush against the Browse cap (Bootstrap overlaps the
   two by 1px to collapse their borders), so the two read as one control. Give it air and
   its own corners back -- it is a separate action, not part of the field. */
.input-group > .custom-file + .input-group-append > .btn {
    margin-left: 8px;
    border-radius: 0.25rem;
}
/* A .badge rendered as an interactive <button> (B2 conversion): keep the badge fill/padding/font,
   but strip the native button bevel/border/box-shadow so it looks like the original badge. */
button.badge {
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    box-shadow: none;
    cursor: pointer;
}

/* Keyboard focus ring only (mouse clicks don't trigger :focus-visible); overrides the outline:none reset above.
   .btn is named too: Bootstrap's .btn:focus { outline: 0 } out-specifies button:focus-visible, which left
   Finish Review, Send and every Bootstrap button with only a 1.6:1 shadow (1.4.11, recheck 2026-09-14). */
.btn-reset:focus-visible,
a:focus-visible,
button:focus-visible,
.btn:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}
/* The outline reset above also silenced the native ring on form controls that carry no
   .form-control class -- bare checkboxes, the auth pages' plain inputs. Keyboard focus
   gets the ring back (2.4.7); mouse focus keeps the quieter box-shadow treatment. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.form-control:focus-visible,   /* Bootstrap's .form-control:focus zeroes the outline and out-specifies the three above */
[contenteditable="true"]:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
}
/* Quill's editing surface is a contenteditable div; inset so the ring hugs the box */
.ql-editor:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -2px;
}

/* ===== Field errors =====
   Every form here reports a field's problem the same way: <p class="error" id="X-error">
   under the field, empty until a script or the server writes into it, and
   form-errors-a11y.js ties it to the field. Its look lives here once; the sign-in,
   profile and folder pages each restated it (a page keeps only its own spacing). */
.error {
    color: var(--danger);
    font-size: 14px;
    margin-top: 5px;
}
.error:empty {
    display: none;
}
/* The field an error names is outlined in the same red, with Bootstrap's circled "!"
   inside it, redrawn in that red (Bootstrap's is a second, lighter one, #dc3545). The
   circle is a shape as well as a colour, so the state never rests on colour alone (1.4.1). */
.form-control.is-invalid,
textarea.form-control.is-invalid,
.custom-file-input.is-invalid ~ .custom-file-label {
    border-color: var(--danger);
}
/* Focused, the field is where the author is typing its fix: Bootstrap's focus rules
   out-rank the ones above and would switch it to their red. */
.form-control.is-invalid:focus,
.custom-file-input.is-invalid:focus ~ .custom-file-label {
    border-color: var(--danger);
    box-shadow: 0 0 0 0.2rem rgba(178, 1, 1, 0.25);
}
.form-control.is-invalid {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23b20101' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23b20101' stroke='none'/%3e%3c/svg%3e");
}
/* A native select draws its own arrow at the right edge; the circle sits just inside it.
   Bootstrap 4.5.3 only moves it for .custom-select. */
select.form-control.is-invalid {
    padding-right: 3rem;
    background-position: right 1.75rem center;
}

/* ===== Resizable columns (components/table-resize.js) =====
   The handle is a real button at the header's right edge, 24px wide so it is a
   target (2.5.8) while its visible line is 2px; it straddles the edge, so half of
   it sits over the next header. */
table[data-resizable] th { position: relative; }
table[data-resizable].is-resizing { cursor: col-resize; user-select: none; }
/* A header narrowed below its text truncates like a cell. The clip is on the TEXT
   (the sort link, or a .th-label span), never on the th: the handle straddles the
   th's edge, and overflow:hidden there would cut its outer half -- and its 24px
   target -- away (owner, 2026-09-10). */
table[data-resizable] th > .th-sort,
table[data-resizable] th > .th-label {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* A div with role=separator (the ARIA window-splitter), not a button: a button is
   what Bootstrap's reboot paints a hand on (owner, 2026-09-10). */
th > .col-resize {
    position: absolute;
    top: 0;
    right: -12px;
    width: 24px;
    height: 100%;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: col-resize;
    z-index: 1;
    touch-action: none;
}
th > .col-resize::after {
    content: '';
    position: absolute;
    top: 20%;
    bottom: 20%;
    left: 11px;
    width: 2px;
    border-radius: 1px;
    background: var(--border-color);
}
.col-resize:hover::after,
.col-resize:focus-visible::after { background: var(--brand-primary); }
.col-resize:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }

/* ===== Assignee panel (shared: the folder table's cell · the Evaluation page) =====
   A trigger beside a line of names opens the assign-editor list below in a floating
   box. Behaviour: components/assignee-panel.js. The box is at least the list plus
   its own padding, from the variable the list reads -- an absolute box inside a
   table cell is otherwise the cell's width, and the list overflowed it. */
.assignee-panel { position: relative; }
.assignee-panel .evaluation-options-dropdown {
    display: none;
    position: absolute;
    right: 0;
    top: 100%;
    z-index: 50;
    min-width: calc(var(--assign-list-width) + 16px);
    padding: 8px;
    background-color: #fff;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    box-shadow: rgba(18, 49, 80, 0.18) 0 6px 18px;
    text-align: left;
}
.evaluation-menu-label {
    margin: 0.3rem 0.6rem 0.4rem;
    font-size: 14px;   /* 14px minimum (Lauren, Basecamp 2026-08-31 and 2026-09-10) */
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--text-muted);
}
/* The Evaluation page's shape: the chips sit under the heading, the admin's Edit chip
   last among them, and the box opens beneath them at the left. */
.assignee-panel-section .assignee-names { margin: 0; }
/* The assigned editors as chips: avatar + full name, each a .popup-profile trigger. */
.assignee-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin: 0;
}
/* One box for every chip: 32px tall (26px avatar + 3px above and below), the border
   drawn inside so the Edit chip sits on the same line as its neighbours. */
.assignee-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    box-sizing: border-box;
    height: 32px;
    padding: 0 12px 0 3px;
    border: 1px solid transparent;
    border-radius: 999px;
    background: var(--brand-primary-tint);
    color: var(--brand-primary);
    font-size: 14px;   /* 14px minimum (Lauren, Basecamp 2026-08-31) */
    /* 400, what the name has always rendered at: base.css's * reset flattened a 600
       here on every page, and that is the chip the owner approved. The evaluation page
       now lets weights inherit (evaluation.css), so the declaration says what shows. */
    font-weight: 400;
    line-height: 1.25;
    cursor: pointer;
}
.assignee-chip:hover,
.assignee-chip:focus-visible {
    background: #dbe3ec;
    color: var(--brand-primary);
    text-decoration: none;
}
.assignee-chips-empty { display: inline-flex; align-items: center; }
/* The Edit chip: quiet -- a hairline border, muted text, no fill -- until it is
   hovered, focused, or its panel is open. */
.assignee-chip-edit {
    padding: 0 12px;
    background: transparent;
    border-color: var(--border-color);
    color: var(--text-muted);
    font-weight: 500;
}
.assignee-chip-edit .fa-pen { font-size: 12px; }
.assignee-chip-edit:hover,
.assignee-chip-edit:focus-visible,
.assignee-chip-edit[aria-expanded="true"] {
    background: var(--brand-primary-tint);
    border-color: var(--brand-primary-tint);
    color: var(--brand-primary);
}
.assignee-chip-avatar {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
}
.assignee-panel-section .evaluation-options-dropdown { left: 0; right: auto; }

/* ===== Assign-editor control (shared: Review mode · Evaluation page · Folder detail) =====
   ONE list, ONE pill per editor. The pill flips inline (no page reload) through three states:
     Assign   → outline, navy   (not assigned)
     Assigned → solid navy      (assigned; resting)
     Remove   → solid red       (assigned; hover/focus — the un-assign affordance)
   Markup: dashboard/evaluation/evaluation_assign_to.html · behaviour: components/assign-toggle.js */
/* The list's one width, wherever it appears -- the folder panel, Review mode's
   dropdown, the Evaluation page -- so the three surfaces line up (owner, 2026-09-10).
   A variable because the folder panel has to know it too: see folder.css. */
:root { --assign-list-width: 21rem; }
/* The list scrolls itself (below); a box that holds it must not scroll as well, or
   there are two scrollbars -- Review Mode's Assignments chip opens one such box. */
.dropdown-menu-container .dropdown-content:has(.editor-assign-list) {
    max-height: none;
    overflow: visible;
}
.editor-assign-list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: var(--assign-list-width);
    max-height: 22rem;
    overflow-y: auto;
}
.editor-assign-search {
    position: sticky;
    top: 0;
    z-index: 1;
    margin-bottom: 0.25rem;
}
.editor-assign-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.5rem;
    border-radius: 0.5rem;
    transition: background-color 0.12s ease;
}
.editor-assign-row:hover {
    background: var(--surface-subtle);
}
.editor-assign-avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
}
.editor-assign-meta {
    display: flex;
    flex-direction: column;
    min-width: 0;
    margin-right: auto;
    line-height: 1.25;
}
.editor-assign-name {
    font-weight: 600;
    color: var(--text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.editor-assign-perm {
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* The pill itself */
.assign-toggle {
    /* One grid cell holds all three labels (below), so the pill is as wide as the
       widest of them in EVERY state and never changes size as it flips (owner,
       2026-09-10). Visibility, not display, does the switching, so the hidden two
       still take part in sizing. */
    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    padding: 0.3rem 0.85rem;
    border: 1.5px solid var(--brand-primary);
    border-radius: 999px;
    background: transparent;
    color: var(--brand-primary);
    font-size: 0.8125rem;
    /* 400, what the labels have always rendered at on every page (base.css's * reset
       flattened a 600 declared here); now that the evaluation page lets weights
       inherit, 600 would have made its pills bolder and wider than the folder page's. */
    font-weight: 400;
    line-height: 1.15;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.assign-toggle:hover { background: var(--brand-primary-tint); }
.assign-toggle .at-icon { font-size: 0.75em; }
.assign-toggle .at-label {
    grid-area: 1 / 1;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    visibility: hidden;
}
/* three labels live in the DOM; CSS shows exactly one at a time by state */
.assign-toggle .at-label-assign { visibility: visible; }

/* Assigned (resting): solid navy, "Assigned ✓" */
.assign-toggle.is-assigned {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: #fff;
}
.assign-toggle.is-assigned .at-label-assign { visibility: hidden; }
.assign-toggle.is-assigned .at-label-assigned { visibility: visible; }
/* Hovering an assigned pill that is NOT offering Remove (the one just flipped, still
   under the pointer) shows the resting Assigned look. This rule used to set the
   Assign hover tint "to neutralise the base hover", which put white text on the pale
   tint the moment a pill flipped -- the old button's hover colour on the new button
   (owner, 2026-09-10). */
.assign-toggle.is-assigned:hover { background: var(--brand-primary); }

/* Assigned + hover/focus: red "Remove ✕" (the un-assign affordance). Not on the
   pill that was just flipped (.just-assigned, cleared when the pointer or focus
   leaves) -- it keeps focus now instead of being blurred away (#122). */
.assign-toggle.is-assigned:not(.just-assigned):hover,
.assign-toggle.is-assigned:not(.just-assigned):focus-visible {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff;
}
.assign-toggle.is-assigned:not(.just-assigned):hover .at-label-assigned,
.assign-toggle.is-assigned:not(.just-assigned):focus-visible .at-label-assigned { visibility: hidden; }
.assign-toggle.is-assigned:not(.just-assigned):hover .at-label-remove,
.assign-toggle.is-assigned:not(.just-assigned):focus-visible .at-label-remove { visibility: visible; }

.assign-toggle[aria-busy="true"] { opacity: 0.55; cursor: progress; }

/* Deep-linked message flash — a recent-message link (?msg=<id>) scrolls to the message
   with id="message-<id>" and briefly highlights it. See components/message-anchor.js.
   The message bubble is width:fit-content, so a plain background hugs the text; a
   same-colour box-shadow ring extends the highlight ~7px beyond it (reads as padding)
   and fades in sync — no layout shift. */
@keyframes message-highlight-fade {
    0% {
        background-color: rgba(54, 89, 150, 0.22);
        box-shadow: 0 0 0 7px rgba(54, 89, 150, 0.22);
    }
    100% {
        background-color: transparent;
        box-shadow: 0 0 0 7px rgba(54, 89, 150, 0);
    }
}
.chat-message.message-highlight {
    animation: message-highlight-fade 2.6s ease-out;
    border-radius: 14px;
}


/* Two-layer filter menu: a Filters toggle opens a list of fields, each field flying its
   options out beside it. Built for the folder-detail bar, now also the journal pickers
   (submit paper step 4, extend submission) -- which is why it lives here and not in
   folder.css. Mechanics in components/filter-menu.js; each page owns its own state. */
.filter-dd-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 34px;
    padding: 0 10px;
    border: 1px solid var(--input-border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text-color);
    font-size: 14px;   /* 14px minimum (Lauren, Basecamp 2026-09-10) */
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}

.filter-dd-toggle:hover {
    border-color: var(--brand-primary);
}

/* An applied filter is named on its own button, not just tinted (WCAG 1.4.1). */
.filter-dd-toggle.is-active {
    border-color: var(--brand-primary);
    background: var(--brand-primary-tint);
    color: var(--brand-primary);
}

.filter-dd-value {
    max-width: 15ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 400;
}

.filter-dd-value::before {
    content: ": ";
}

.filter-dd-caret {
    font-size: 9px;
    opacity: 0.7;
}

/* A list of fields; each row opens its own options to the right. A flat panel showed all
   nine controls whether or not you wanted them. */
.filter-menu {
    width: 268px;
    padding: 6px;
    border-color: var(--border-color);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(18, 49, 80, 0.14);
}

.filter-menu-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.filter-menu-row {
    position: relative;
}

.filter-menu-field {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    border-radius: 6px;
    font-size: 13px;
    color: var(--text-color);
    cursor: pointer;
}

.filter-menu-row:hover > .filter-menu-field,
.filter-menu-row.is-open > .filter-menu-field {
    background: var(--surface-subtle);
}

.filter-menu-name {
    flex: 1 1 auto;
    text-align: left;
}

/* How many options this field is holding -- the row says so without opening. */
.filter-menu-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--brand-primary);
    color: var(--surface);
    font-size: 11px;
    font-weight: 700;
}

.filter-menu-caret {
    font-size: 9px;
    opacity: 0.55;
}

.filter-menu-row.is-set > .filter-menu-field .filter-menu-name {
    font-weight: 600;
    color: var(--brand-primary);
}

/* Flies out beside the row it belongs to. */
.filter-submenu {
    display: none;
    position: absolute;
    top: -6px;
    left: 100%;
    z-index: 10;
    width: 250px;
    margin-left: 4px;
    padding: 8px;
    background: var(--surface);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(18, 49, 80, 0.14);
}

/* Near the right edge there is no room to the right, so it opens leftward instead. */
.filter-menu-row.opens-left > .filter-submenu {
    left: auto;
    right: 100%;
    margin: 0 4px 0 0;
}

.filter-menu-row.is-open > .filter-submenu {
    display: block;
}

.filter-submenu-search {
    height: 32px;
    margin-bottom: 6px;
    border-radius: 6px;
    border-color: var(--input-border);
    font-size: 13px;
}

.filter-submenu-options {
    /* Tall enough that the 8 ranking tiers show whole -- 240px cut the list off at
       exactly the last option, which reads as a shorter list, not a scrollable one.
       Long lists (52 subjects, fields of law) still scroll, with search above them. */
    max-height: min(340px, 55vh);
    overflow-y: auto;
}

.filter-submenu-option {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0;
    padding: 5px 6px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 400;
    color: var(--text-color);
    cursor: pointer;
}

.filter-submenu-option:hover {
    background: var(--surface-subtle);
}

.filter-submenu-option input {
    margin-top: 3px;
    flex: 0 0 auto;
    accent-color: var(--brand-primary);
}

.filter-submenu-label {
    display: block;
    margin: 4px 0 2px;
    font-size: 12px;
    font-weight: 600;
    color: var(--brand-primary);
}

.filter-submenu .form-control[type="date"] {
    height: 32px;
    border-radius: 6px;
    border-color: var(--input-border);
    font-size: 13px;
}

.filter-submenu-empty {
    margin: 6px;
    color: var(--text-muted);
    font-size: 13px;
}

.filter-menu-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-top: 6px;
    padding: 8px 6px 2px;
    border-top: 1px solid var(--border-color);
}

/* Count on the Filters button: how much is hidden behind it. */
.filter-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--brand-primary);
    color: var(--surface);
    font-size: 11px;
    font-weight: 700;
}

.filter-dd-toggle.is-active .filter-count {
    background: var(--brand-primary);
    color: var(--surface);
}

/* The one-line lead under a page's h1 (Tools; Journal Activity and Anonymous Q&A
   since the forum split): what the page is for, in one sentence. One home, so the
   three pages that open this way look the same. */
.page-subtitle {
    font-size: 15px;
    color: #5a6472;
    margin: 6px 0 12px;
}

/* Best-view width shell. Column-of-content pages (forms, profiles, lists) opt in by
   putting this on their content root; wide-by-design surfaces (dashboard, evaluation,
   review mode, data tables) stay full width. Matches the folders / forum cap. */
.page-shell {
    width: 95%;
    max-width: 1080px;
    margin-left: auto;
    margin-right: auto;
}

/* ---- Section hint: one line under a section heading saying what it contains ---- */
.section-hint {
    margin: -2px 0 10px;
    font-size: 12px;
    line-height: 1.45;
    max-width: 70ch;
    color: var(--text-muted);
}

/* ---- Shared empty state (tables, lists, chats, dashboard panels) ---- */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 6px;
    min-height: 110px;
    padding: 22px 20px;
}
.empty-state-icon {
    font-size: 17px;
    color: var(--text-color);
    /* Faded rather than a lighter colour token: one rule reads correctly on white,
       tinted, and grey surfaces alike. */
    opacity: 0.3;
}
.empty-state-title {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
}
.empty-state-hint {
    margin: 0;
    font-size: 12px;
    line-height: 1.45;
    max-width: 40ch;
    color: var(--text-muted);
}
.empty-state-hint a {
    text-decoration: underline;
}
/* On the navy sidebar: the same component, inverse ink (#47). */
.empty-state--inverse .empty-state-icon { color: #fff; opacity: 0.5; }
.empty-state--inverse .empty-state-title,
.empty-state--inverse .empty-state-hint { color: var(--text-inverse-muted); }
/* Menus that hold a short list (folders, offers, assignees): the one-line empty (#140). */
.dropdown-empty {
    margin: 0;
    padding: 6px 10px;
    font-size: 13px;
    color: var(--text-muted);
    white-space: nowrap;
    text-align: left;
}

/* ==================== Shared component library (2026-08-21) ====================
   Canonical classes first in each group; legacy per-page names ride along as
   aliases until their markup migrates. Page stylesheets keep only local size and
   layout overrides. folder.css / submission_forum.css still carry the original
   table + search styling this library was distilled from -- migrate them
   opportunistically when those pages are next touched. */

/* ---- Menu chrome: one dropdown look ---- */
.app-menu,
.image-options,
.role-menu,
#editor .associate-editor-list .dropdown-menu,
.institutional-account-page .dropdown-menu {
    padding: 6px 0;
    background: var(--surface);
    border: 1px solid #e6e8eb;
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.app-menu .dropdown-header,
.role-menu .dropdown-header {
    font-size: 10px;
    color: var(--text-muted);
    padding: 4px 14px 6px;
}
.app-menu .dropdown-item,
#editor .associate-editor-list .dropdown-item,
.institutional-account-page .dropdown-item {
    padding: 7px 14px;
    font-size: 14px;
}

/* ---- Tab switcher strip: white bordered container, navy underlined active.
   One look for every page's switcher (profile, forum, evaluation, AI tools,
   institutional accounts, contact). ---- */
.nav-tabs {
    border: 1px solid grey;
    border-radius: 5px;
    padding: 15px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
}
.nav-tabs .nav-link {
    color: #495057;
    margin-inline: 20px;
    border: none;
    border-radius: 0.25rem;
    font-weight: bold;
}
.nav-tabs .nav-link.active {
    color: var(--brand-primary);
    background-color: transparent;
    text-decoration: underline;
    text-decoration-thickness: 3px;
    text-underline-offset: 8px;
}

/* ---- Table language: muted labels over a rule; hairline rows, quiet hover ---- */
/* ---- Form control language (#113, #130): the quiet navy label, inputs on an 8px
   radius. Lifted from the submission wizard so every form reads the same. ---- */
label.control-label {
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.2px;
    color: var(--brand-primary);
    margin-bottom: 4px;
}
form .form-control {
    border-radius: 8px;
    border-color: var(--input-border);
}
/* select2 replaces the <select> with markup of its own, so the rule above never
   reaches it. */
form .select2-container--bootstrap-5 .select2-selection {
    border-radius: 8px;
    border-color: var(--input-border);
}
form .custom-file-label {
    border-radius: 8px;
    border-color: var(--input-border);
    color: var(--text-muted);
}
form .custom-file-label::after {
    background: var(--surface-subtle);
    color: var(--brand-primary);
    font-weight: 600;
    border-radius: 0 7px 7px 0;
}
/* The Total / Available Credit pair on the wizard's review step and the extend form. */
.payment-info-container :is(h6, .h6) {
    margin-bottom: 0;
    color: var(--brand-primary);
    font-size: 15px;
}
.payment-info-container p {
    font-size: 15px;
    font-weight: 500;
    margin-bottom: 12px;
}

/* One header spec (#108): the four <table>s, both div rosters and the forum's
   decision header all carry .app-table-header; page sheets keep only padding
   and layout. --table-row-hover is the one hover tint. */
:root { --table-row-hover: #f7f7fa; }

/* The floating box components/tip.js draws for any [data-tip] (the folder table's pin
   and text cells, the review-mode rail). Fixed and on <body>, so no scroller clips it;
   above the modal layer so a tip inside a dialog is not painted under its backdrop.
   Wraps at a readable width -- a full paper title is the longest text it carries. */
.tip-box {
    position: fixed;
    z-index: 1100;
    max-width: 24rem;
    padding: 3px 7px;
    border-radius: 4px;
    background: var(--brand-primary);
    color: #fff;
    font-size: 14px;   /* 14px minimum (Lauren, Basecamp 2026-08-31 and 2026-09-10) */
    font-weight: 500;
    line-height: 1.3;
    overflow-wrap: anywhere;
    box-shadow: 0 2px 8px rgba(18, 49, 80, 0.25);
}
/* The bridge: an invisible strip of the box spanning the 5px gap to its anchor, so the
   pointer can cross from one to the other without leaving either (WCAG 1.4.13). */
.tip-box::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 5px;
}
.tip-box.is-above::after { top: 100%; }
.tip-box.is-below::after { bottom: 100%; }
.app-table-header,
.app-table-header th,
#editor .single-member-container.header,
.institutional-account-page .single-member-container.header,
.payment-history-column {
    background-color: transparent;
    border-radius: 0;
    border-bottom: 2px solid var(--border-color);
    /* 14px minimum (Lauren, Basecamp 2026-09-10). One rule, so this moves EVERY
       table header in the app -- the folder table she was looking at, the member
       lists, payment history and the decision header -- which is the point of it
       being one rule. */
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.3px;
    color: var(--text-muted);
    text-align: left;
    white-space: nowrap;
    vertical-align: middle;
}
.app-table-header th { border-top: none; }
/* A sortable header is a control in the header's own face -- no link blue, no
   underline at rest -- with HeinOnline's double arrow beside its label (Lauren, Basecamp
   2026-09-11: sortable was not clear at a glance); the sorted one wears the brand colour
   and the arrow of its direction instead. aria-sort on the cell is what actually carries the state. One
   language for every table that sorts from its headers: the folder table's links,
   the editorial board's buttons (owner, 2026-09-10: the roster looked different). */
.th-sort {
    color: inherit;
    text-decoration: none;
}
.th-sort:hover,
.th-sort:focus-visible {
    color: var(--brand-primary);
    text-decoration: underline;
}
.th-sort.is-sorted { color: var(--brand-primary); }
.th-sort-dir {
    margin-left: 0.35rem;
    font-size: 12px;   /* a caret beside 14px text (2026-09-05) */
}
.app-table-row,
#editor .single-member-container,
.institutional-account-page .single-member-container,
.payment-history-header {
    border-bottom: 1px solid var(--border-color);
}
.app-table-row:hover,
#editor .single-member-container:not(.header):hover,
.institutional-account-page .single-member-container:not(.header):hover,
.institutional-account-page .payment-history-header:hover,
.payment-history-header.active,
.folder-row:hover,
.single-decision-container:hover {
    background-color: var(--table-row-hover);
}

/* ---- Status dots ---- */
.status-dot, .journal-dot, .jd-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
}
.status-dot.is-open, .journal-dot.is-open, .jd-dot.is-open { background-color: var(--success); }
.status-dot.is-closed, .journal-dot.is-closed, .jd-dot.is-closed { background-color: var(--danger); }
.status-dot.is-pending, .journal-dot.is-pending { background-color: var(--warning); }

/* ---- Rail sections: icon tile + serif title left, fields right ---- */
.rail-section, .profile-section, .jd-section {
    display: grid;
    grid-template-columns: 250px minmax(0, 1fr);
    column-gap: 48px;
    padding: 30px 0;
}
.rail-section + .rail-section,
.profile-section + .profile-section,
.jd-section + .jd-section {
    border-top: 1px solid var(--border-color);
}
.rail-section-head, .profile-section-head {
    padding-top: 2px;
    position: sticky;
    top: 24px;
    align-self: start;
}
.rail-section-titlerow, .profile-section-titlerow {
    display: flex;
    align-items: center;
    gap: 12px;
}
.rail-section-icon, .profile-section-icon, .jd-section-icon {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background-color: var(--brand-primary);
    color: #fff;
    font-size: 15px;
    box-shadow: 0 3px 8px rgba(18, 49, 80, 0.22);
}
.rail-section-title, .profile-section-title, .jd-section-title, .status-review-heading {
    margin: 0;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 20px;
    font-weight: 700;
    letter-spacing: 0.2px;
    color: var(--brand-primary);
}
.rail-section-desc, .profile-section-desc, .status-review-lead {
    margin: 8px 0 0 50px;   /* aligns under the title, clear of the icon tile */
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-muted);
}
.rail-section-fields, .profile-section-fields, .status-review-body {
    min-width: 0;
}
@media (max-width: 900px) {
    .rail-section, .profile-section, .jd-section {
        grid-template-columns: 1fr;
        row-gap: 12px;
        padding: 22px 0;
    }
    .rail-section-head, .profile-section-head {
        position: static;
    }
}

/* ---- Search with icon + filled action ---- */
.app-search, .jl-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
}
.app-search-icon, .jl-search-icon {
    position: absolute;
    left: 12px;
    font-size: 13px;
    color: #5c656d;
    pointer-events: none;
}
.app-search input, .jl-search input {
    height: 34px;
    padding: 0 12px 0 34px;
    font-size: 13px;
    border: 1px solid #e6e6e6;
    border-radius: 8px;
}
.app-search input:focus, .jl-search input:focus {
    outline: 2px solid #5187bd;
    outline-offset: 1px;
    border-color: #e6e6e6;
}
.app-search-btn, .jl-search-btn {
    flex: 0 0 auto;
    white-space: nowrap;
    height: 34px;
    padding: 0 16px;
    border: 1px solid var(--brand-primary);
    border-radius: 8px;
    background-color: var(--brand-primary);
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}
.app-search-btn:hover, .jl-search-btn:hover {
    background-color: #0d2540;
}
.app-search-btn:focus-visible, .jl-search-btn:focus-visible {
    outline: 2px solid #5187bd;
    outline-offset: 2px;
}

/* Assign-editors rows (evaluation panel, folder-detail dropdown, review panel --
   the shared include renders on pages with different stylesheets, so the cap
   lives here): scroll under a cap; the search input stays pinned above. */
.editor-assign-rows {
    max-height: 320px;
    overflow-y: auto;
}

/* ---- My Memberships list (shared: profile Journal tab + dashboard join state) ----
   Restored 2026-08-22: a dead-selector sweep deleted these because it grepped
   personal_profile.html, where the markup lived at the start of the week, and
   missed that the same week had moved it into journal_memberships.html. Any
   future sweep must grep templates AS A SET, not per file.
   Re-styled on the way back in: hairline rows and a danger pill, not the old
   bordered cards, since every neighbour was rebuilt this week. */
.membership-list {
    list-style: none;
    padding: 0;
    margin: 0;
}
.membership {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 10px;
    border-bottom: 1px solid var(--border-color);
    transition: background-color 0.15s ease;
}
.membership:hover {
    background-color: var(--table-row-hover);
}
/* The current membership carries no tint (owner, 2026-09-10): its meta already says
   "currently viewing", and a filled row read as a selection on the Personal tab. */
.membership-body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.membership-name {
    font-size: 15px;
    font-weight: 600;
    color: var(--brand-primary);
    line-height: 1.3;
    overflow-wrap: anywhere;
}
.membership-meta {
    font-size: 12px;
    color: var(--text-muted);
    line-height: 1.45;
}
.membership-sep {
    margin: 0 6px;
    opacity: 0.55;
}
.membership-current {
    font-weight: 600;
    color: var(--brand-primary);
}
/* Leave/Withdraw is destructive: the shared danger-outline pill, one size down. */
.membership-action {
    margin-left: auto;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    height: 30px;
    padding: 0 16px;
    border: 1px solid var(--danger);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    color: var(--danger);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.15s ease;
}
.membership-action:hover {
    background-color: rgba(178, 1, 1, 0.06);
}
.membership-action:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}
/* The name switches to that journal; underline on hover only -- the row already
   carries a Leave button and a second boxed control would crowd it. */
.membership-switch-form {
    display: inline;
    margin: 0;
}
button.membership-switch {
    cursor: pointer;
    color: var(--brand-primary);
    text-align: left;
}
button.membership-switch:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}
button.membership-switch:focus-visible {
    outline: 2px solid var(--brand-primary);
    outline-offset: 2px;
}

/* The memberships container sits inside a section's fields column now -- the rail
   provides the header and the section the rule, so the standalone chrome goes. */
.profile-section-fields .journal-info-container {
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
}

/* A picture that is a control (V2 audit #28/#45): the button wraps the image
   and adds nothing to it -- no inline gap under the image, the focus ring from
   .btn-reset:focus-visible above. */
.image-trigger {
    display: block;
    line-height: 0;
}

/* ==================== Load-more controls (M2, 2026-09-02) ====================
   Three places page what they used to load whole. Each control borrows the shape
   its surface already uses: the chat's time-marker chip, the wizard's "Load More
   Journals" text button (on the sidebar's inverse palette), and a rail row. */
/* The day and time markers and this chip are plain centred text: no box (owner, 2026-09-02). */
.chat-day-divider, .chat-day-divider.chat-load-earlier-wrap { background: transparent; padding: 0; border-radius: 0; }
.chat-load-earlier {
    font: inherit;
    color: var(--link-color);
    padding: 2px 10px;
    cursor: pointer;
}
.chat-load-earlier:hover, .chat-load-earlier:focus-visible { text-decoration: underline; }
.chat-load-earlier[disabled] { color: var(--text-muted); cursor: progress; }
.activity-load-older,
.messages-load-more {
    font-size: 13px;
    color: var(--text-inverse);
    text-decoration: underline;
    cursor: pointer;
}
.activity-load-older[disabled],
.messages-load-more[disabled] { color: var(--text-inverse-muted); cursor: progress; }

/* The drawer's Messages tab (M2, 2026-09-02): one block per conversation on the
   sidebar's palette -- the title on its own line with the unread count as the
   tab badge's pill, the side links beneath it for a seat. */
.drawer-thread { padding: 0.85rem 0; border-bottom: 1px solid rgba(255, 255, 255, 0.35); }
.drawer-thread:last-of-type { border-bottom: 0; }
.drawer-thread-main {
    display: flex;
    align-items: center;   /* the count and the chevron sit mid-row, however the title wraps */
    gap: 0.6rem;
    color: var(--text-inverse);
}
.drawer-thread-main:hover .drawer-thread-title, .drawer-thread-main:focus-visible .drawer-thread-title { text-decoration: underline; }
.drawer-thread-title { flex: 1 1 auto; font-weight: 600; line-height: 1.35; }
.drawer-thread-count {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem;
    height: 1.4rem;
    padding: 0 0.45rem;
    border-radius: 0.7rem;
    /* A quiet count on the sidebar's own palette, not an alert colour (owner, 2026-09-02). */
    background: rgba(255, 255, 255, 0.16);
    color: var(--text-inverse);
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1;
}
.drawer-thread-count--small { min-width: 1.15rem; height: 1.15rem; font-size: 0.7rem; margin-left: 0.3rem; }
.drawer-thread-chevron { flex: 0 0 auto; color: var(--text-inverse-muted); }
/* No slide on hover: it pushed the chevron past the panel's edge. The row underlines instead. */
.drawer-thread-main:hover .drawer-thread-chevron, .drawer-thread-main:focus-visible .drawer-thread-chevron { color: var(--text-inverse); }
.drawer-thread-sides { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin-top: 0.45rem; }
.drawer-thread-side { display: inline-flex; align-items: center; font-size: 0.85rem; color: var(--text-inverse-muted); }
a.drawer-thread-side:hover, a.drawer-thread-side:focus-visible { color: var(--text-inverse); text-decoration: underline; }

/* Unread counts in the author chat's header and journal list (2026-09-02): quiet
   pills in the surface's own palette, never a red dot. */
.chat-unread-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 0.35rem;
    border-radius: 0.55rem;
    background: rgba(255, 255, 255, 0.22);
    color: inherit;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
}
.chat-unread-count--row {
    position: absolute;
    top: 50%;
    right: 0.75rem;
    transform: translateY(-50%);
    background: var(--brand-primary);
    color: var(--text-inverse);
}

/* The workspace refusal (journals/switch_to_continue.html): a folder or an
   evaluation the reader holds a seat for, reached from another workspace. Sized like
   a short page, not a modal -- it replaces the page at that page's own URL. */
.switch-to-continue {
    max-width: 40rem;
    margin: 3rem auto;
    padding: 2rem;
    border: 1px solid var(--border-color);
    border-radius: 0.5rem;
    background: var(--surface);
}
.switch-to-continue-title {
    margin: 0 0 0.75rem;
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--text-color);
}
.switch-to-continue-lede {
    margin: 0 0 1.5rem;
    color: var(--text-muted);
    line-height: 1.6;
}
.switch-to-continue-form {
    margin: 0 0 1rem;
}
.switch-to-continue-back {
    display: inline-block;
    color: var(--text-muted);
}


/* ============================ App bar (the frame pages) ============================ */
/* The 64px bar the evaluation page and Review Mode render in place of the logo band
   (dashboard/evaluation/_app_bar.html): the logo at 36px, the four evaluation sections,
   and at the right the Submissions picker, the position readout and Previous / Next.
   The right padding keeps the pager clear of the message drawer's tab (base.css). */
/* ================= The evaluation page's header (Ben's "Review Mode -- option C",
   2026-09-14) =================================================================
   One header owns the paper, on a two-row grid: on the left the paper's identity over
   its four sections, flush with the logo; on the right the queue as one card spanning
   both rows -- every stage with its count, and as the card's footer the way through the
   queue. The card is fixed-width and carries every phase, so Previous and Next never
   move as titles or queues change. --app-bar-h is the one place the height is set; the
   frame reads it too (base.css). */
:root {
    --app-bar-h: 117px;
    /* The right column on every tab of the evaluation page -- Review Mode's evaluation
       panel and the side panels of the other three -- reads this one width, so switching
       tabs never moves it (owner, 2026-09-14: the four were 574 / 400 / 400 / 400). The
       clamp is the owner's earlier call for a larger panel on a wide screen. */
    --eval-panel-w: clamp(400px, 38%, 600px);
    /* Two brand shades the header needs and the palette did not name: the hover/active
       face of the navy, and the quiet blue the author line and icons take. */
    --brand-deep: #0d2540;
    --brand-steel: #29527e;
}
.app-bar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: 64px 52px;
    column-gap: 24px;
    height: var(--app-bar-h);
    padding-right: var(--drawer-tab-w, 72px);
    border-bottom: 1px solid var(--border-color);
    background: var(--surface);
    overflow: hidden;
}

/* ---- row 1, left: the paper ---- */
.arthead {
    grid-area: 1 / 1;
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
    padding: 0 14px;
    position: relative;
}
/* Scoped to the bar: base.css's .app-logo (76px) loads after this file and would win a
   single-class tie. */
.app-bar .app-logo {
    height: 40px;
    width: auto;
    flex: 0 0 auto;
}
.app-bar .back-btn {
    flex: 0 0 auto;
}
.app-bar-divider {
    width: 1px;
    height: 26px;
    background: var(--border-color);
    flex: 0 0 1px;
}
.artid {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
/* The inner span is the clipping box, not the <p>: tip.js measures [data-tip-clip] to
   decide whether to show the whole title, and an inline span always measures zero. */
.arttitle {
    display: flex;
    margin: 0;
    font-family: Georgia, "Times New Roman", Times, serif;
    font-size: 18px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--text-color);
    min-width: 0;
}
.arttitle > span {
    font-family: inherit;   /* base.css sets the sans face on *, which beats inheritance */
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.artmeta {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    font-size: 12.5px;
    color: var(--text-muted);
    white-space: nowrap;
}
.artby {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.artby .popup-profile {
    color: var(--brand-steel);
    font-size: inherit;
}
/* The status pill takes its colour from the phase on .artid, which is also what tints
   the matching stage box -- one source for both. */
/* One source for the phase's colour, wherever the phase is shown: the header's pill,
   the matching stage box, and the pill in a side panel. */
.artid { --st: var(--text-muted); }
/* A pill only sets --st when it carries its own phase; otherwise it inherits the one
   its container set, which is how the header's pill takes its colour from .artid. */
.spill[data-st] { --st: var(--text-muted); }
.artid[data-st="ur"], .spill[data-st="ur"] { --st: var(--warning); }
.artid[data-st="oe"], .spill[data-st="oe"] { --st: var(--info); }
.artid[data-st="ip"], .spill[data-st="ip"] { --st: var(--success); }
.artid[data-st="c"],  .spill[data-st="c"]  { --st: var(--danger); }
.artid[data-st="w"],  .spill[data-st="w"]  { --st: var(--text-muted); }
.spill {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 9px;
    border-radius: 10px;
    font-size: 11.5px;
    font-weight: 500;
    line-height: 1;
    color: var(--st);
    background: color-mix(in srgb, var(--st) 8%, #fff);
    flex: 0 0 auto;
}
.detbtn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 22px;
    padding: 0 7px;
    margin-left: -3px;
    border: 0;
    border-radius: 5px;
    background: none;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--brand-steel);
    flex: 0 0 auto;
    cursor: pointer;
}
.detbtn svg {
    width: 11px;
    height: 11px;
}
.detbtn:hover {
    background: var(--surface-subtle);
    color: var(--brand-primary);
}
.detbtn[aria-expanded="true"] {
    background: var(--brand-primary-tint);
    color: var(--brand-primary);
}
/* The fold drops from the button rather than pushing the header taller. */
/* The Details pop-up (Ben's handoff): a 440px dialog under its button -- a label / value
   grid, the keywords as small tags, and a way through to Submission Info. It hangs off
   the frame rather than the bar, because the bar clips its own overflow to keep the
   two-row grid from growing; review.js sets --details-x to the button's left edge. */
.main-frame > .details {
    position: absolute;
    top: 62px;
    left: var(--details-x, 62px);
    z-index: 6;
    width: 440px;
    max-width: calc(100% - 28px);
    padding: 16px 18px;
    border: 1px solid #cfd7e0;
    border-radius: 10px;
    background: var(--surface);
    box-shadow: 0 12px 34px rgba(13, 37, 64, 0.2);
}
.detgrid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 9px 22px;
    margin: 0;
    font-size: 13px;
    line-height: 1.35;
}
.detgrid > div {
    display: contents;
}
.detgrid dt {
    font-weight: 400;
    color: var(--text-muted);
}
.detgrid dd {
    margin: 0;
    color: var(--text-color);
}
.details .kwlabel {
    margin: 14px 0 0;
    font-size: 11.5px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.details .kw {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 7px;
}
.details .kw span {
    border: 1px solid var(--border-color);
    background: var(--surface);
    border-radius: 4px;
    padding: 3px 9px;
    font-size: 11.5px;
}
.detmore {
    display: inline-flex;
    margin-top: 14px;
    padding: 0;
    border: 0;
    background: none;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--brand-steel);
    cursor: pointer;
}
.detmore:hover {
    color: var(--brand-primary);
    text-decoration: underline;
}

/* ---- row 2, left: the four sections on one rail ---- */
.viewbar {
    grid-area: 2 / 1;
    display: flex;
    align-items: stretch;
    min-width: 0;
    padding-right: 14px;
}
.evaltabs {
    --tp: 14px;
    position: relative;
    display: flex;
    flex: 0 0 auto;
    gap: 4px;
    align-self: stretch;
    margin: 0;
    padding: 0;
    list-style: none;
    border: 0;
}
.evaltabs > li {
    display: flex;
}
/* The rail, and the navy marker that glides along it to the section you pick
   (review.js measures the active tab into --ink-x / --ink-w). */
.evaltabs::before {
    content: "";
    position: absolute;
    left: var(--tp);
    right: var(--tp);
    bottom: 0;
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: var(--border-color);
}
.evaltabs::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: var(--ink-w, 0);
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: var(--brand-primary);
    transform: translateX(var(--ink-x, 0));
}
.evaltabs.ready::after {
    transition: transform 0.28s cubic-bezier(.22,.61,.36,1), width 0.28s cubic-bezier(.22,.61,.36,1);
}
.evaltabs .nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 var(--tp);
    border: 0;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-muted);
    white-space: nowrap;
    transition: color 0.15s;
    isolation: isolate;
}
/* Pointing at a section lights it as a button before you click. */
.evaltabs .nav-link::after {
    content: "";
    position: absolute;
    inset: 10px 4px;
    border-radius: 8px;
    background: var(--surface-subtle);
    opacity: 0;
    transition: opacity 0.15s;
    z-index: -1;
}
.evaltabs .nav-link:not(.active):not(.is-locked):hover::after {
    opacity: 1;
}
.evaltabs .nav-link svg {
    width: 14px;
    height: 14px;
    flex: 0 0 14px;
    color: #7b8794;
    transition: color 0.15s;
}
.evaltabs .nav-link:hover {
    color: var(--brand-primary);
}
.evaltabs .nav-link:hover svg:not(.decision-check):not(.decision-lock) {
    color: var(--brand-steel);
}
.evaltabs .nav-link.active {
    color: var(--brand-primary);
    font-weight: 700;
}
.evaltabs .nav-link.active svg:not(.decision-check) {   /* the check is state: green on every tab, hovered or not */
    color: var(--brand-primary);
}
.evaltabs .nav-link:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -10px;
    border-radius: 8px;
}
/* Each label reserves its own bold width, so switching never nudges its neighbours. */
.evaltabs .tl {
    display: grid;
}
.evaltabs .tl::after {
    content: attr(data-label);
    font-weight: 700;
    height: 0;
    overflow: hidden;
    visibility: hidden;
}
@media (max-width: 1240px) {
    .evaltabs .nav-link svg {
        display: none;
    }
}
/* The Decision tab's states: a recorded decision wears a check; a seat that cannot
   decide gets the tab locked -- there, not gone -- with the reason on the hover box. */
.evaltabs .nav-link .decision-check {
    margin-left: -3px;
    color: var(--success);
}
.evaltabs .nav-link .decision-lock {
    margin-left: -3px;
    font-size: 11px;
    opacity: 0.7;
}
.evaltabs .nav-link.is-locked,
.evaltabs .nav-link.is-locked:hover {
    color: var(--text-muted);
    opacity: 0.6;
    cursor: not-allowed;
}

/* ---- both rows, right: the queue as one card ---- */
.queue {
    grid-area: 1 / 2 / 3 / 3;
    align-self: center;
    margin-right: 14px;
    display: flex;
    flex-direction: column;
    border: 1px solid #cfd7e0;
    border-radius: 10px;
    overflow: hidden;
    background: var(--surface);
    box-shadow: 0 1px 2px rgba(13, 37, 64, 0.05);
}
.stages {
    display: flex;
    align-items: stretch;
    height: 50px;
    flex: 0 0 auto;
}
.stage {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    padding: 3px 14px 0;
    border: 0;
    background: none;
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.2s;
    --st: var(--text-muted);
}
.stage[data-st="ur"] { --st: var(--warning); }
.stage[data-st="oe"] { --st: var(--info); }
.stage[data-st="ip"] { --st: var(--success); }
.stage[data-st="c"]  { --st: var(--danger); }
.stage[data-st="w"]  { --st: var(--text-muted); }
.stage::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 3px;
    background: var(--st);
}
.stage + .stage {
    border-left: 1px solid var(--border-color);
}
.stage:hover:not(:disabled) {
    background: var(--surface-subtle);
}
.stage[aria-current="true"] {
    background: color-mix(in srgb, var(--st) 8%, #fff);
}
.stage[aria-current="true"]:hover:not(:disabled) {
    background: color-mix(in srgb, var(--st) 13%, #fff);
}
.stage[aria-current="true"] .sn,
.stage[aria-current="true"] .sl {
    color: var(--st);
}
.stage:disabled {
    cursor: default;
    opacity: 0.55;
}
.stage:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -3px;
}
.stage .sn {
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
    color: var(--text-color);
    font-variant-numeric: tabular-nums;
}
.stage .sl {
    font-size: 11px;
    line-height: 1.15;
    color: var(--text-muted);
}
/* The card's footer: Previous, Submissions, Next as three cells of one control. The
   stage boxes alone set the card's width and the footer fills it -- if the footer
   counted too, the two would feed each other and Next would grow on every resize. */
.app-bar .pager {
    display: flex;
    align-items: stretch;
    height: 40px;
    white-space: nowrap;
    background: var(--surface);
    border-top: 1px solid var(--border-color);
    width: 0;
    min-width: 100%;
}
.app-bar .pager .change-submission-btn {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 0 12px;
    border-radius: 0;
    background: none;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-muted);
    transition: background 0.13s, color 0.13s;
}
.app-bar .pager #next {
    border-left: 1px solid var(--border-color);
}
.app-bar .pager .change-submission-btn:hover {
    background: var(--surface-subtle);
    color: var(--brand-primary);
    text-decoration: none;
}
.app-bar .pager .change-submission-btn[aria-disabled="true"] {
    background: none;
    color: #aab4bf;
    cursor: default;
    pointer-events: none;
}
/* Submissions leads without a block of colour: a bold navy label and the card's one
   navy accent, its position badge. */
.app-bar .pager .pickbtn {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    height: auto;
    padding: 0 12px;
    border: 0;
    border-left: 1px solid var(--border-color);
    border-radius: 0;
    background: none;
    box-shadow: none;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--brand-primary);
    cursor: pointer;
}
.app-bar .pager .pickbtn:hover {
    background: var(--surface-subtle);
}
.app-bar .pager .pickbtn[aria-expanded="true"] {
    background: var(--brand-primary-tint);
}
.app-bar .pager .pickbtn .n {
    font-size: 11.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: var(--brand-primary);
    color: #fff;
    border-radius: 10px;
    padding: 1px 7px;
}
.app-bar .pager .pickbtn .kbd {
    flex: none;
    height: 18px;
    padding: 0 5px;
    border: 1px solid #d5dde7;
    border-radius: 4px;
    background: var(--surface-subtle);
    font-size: 11px;
    font-weight: 500;
    line-height: 16px;
    color: var(--text-muted);
}
/* The chevron rotates about its own centre: Font Awesome's svg is inline and carries a
   baseline shift, which put the arrow low pointing down and high pointing up. A grid box
   with a block-level svg has no baseline to sit on. */
.app-bar .pager .pickbtn .cv {
    display: grid;
    place-items: center;
    width: 10px;
    height: 10px;
    color: var(--brand-steel);
    flex: 0 0 10px;
    transition: transform 0.18s cubic-bezier(.22,.61,.36,1);
}
.app-bar .pager .pickbtn .cv svg {
    display: block;
    width: 100%;
    height: 100%;
    vertical-align: top;
}
.app-bar .pager .pickbtn[aria-expanded="true"] .cv {
    transform: rotate(180deg);
}
.app-bar .pager .change-submission-btn svg {
    width: 10px;
    height: 10px;
}
.app-bar .pager button:focus-visible,
.app-bar .pager a:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -4px;
}
@media (max-width: 1240px) {
    .app-bar .pager .sw {
        display: none;
    }
}

.scrim {
    position: absolute;
    inset: 0;
    z-index: 55;
}
.pop {
    position: absolute;
    top: calc(var(--app-bar-h, 117px) + 4px);
    right: 14px;
    width: min(700px, calc(100% - 28px));
    max-height: min(560px, calc(100vh - 80px));
    background: var(--surface);
    border: 1px solid #cfd7e0;
    border-radius: 10px;
    box-shadow: 0 12px 34px rgba(13, 37, 64, 0.22);
    z-index: 60;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
/* The search, the legend and the footer keep their height; only the list shrinks. */
.pop .psearch,
.pop .plegend,
.pop .pfoot {
    flex: 0 0 auto;
}
.pop .psearch {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 11px 13px;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-muted);
}
.pop .psearch input {
    border: 0;
    outline: 0;
    font: inherit;
    font-size: 14px;
    width: 100%;
    background: none;
    color: var(--text-color);
}
/* The legend, and the filter: one chip per status with its live count. */
.pop .plegend {
    display: flex;
    flex-wrap: nowrap;
    gap: 5px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-color);
    background: var(--surface-subtle);
    overflow-x: auto;
    scrollbar-width: none;
}
.pop .plegend::-webkit-scrollbar {
    display: none;
}
.pop .plegend button {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    color: var(--text-muted);
    background: none;
    border: 1px solid transparent;
    border-radius: 20px;
    padding: 3px 9px;
    white-space: nowrap;
    flex: 0 0 auto;
    cursor: pointer;
}
.pop .plegend button:hover {
    background: var(--surface);
    border-color: var(--border-color);
    color: var(--brand-primary);
}
/* A status with no submissions: shown, so the legend reads the same on every paper, but
   plainly not a filter -- no hover, no pointer. */
.pop .plegend button:disabled,
.pop .plegend button:disabled:hover {
    opacity: 0.45;
    cursor: default;
    background: none;
    border-color: transparent;
    color: var(--text-muted);
}
.pop .plegend button[aria-pressed="true"] {
    background: var(--surface);
    border-color: #c7d6e6;
    color: var(--brand-primary);
    font-weight: 500;
}
.pop .plegend b {
    font-variant-numeric: tabular-nums;
    color: var(--text-color);
    font-weight: 700;
}
.pop .plegend button[aria-pressed="true"] b {
    color: var(--brand-primary);
}
.pop .plegend .allchip {
    background: var(--surface);
    border-color: var(--border-color);
}
.pop .plegend .allchip[aria-pressed="true"] {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: #fff;
}
.pop .plegend .allchip[aria-pressed="true"] b {
    color: #fff;
}
.pop .plist {
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
    padding: 0 0 6px;
    /* A row the arrows scroll to stops below its group's sticky header, not under it. */
    scroll-padding-top: 40px;
}
.pop .pgroup {
    position: sticky;
    top: 0;
    background: var(--surface);
    padding: 10px 13px 5px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
    z-index: 1;
}
.pop .pgroup-wrap[hidden] {
    display: none;
}
/* A row: the tick for the current paper, the dot, the whole title (it wraps; nothing
   is cut mid-word), the status in words. */
.popt {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    width: 100%;
    padding: 8px 13px;
    font-size: 14px;
    line-height: 1.35;
    color: var(--text-color);
    text-decoration: none;
}
.popt:hover {
    background: var(--surface-subtle);
    color: var(--text-color);
    text-decoration: none;
}
.popt.active {
    background: var(--brand-primary-tint);
    color: var(--text-color);
}
.popt[hidden] {
    display: none;
}
.popt .sdot {
    margin-top: 5px;
}
.popt .pin,
.popt .bind {
    font-size: 11px;
    margin-top: 4px;
    color: var(--brand-primary);
}
.popt .ptitle {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}
.popt[aria-selected="true"] {
    font-weight: 600;
    color: var(--brand-primary);
    background: var(--brand-primary-tint);
    box-shadow: inset 3px 0 0 var(--brand-primary);
}
.popt[aria-selected="true"].active {
    background: var(--brand-primary-tint);
}
.popt.rail-see-all {
    color: var(--brand-primary);
    font-weight: 600;
}
.pop .dropdown-empty {
    padding: 6px 13px 8px;
}
.pop .pfoot {
    border-top: 1px solid var(--border-color);
    padding: 9px 13px;
    font-size: 11.5px;
    color: var(--text-muted);
    display: flex;
    justify-content: space-between;
    gap: 10px;
    background: var(--surface-subtle);
}
.pop .pfoot kbd {
    font-family: inherit;
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--border-color);
    border-radius: 3px;
    padding: 0 4px;
    font-size: 11px;
}
.pop .nores {
    padding: 34px 20px;
    text-align: center;
    margin: 0;
}
.pop .nores[hidden] {
    display: none;
}
.pop .nores .t {
    display: block;
    font-size: 14px;
    font-weight: 500;
}
.pop .nores .d {
    display: block;
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 4px;
}

/* Status dots: the phase palette, one rule per phase, named where they are read
   (role="img" + aria-label on the dot itself). */
.sdot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 8px;
    box-shadow: 0 0 0 1px rgba(18, 49, 80, 0.15);
}
.sdot.s-ur { background-color: var(--warning); }
.sdot.s-oe { background-color: var(--info); }
.sdot.s-ip { background-color: var(--success); }
.sdot.s-c { background-color: var(--danger); }
.sdot.s-w { background-color: var(--text-muted); }
