/* Design tokens — the shared vocabulary for /dev.
 *
 * Replaces per-page 2016 CSS, where every page redeclared its own colours and
 * spacing. Everything downstream references these, so a change lands once.
 *
 * THE ACCENT IS SAMPLED FROM THE MOBILE APP, not chosen. #5ac8fa was measured
 * from a screenshot of the shipping iOS app -- 19,590 pixels of it in the
 * floating action button, and the active tab icon is the identical value. Using
 * it here means the web and the app are literally the same blue rather than two
 * approximations of one brand.
 *
 * The app's own structure, for reference:
 *     page #141414 · cards #1c1c1e (raised) · tab bar #1c1c1e · accent #5ac8fa
 *
 * The web deliberately differs in two ways, both requested: the page is lighter
 * than the app's, and the nav rail is DARKER than the page rather than lighter.
 * A full-width desktop page at #141414 reads as a void in a way a phone screen
 * does not, and a side rail wants to recede where a bottom tab bar wants to sit
 * forward. The rail at #1a1a1a echoes the app's page colour, which ties the two
 * together without copying the layout.
 *
 * Neutrals still come from the site's own CSS -- #2b2b2b was the single
 * most-used colour across the legacy stylesheets (41 occurrences) and survives
 * as the zebra stripe.
 *
 * Contrast is measured, not eyeballed (WCAG 2.1 relative luminance):
 *
 *     #f2f2f2 text     on #282828   12.3   AA
 *     #b8b8b8 secondary on #282828    7.0   AA
 *     #949494 muted     on #282828    4.6   AA  (site #848484 was 3.9 here)
 *     #5ac8fa accent    on #282828    7.8   AA
 *     #1a1a1a icon      on #5ac8fa    9.2   the selected nav item
 *     #bb0600 legacy danger           FAILS -> lifted to #ff5c54
 *     #6f5499 legacy purple           FAILS -> lifted to #8b6fb8
 *
 * Structured so a light theme is a variable swap, not a rewrite: override these
 * under :root[data-theme="light"] and nothing else needs to change.
 */

:root {
    /* ---- surfaces: built up from the site's #2b2b2b ---- */
    --scs-bg:            #282828;   /* page */
    --scs-surface:       #2f2f2f;   /* cards, table body */
    --scs-surface-raised:#363636;   /* table headers, toolbars -- the site's dark */
    --scs-surface-hover: #3d3d3d;   /* row hover */
    --scs-surface-stripe:#2b2b2b;   /* zebra: subtle, replacing #eee on white */

    /* ---- text ---- */
    --scs-text:          #e4e4e4;   /* Lifted from #d6d6d6 on Ben's read that
                                       the dark theme looked dull: 10.1 -> 11.6
                                       on the page, 9.2 -> 10.5 on a card.
                                       Deliberately NOT back to the original
                                       #f2f2f2 (13.2), which was reverted for
                                       being glare at length -- this sits
                                       between the two, closer to the bright
                                       end without reaching it. Midnight keeps
                                       #d6d6d6: it sits on #141414, where that
                                       value already measures 12.6. */
    --scs-text-secondary:#b8b8b8;   /* site colour, 8.59 contrast */
    --scs-text-muted:    #9a9a9a;   /* the site's #848484 lifted twice. First to
                                       #949494, measured against the PAGE
                                       (#282828, 4.63). But nothing muted sits on
                                       the page -- .stat-zero, .hit-when, .dl-note
                                       and .tmpl-note all sit on a CARD (#2f2f2f),
                                       where #949494 is 4.41 and fails AA. Tune
                                       the dimmest token against the lightest
                                       surface it lands on: 5.32 on a card, 5.86
                                       on the page. */
    /* Dark, not white. The accent is a LIGHT blue, so white on it is 1.90 --
       less legible than the disabled state it is meant to stand out from.
       #1a1a1a gives 9.18. Any theme whose accent is dark (Light) overrides this
       back to white. */
    --scs-text-on-accent:#1a1a1a;

    /* ---- borders ---- */
    --scs-border:        #3a3a3a;
    --scs-border-strong: #4a4a4a;

    /* The table head is its own pair: in light mode the legacy look is a DARK
       head on a white page, which is not simply "surface-raised". */
    --scs-table-head-bg:   var(--scs-surface-raised);
    /* The accent, as seen ON THE HEAD. In a dark theme that is just the accent:
       the head is a dark grey and the accent is a light blue. In a LIGHT theme
       it cannot be -- the head is deliberately dark (the legacy look) while the
       accent is a dark blue tuned for a white page, so the sorted column's
       header was accent-on-dark at 2.47:1 in Light and 1.82:1 in Paper. Those
       two themes override this with a light blue of the same family. */
    --scs-table-head-accent: var(--scs-accent);
    --scs-table-head-text: var(--scs-text);

    /* ---- accent + status ---- */
    --scs-accent:        #5ac8fa;   /* site sidebar blue, 5.68 contrast */
    --scs-accent-hover:  #8ad8fc;   /* lifted for dark; #4678b3 is too dark here */
    --scs-accent-muted: rgba(90, 200, 250, 0.16);

    --scs-danger:        #ff5c54;   /* #bb0600 lifted from 2.55 -> 5.61 */
    --scs-danger-hover:  #ff7a73;
    --scs-purple:        #8b6fb8;   /* #6f5499 lifted from 2.76 -> 4.11 (large text) */
    --scs-success:       #3ddc84;

    /* originals, for a light theme and for reference */
    --scs-danger-legacy: #bb0600;
    --scs-purple-legacy: #6f5499;
    --scs-dark-legacy:   #2b2b2b;

    /* ---- type ----
       Inter for UI: designed for screens, tall x-height, and legible at the
       small sizes a dense table needs -- Helvetica Neue and Trebuchet were
       chosen in 2016 for a very different layout.

       JetBrains Mono for archive ids. The slugs are 10 random alphanumerics
       (EN02faX30b) and a proportional face makes 0/O and 1/l/I ambiguous;
       a mono face with slashed zero makes them scannable and dictatable.

       The system stack after each is not decoration. Hospital networks
       routinely block external font CDNs, and without a real fallback chain the
       page drops to the browser default serif -- so the fallback IS the
       experience for some users. */
    /* Page titles only. Manrope is rounder and a touch warmer than Inter at
       large sizes, which is what a heading wants; body copy stays Inter, where
       Manrope's character would become noise at 13px. Served from
       assets/fonts -- see assets/css/fonts.css. */
    --scs-font-title: "Manrope", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;

    --scs-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                "Helvetica Neue", Arial, sans-serif;
    --scs-font-table: var(--scs-font);
    --scs-font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo,
                     Consolas, "Liberation Mono", monospace;

    --scs-text-xs: 0.75rem;
    --scs-text-sm: 0.875rem;
    --scs-text-md: 1rem;
    --scs-text-lg: 1.25rem;
    --scs-text-xl: 1.75rem;
    --scs-text-2xl: 2.5rem;

    /* ---- spacing: 4px base ---- */
    --scs-space-1: 4px;
    --scs-space-2: 8px;
    --scs-space-3: 12px;
    --scs-space-4: 16px;
    --scs-space-5: 24px;
    --scs-space-6: 32px;
    --scs-space-7: 48px;

    /* ---- page frame ----------------------------------------------------
       Every view invented its own width and its own heading size. Measured
       across thirteen pages: content columns of 1376, 1360, 1344, 1148, 1132,
       1032, 932, 892 and 688px, and h1 at 18, 22, 26, 28, 32 and 64px in
       three different fonts. Nothing chose those numbers; they accumulated.

       One frame, stated once. Pages set `max-width: var(--scs-page-max)` and
       `padding: var(--scs-page-pad-y) var(--scs-page-pad-x)` instead of a
       literal, so the next page inherits the decision rather than guessing.

       1400 because that is what Statistics already used and what Archives and
       Shared, which have no cap at all, effectively occupy on a 1440 screen --
       the three Ben named as the model.

       Line length is a SEPARATE problem from page width. A page is 1400 wide;
       prose and form fields inside it get their own narrower cap
       (--scs-measure) so widening the frame does not stretch a paragraph to
       1400px. */
    --scs-page-max:   1400px;
    /* For views whose content is a single column of rows -- Search results,
       Contacts -- where the full 1400 leaves text stranded across the width.
       Applied LEFT-ALIGNED, never centred: the heading must stay on the same
       x as every other view's. */
    --scs-page-max-narrow: 1040px;
    --scs-page-pad-x: 16px;
    --scs-page-pad-y: 20px;
    --scs-measure:    68ch;    /* readable line length for prose blocks */

    /* Section heading. 28px is Archives/Shared, the pages Ben pointed at. */
    --scs-h1: 28px;

    /* ---- shape + depth ---- */
    --scs-radius-sm: 3px;
    --scs-radius:    6px;
    --scs-radius-lg: 10px;

    --scs-shadow:    0 2px 8px rgba(0, 0, 0, 0.45);
    --scs-shadow-lg: 0 8px 28px rgba(0, 0, 0, 0.6);

    /* The sidebar is 36px collapsed / 125px expanded in navbar.min.css, and
       #bodywrap is offset by 64px. Kept as tokens so the offset is stated once
       instead of hardcoded per page. */
    --scs-sidebar-collapsed: 36px;
    --scs-content-offset:    64px;
}

/* Tell the browser to render native controls (scrollbars, form fields, the
   canvas the favicon script draws on) in dark. Without this, scrollbars and
   inputs stay light and the page looks half-converted. */
:root {
    color-scheme: dark;
}

html, body {
    background: var(--scs-bg);
    color: var(--scs-text);
    font-family: var(--scs-font);
}

a, a:link, a:visited {
    color: var(--scs-accent);
    text-decoration: none;
}
a:hover { color: var(--scs-accent-hover); }

/* The legacy sheets set `a:link,a:visited{color:#2b2b2b}` -- near-invisible on
   dark. Originally scoped to #bodywrap, which missed the footer: #footer sits
   OUTSIDE #bodywrap, so the copyright line stayed near-black on near-black. */
body a:link, body a:visited { color: var(--scs-accent); }
body a:hover { color: var(--scs-accent-hover); }
#footer, #copyright, #ula { color: var(--scs-text-muted); }

hr {
    border: 0;
    border-top: 1px solid var(--scs-border);
    margin: var(--scs-space-5) 0;
}

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

/* Visible focus, which the legacy CSS removes in several places with
   `outline:0`. Keyboard users need it. */
:focus-visible {
    outline: 2px solid var(--scs-accent);
    outline-offset: 2px;
}

/* ---------------------------------------------------------------- nav rail */
/* navbar.min.css paints the whole sidebar #09f. tokens.css loads last, so a
   plain rule wins without !important. */
nav.sidebar {
    background: #1a1a1a;                    /* darker than the page; echoes the app's #141414 */
    border-right: 1px solid var(--scs-border);
}
/* The list items themselves inherit #fff from navbar.min.css. The icons never
   showed it because the rule below colours the anchor, but anything in the rail
   NOT inside a.expandable -- counts, labels -- fell back to pure white. */
/* The glyphs are marked fa-2x in the markup, which is 2em. 10% off that is
   1.8em, set here rather than by swapping the class so the markup keeps one
   size name and the rail decides what it means.
   fa-fw gives every icon the same advance width, so centring the ANCHOR centres
   every glyph on the same axis -- without it they sit against navbar.min.css's
   3px left padding and a narrow glyph looks off-centre next to a wide one. */
nav.sidebar ul li a.expandable .fa-2x { font-size: 1.8em; }

/* THE ICON COLUMN NEVER MOVES.
 *
 * These rules are deliberately NOT scoped to .sidebar-menu-collapsed. They were,
 * and the result was that expanding or collapsing the rail swapped the class
 * instantly while the WIDTH animated over 0.4s: on expand the icons jumped left
 * the moment the centring rules stopped matching, and on collapse they snapped
 * to the centre of the still-wide rail and drifted in from there. The icons were
 * animating because their alignment depended on a state that changes in one
 * frame.
 *
 * Instead the icon lives in a fixed-width column the width of the COLLAPSED
 * rail's content box, in both states. Expanding widens the rail to the right of
 * that column and the label appears in the new space; the icon's centre line is
 * the same pixel throughout. Nothing about the icon depends on which class the
 * rail is carrying, so there is nothing to snap.
 *
 * 36px is navbar.min.css's collapsed content width (36px rail + 10px padding
 * each side = 57px box). Hard-coded because that is the library's number, and
 * deriving it would mean re-deriving the library's box model.
 */
nav.sidebar ul li a.expandable {
    display: flex;
    align-items: center;
}
nav.sidebar ul li a.expandable > span:first-child {
    flex: 0 0 36px;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* fa-fw pins every icon to a 1.25em box so a column of icons lines up beside a
   column of text. Here the COLUMN does that job, and the fixed box only stops
   the glyph centring inside it. */
nav.sidebar ul li a.expandable .fa-fw { width: auto; }
/* Auto margins, not justify-content alone: on this element the parent computes
   to display:flex with justify-content:center and the icon still sat flush
   left. Auto margins are resolved first and centre it regardless. */
/* The icon FILLS its column and centres the glyph itself, rather than being a
   box that the column then has to position. As an inline-block the <i>'s rect
   sat below its own span -- span 617..646, icon 636..666 -- so centring the
   span still left the glyph low. A flex box with a definite size has its
   content where its box is. */
nav.sidebar ul li a.expandable .fa-2x {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    margin: 0;
    line-height: 1;
}

/* Both children centre on the anchor's axis rather than inheriting a stretched
   height. Without this the icon's span stretched to the item's full height and
   the glyph floated inside it, so icon and label agreed on every short item and
   disagreed on the tall one. */
nav.sidebar ul li a.expandable > span:first-child,
nav.sidebar ul li a.expandable span.expanded-element { align-self: center; }

/* A DEFINITE height on the icon column, not a stretched one.
   align-self:center was not enough on its own: the Logout item's anchor comes
   out 69px tall where the others are 29, and its icon span stretched to fill
   that and then placed the glyph at the bottom -- 20px below its own label.
   Giving the column the icon's own height means there is no spare space inside
   it for the glyph to sit at one end of, whatever makes the row tall. */
nav.sidebar ul li a.expandable > span:first-child {
    height: 1.8em;
    line-height: 1;
    /* So the badge can be pinned to the GLYPH rather than to the row. When the
       rail is collapsed the icon is the only thing on screen, and a badge
       positioned against the item would drift away from it on expand. */
    position: relative;
}

/* ------------------------------------------------------- the rail badge
   A count of something waiting, drawn on the icon it belongs to. Today that
   is unhandled feedback, on the admin rail's Feedback item and on the Admin
   door of the ordinary rail -- see public/assets/js/admin-badge.js, which is
   what fills it; the markup ships empty and hidden.

   Danger red rather than the accent: the accent is what this rail uses for
   "you are here", and a count that borrowed it would read as selection. Red
   is the one colour on the page that already means look at this. */
.nav-badge {
    position: absolute;
    top: -4px;
    /* Right of the glyph's centre, not of the 36px column: the column is wider
       than the icon, and hanging the badge off its edge left a visible gap on
       the narrow glyphs. */
    left: 56%;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    box-sizing: border-box;
    border-radius: 8px;
    background: var(--scs-danger);
    /* NOT #fff. The dark themes LIFT --scs-danger to #ff5c54 so it stands off a
       near-black rail, and white on that is 3.04:1 -- 2.44:1 on `contrast`, the
       theme whose entire purpose is contrast. --scs-text-on-accent already does
       this inversion for the accent (#1a1a1a on the dark themes' light accent,
       #ffffff on light and paper's dark one) and the danger colour inverts the
       same way, so it applies unchanged here. Measured after, worst case across
       the six: 5.73 -- and 7.14 on `contrast`, up from 2.44. */
    color: var(--scs-text-on-accent);
    font-family: var(--scs-font);
    font-size: 10px;
    font-weight: 700;
    /* The digits are the whole content, and a rail is a column of them: without
       this "11" is visibly narrower than "44" and the badges stop lining up. */
    font-variant-numeric: tabular-nums;
    line-height: 16px;
    text-align: center;
    /* A dark hairline rather than a ring in the rail's own colour. Every theme
       sets nav.sidebar's background as a literal in its own block -- six of
       them, no shared variable -- so a ring could only ever match one. Black at
       40% separates the badge from five near-black rails AND from light's
       #0099ff, which is the one it has to work hardest against.

       It is doing real work on the light themes: the badge's own fill is only
       1.6:1 against paper's rail and 2.23:1 against light's, because both put a
       DARK red on a mid-tone rail. On those two the hairline is most of what
       makes it a distinct object rather than a smudge. */
    border: 1px solid rgba(0, 0, 0, 0.4);
    pointer-events: none;              /* the LINK is the target, not the count */
}
.nav-badge[hidden] { display: none; }  /* [hidden] loses to display:block alone */

/* navbar.min.css lifts every label 2px with `position:relative; bottom:2px`,
   which was compensating for a baseline it no longer sits on now that the
   anchor centres its children. Every label measured exactly 2px above its
   icon's centre; this puts them on the same line. */
nav.sidebar ul li a.expandable span.expanded-element {
    bottom: 0;
    /* navbar.min.css sets 2px, which put the text almost against the icon
       column's edge. A little more air reads as a gutter rather than a gap the
       glyph happened to leave. The mobile drawer sets its own, wider value. */
    margin-left: 6px;
}
/* The rail's own width animates in step with the content offset below, so the
   label is revealed by the rail widening rather than appearing beside a rail
   that has not moved yet. */
nav.sidebar { transition: width 0.4s; overflow-x: hidden; }

/* EXPERIMENT: the closing rail COVERS the labels rather than the labels
   vanishing first.
   navbar.js calls .hide() on every label the instant the collapse starts, so
   the text disappeared in one frame and the rail then slid over empty space.
   Keeping the labels laid out and letting the rail's own overflow clip them
   means the edge of the rail wipes across the text as it narrows.
   `display: inline-block !important` beats the inline style jQuery writes;
   `flex: 0 0 auto` with nowrap stops the label shrinking to nothing instead of
   overflowing -- a flex item's default is to shrink, which would fade the text
   out rather than cover it. */
nav.sidebar ul li a.expandable span.expanded-element {
    display: inline-block !important;
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Symmetric, and no negative pull. navbar.min.css styles the selected item as
   `padding: 4px 2px 4px 6px; margin-left: -4px`, which put the accent box 2px
   left of the rail's centre line. */
nav.sidebar ul li,
nav.sidebar ul li.active {
    padding-left: 0;
    padding-right: 0;
    margin-left: 0;
    margin-right: 0;
    /* The hamburger has no label, so it has nothing to clip -- and its icon
       renders 20px above its own box (60..89 inside a span at 80..109), so the
       clip took all but the bottom bar of it. Exempted rather than chased: the
       clipping exists for labels. */
    /* Clip the label HERE, not on the rail. overflow-x on nav.sidebar clips at
       its padding box, and navbar.min.css gives the rail 10px of horizontal
       padding -- so a label overflowing the 36px icon column still showed its
       first letter in that gutter once collapsed. The item's own box ends where
       the icon column ends, so clipping here leaves nothing peeking out. */
    overflow: hidden;
}
nav.sidebar ul li:first-child { overflow: visible; }

/* Logout, held apart. The gap was there before and I removed it by accident:
   it came from the HTML comment that used to sit inside this <li>, which gave
   the item a stray 40px of height. Ben liked the separation -- it marks a
   deliberate action away from the navigation -- so it is stated on purpose now
   rather than being a side effect of a comment. */
nav.sidebar ul li:last-child { margin-top: 44px; }

nav.sidebar ul li                     { color: var(--scs-text); }
nav.sidebar ul li a.expandable        { color: var(--scs-text); }
nav.sidebar ul li a.expandable:hover  { color: var(--scs-accent); }

/* Selected item: accent pill with a DARK icon -- 9.2 contrast, and it reads as
   a filled state rather than just another white glyph. */
nav.sidebar ul li.active { background: var(--scs-accent); }
nav.sidebar ul li.active a.expandable,
nav.sidebar ul li.active a.expandable:hover { color: #1a1a1a; }

/* The cmn-toggle switches are themed in ONE place, tables.css -- a partial copy
   lived here and set only the track, so whichever file loaded last decided how a
   switch looked. */


/* Inter's optical sizing and contextual alternates, on where supported. */
html { font-optical-sizing: auto; }

/* Tabular figures in tables so counts and dates line up column-wise. With
   proportional figures a 1 is narrower than a 7 and numeric columns visibly
   ripple down the page. */
table.dataTable,
.media-count, .pill, .folder-count, .dt-info {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* Archive slugs and other codes. */
code, .mono, .archive-id { font-family: var(--scs-font-mono); font-size: 0.92em; }

/* ------------------------------------------------- sidebar expand reflow */
/* navbar.js swaps .sidebar-menu-collapsed for .sidebar-menu-expanded (36px ->
   125px), but #bodywrap's margin-left is a fixed 64px in myarchives.min.css, so
   the expanded rail simply covered the content. nav and #bodywrap are siblings,
   so the general sibling combinator can reflow the page instead.
   The transition matches nav.sidebar's own 0.4s so they move together. */
#bodywrap, .archives-layout { transition: margin-left 0.4s; }
nav.sidebar-menu-expanded ~ #bodywrap { margin-left: 153px; }

@media (max-width: 900px) {
    nav.sidebar-menu-expanded ~ #bodywrap { margin-left: 64px; }
}

/* ---------------------------------------------------------------- tag pills */
/* Comments are stored as raw HTML by the legacy tag feature, so the viewer
   never renders them as markup -- it reduces the stored markup to plain #Tag
   tokens and rebuilds these as DOM nodes. See renderBody() in
   archive-viewer.js for why. */
.tag-pill {
    display: inline-block;
    background: var(--scs-accent-muted);
    color: var(--scs-accent);
    border: 1px solid var(--scs-accent);
    border-radius: 10px;
    padding: 0 7px;
    margin: 0 2px 2px 0;
    font-size: 11px;
    font-weight: 600;
    line-height: 17px;
    white-space: nowrap;
}

/* Category colours, taken from legacy's archive.v2.min.css rather than invented:
 *
 *   .fullword  background:#404040; color:#fff    type
 *   .dx        background-color:#09f             dx
 *   .stat      background-color:#fff; color:#222 stat
 *
 * Same three meanings, so a user moving between the old and new viewer sees one
 * visual language. The 1px border is the one addition: /dev has a light theme and
 * legacy does not, and a #fff stat pill on a light background would vanish. */
.tag-pill {
    border-width: 1px;
    border-style: solid;
}
.tag-pill.tag-type {
    background: #404040;
    color: #fff;
    /* A thin grey edge: #404040 on a dark surface has almost no contrast with
       the background, so the pill's shape was only implied by its text. */
    border-color: #6b6b6b;
}
/* The one departure from legacy, Ben's call: the dx pill takes the THEME accent
   rather than legacy's hardcoded #09f. That blue belonged to no theme and did not
   move when the theme did -- a wall of it read as one flat slab.
   --scs-text-on-accent does the light/dark pairing for free: the dark themes put
   an accent of #5ac8fa/#7fd4ff against #1a1a1a text, and the light theme puts its
   darker #0b74b5 against white. Never hardcode the text colour here; the accent
   is light in some themes and dark in others, so one fixed value is wrong in
   half of them. */
.tag-pill.tag-dx {
    background: var(--scs-accent);
    color: var(--scs-text-on-accent);
    border-color: var(--scs-accent);
}
.tag-pill.tag-stat {
    background: #fff;
    color: #222;
    border-color: #b4b4b4;
}

/* ------------------------------------------------------------ accent button */
/* The primary action on a page. Lived in archive.css, but the uploader
   (Continue, Retry failed) and the archive manager (Save order) use it too and
   neither loads that file -- so on both pages the one button that should stand
   out was rendering as an unstyled default. Same class-in-the-wrong-stylesheet
   mistake as .thumb-sizer above it. */
.btn-accent {
    /* inline-block, and it matters on an ANCHOR.
       <button> is inline-block already, so this changed nothing for the many
       button callers -- but a bare <a class="btn-accent"> computes to `inline`,
       and vertical padding on an inline box does not affect line height. The
       button paints 34px tall inside a 20px line box and spills over whatever
       is above it: on Favorites' empty state it sat across the second line of
       the sentence explaining what to do. Measured, not guessed. */
    display: inline-block;
    background: var(--scs-accent); color: var(--scs-text-on-accent);
    border: 0; border-radius: var(--scs-radius-sm);
    padding: 9px 14px; font-size: 13px; font-weight: 600; cursor: pointer;
    font-family: var(--scs-font);
    /* An anchor is not a button; without this the label sits on the baseline
       of surrounding text rather than centred in its own box. */
    line-height: 1.2;
    text-decoration: none;
}
.btn-accent:hover:not([disabled]) { background: var(--scs-accent-hover); }
/* An ANCHOR styled as an accent button needs its colour scoped as a.btn-accent.
   `body a:link { color: var(--scs-accent) }` above is specificity (0,1,2) and beats
   a bare class, so the label came out accent-on-accent -- an invisible caption on a
   blue rectangle. Seen on the Apps page's "New archive" button; the same trap is
   already noted in docs/STATE.md. */
a.btn-accent,
a.btn-accent:link,
a.btn-accent:visited { color: var(--scs-text-on-accent); }
a.btn-accent:hover { color: var(--scs-text-on-accent); }
/* A disabled primary action must not still look like the thing to click. */
.btn-accent[disabled] { cursor: default; }

/* --------------------------------------------------------- thumbnail sizer */
/* Three squares of increasing size. Used by My Archives, Shared, the archive
   viewer and the archive manager.
 *
 * Defined ONCE, here, because it was defined twice before -- in archives.css and
 * archive.css -- and the two copies had already drifted apart: 7/11/15px
 * borderless against 6/10/14px bordered, so the same control looked like two
 * different controls depending on the page. A third copy for the manager is how
 * this codebase ended up with three sets of comment endpoints.
 *
 * The bordered variant is the one kept: the button reads as a button before you
 * hover it, which the borderless one did not.
 *
 * The active button carries .selected, set by each page's own script. Pages also
 * mirror it to aria-pressed where they can; the class is what styles it. */
.thumb-sizer { display: flex; align-items: center; gap: var(--scs-space-1); }
.thumb-sizer .size {
    display: flex; align-items: center; justify-content: center;
    width: 26px; height: 26px;
    padding: 0; cursor: pointer;
    background: var(--scs-surface);
    border: 1px solid var(--scs-border);
    border-radius: var(--scs-radius-sm);
}
.thumb-sizer .size + .size { margin-left: 2px; }
.thumb-sizer .size:hover { background: var(--scs-surface-hover); }
.thumb-sizer .size:focus-visible { outline: 2px solid var(--scs-accent); outline-offset: 1px; }

.thumb-sizer .size span {
    display: block;
    background: var(--scs-text-secondary);
    border-radius: 1px;
    transition: width 0.12s ease, height 0.12s ease, background 0.12s ease;
}
.thumb-sizer .size[data-size="s"] span { width: 6px;  height: 6px; }
.thumb-sizer .size[data-size="m"] span { width: 10px; height: 10px; }
.thumb-sizer .size[data-size="l"] span { width: 14px; height: 14px; }

.thumb-sizer .size.selected {
    background: var(--scs-accent-muted);
    border-color: var(--scs-accent);
}
.thumb-sizer .size.selected span { background: var(--scs-accent); }

/* ------------------------------------------------------------- help overlay */
/* The wiki, full screen, over whatever you were doing. Global because the nav
   rail that opens it is -- see help.js. Lives here with the other shared
   components (.thumb-sizer, .btn-accent, .tag-pill) rather than in a page sheet,
   which is the mistake this file has already absorbed three times. */
.help-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;          /* over the nav rail (nav is ~1000) and any dialog */
    display: flex;
    flex-direction: column;
    /* Not fully opaque: a full-screen panel that erases the page underneath
       reads as a navigation, and this deliberately is not one -- you are still
       where you were. */
    background: rgba(0, 0, 0, .55);
    backdrop-filter: blur(2px);
}
/* display:flex outranks [hidden] -- stated rather than rediscovered. */
.help-overlay[hidden] { display: none; }

.help-overlay-bar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 10px 14px;
    background: var(--scs-surface-raised);
    border-bottom: 1px solid var(--scs-border);
    /* The bar is the only part of the chrome that is ours; it has to read as the
       app rather than as part of the wiki. */
    color: var(--scs-text);
    font-size: 14px;
}
.help-overlay-title { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }
.help-overlay-title .fa { color: var(--scs-accent); }
.help-overlay-tools { display: inline-flex; align-items: center; gap: 14px; }
.help-overlay-out { font-size: 13px; }

.help-overlay-frame {
    flex: 1 1 auto;
    min-height: 0;          /* a flex child will not shrink below content without it */
    width: 100%;
    border: 0;
    display: block;
    /* Matched to the APP's current theme, not fixed. The wiki turned out to be
       theme-aware itself -- it renders dark under prefers-color-scheme: dark --
       so the white this originally used would have flashed on every open for
       anyone in a dark theme, which is most people here. */
    background: var(--scs-bg);
}

/* The page behind must not scroll while the overlay is up, or a scroll gesture
   aimed at the wiki moves the archive list underneath it. */
html.help-open, html.help-open body { overflow: hidden; }

@media (max-width: 560px) {
    .help-overlay-out { display: none; }   /* the close button is the priority */
}

/* =========================================================================
 * TOASTS
 *
 * Bottom-centre, above everything. Not bottom-right: on a phone the right is
 * where thumbs rest and the left already carries the nav trigger, so the
 * centre is the one place that collides with neither -- and --scs-appbar-h
 * lifts the stack clear of the app-install bar when it is present.
 *
 * z-index sits above the drawer (1002) and the app bar (1004) because a toast
 * reporting the outcome of an action is the newest thing on screen and has to
 * be readable over whatever triggered it.
 * ====================================================================== */
.scs-toasts {
    position: fixed;
    left: 50%;
    bottom: calc(16px + var(--scs-appbar-h, 0px));
    transform: translateX(-50%);
    z-index: 3000;
    display: flex;
    /* Oldest nearest the bottom edge: the stack grows upward, so a toast
       already on screen never moves when another arrives -- you are not
       asked to re-find a message you were halfway through reading. */
    flex-direction: column-reverse;
    gap: var(--scs-space-2);
    width: max-content;
    max-width: min(440px, calc(100vw - 32px));
    pointer-events: none;             /* the gap between toasts stays clickable */
}

.scs-toast {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: var(--scs-space-3);
    padding: 10px var(--scs-space-3);
    border-radius: var(--scs-radius-sm);
    border: 1px solid var(--scs-border-strong);
    background: var(--scs-surface-raised);
    color: var(--scs-text);
    font-size: var(--scs-text-sm);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.scs-toast.is-going { opacity: 0; transform: translateY(6px); }

/* The accent as a left bar rather than a fill: a confirmation should be
   legible, not loud, and a filled accent panel competes with the page. */
.scs-toast.is-success { box-shadow: inset 3px 0 0 var(--scs-accent), 0 4px 16px rgba(0, 0, 0, 0.45); }
.scs-toast.is-error   { box-shadow: inset 3px 0 0 var(--scs-danger), 0 4px 16px rgba(0, 0, 0, 0.45); }

.scs-toast-text { flex: 1 1 auto; min-width: 0; }

.scs-toast-action {
    flex: 0 0 auto;
    background: none;
    border: 0;
    padding: 2px 4px;
    font: inherit;
    font-weight: 600;
    color: var(--scs-accent);
    cursor: pointer;
}
.scs-toast-action:hover { color: var(--scs-accent-hover); }

.scs-toast-close {
    flex: 0 0 auto;
    background: none;
    border: 0;
    padding: 0 2px;
    font-size: 17px;
    line-height: 1;
    color: var(--scs-text-muted);
    cursor: pointer;
}
.scs-toast-close:hover { color: var(--scs-text); }

@media (prefers-reduced-motion: reduce) {
    .scs-toast { transition: none; }
}

/* The page titles the font was chosen for. Named individually rather than by
   `h1`: the archive viewer's header and the landing page have their own
   typography, and a blanket h1 rule would reach both. */
#title,
.contacts-head h1,
.tags-head h1,
.stats-head h1,
.search-page h1,
.apps-page h1,
.help-head h1,
.upload-head h1 { font-family: var(--scs-font-title); }

/* ---------------------------------------------------------------- no rail
   Signed-out pages -- only /apps today, which the Auth0 login screen and the
   Uploader's UPDATE_URL both link to. base.php suppresses the rail and adds
   .no-nav.

   #bodywrap's margin-left is a fixed 64px in myarchives.min.css (see the note
   at the sidebar rules above), reserving space for a rail that is no longer
   rendered. Without this the public page opens with an empty left gutter. */
body.no-nav #bodywrap,
body.no-nav .archives-layout {
    margin-left: 0;
}
