/* ============================================================
   funding.css — the funding app's own stylesheet.

   Loaded AFTER themeV0.01.css (which supplies every colour token and the
   font) and after topbar.css. Nothing here hard-codes a colour: a literal
   hex is invisible to the other skin, which is the rule the theme file sets
   out and this file follows.

   It exists as a FILE rather than the <style nonce> block index.php used to
   carry, because D10 turns one page into five. Five copies of the same rules
   is five places to forget; a linked stylesheet is also covered by
   style-src 'self', cached once, and never at risk of losing its nonce.
============================================================ */

/* ------------------------------------------------------------
   nav — the funding app's own bar, under the sitewide topbar
------------------------------------------------------------ */

.fx-nav {
    display: flex;
    align-items: center;
    gap: 24px;
    flex-wrap: wrap;
    padding: 10px 20px;
    background: var(--panel-alt);
    border-bottom: 1px solid var(--border);
}

.fx-nav__brand {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    color: var(--text-strong);
    text-decoration: none;
}

.fx-nav__mark {
    font-size: 20px;
    color: var(--accent-soft);
}

.fx-nav__list {
    display: flex;
    gap: 18px;
    list-style: none;
    margin: 0;
    padding: 0;
    flex-wrap: wrap;
}

.fx-nav__link {
    color: var(--text-dim);
    text-decoration: none;
}

.fx-nav__link:hover,
.fx-nav__link:focus {
    color: var(--text);
}

.fx-nav__link.is-current {
    color: var(--text-strong);
    font-weight: 600;
}

/* ------------------------------------------------------------
   page shell
------------------------------------------------------------ */

.fx-wrap {
    max-width: 1000px;
    margin: 0 auto;
    padding: 24px 20px 56px;
}

.fx-wrap h1 {
    color: var(--text-strong);
    line-height: 1.25;
    margin: 8px 0 12px;
}

.fx-wrap h2 {
    color: var(--text-strong);
    margin: 32px 0 12px;
    font-size: 20px;
}

/* PROSE LINKS ONLY — the :not() is load-bearing, not tidiness.
   A .fx-btn is a control that happens to be an <a>, and it carries its own ink
   on its own fill. This selector is 0,2,0 and .fx-btn is 0,1,0, so for two
   months every anchor button in the app was repainted --accent-soft: on the
   light skin that is #0055b3 on the #0066cc fill (1.15:1 — the "blue text on
   blue" a user reported), and on the dark skin #60a5fa on #2563eb (2.2:1).
   Their <button> twins were never touched, which is why the same control looked
   right in one place and wrong in another rather than uniformly broken.
   Bumping .fx-btn's specificity instead would have fixed the fill buttons and
   left .fx-btn--quiet anchors still wearing link ink; excluding them here is
   the rule the rest of the file already assumes. */
.fx-wrap a:not(.fx-btn) {
    color: var(--accent-soft);
}

.fx-lede {
    color: var(--text-dim);
    line-height: 1.65;
    max-width: 70ch;
}

/* Breadcrumbs. The visible twin of the BreadcrumbList JSON-LD every entity
   page emits — search engines are told the same trail the reader sees. */
.fx-crumbs {
    font-size: 14px;
    color: var(--text-mute);
    margin-bottom: 4px;
}

.fx-crumbs a {
    color: var(--text-dim);
    text-decoration: none;
}

.fx-crumbs a:hover {
    color: var(--text);
    text-decoration: underline;
}

.fx-crumbs span::after {
    content: "›";
    margin: 0 8px;
    color: var(--border);
}

/* ------------------------------------------------------------
   stat tiles (landing page)
------------------------------------------------------------ */

.fx-stats {
    display: grid;
    gap: 16px;
    margin: 28px 0;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.fx-stat {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 16px;
}

.fx-stat__n {
    display: block;
    font-size: 28px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-strong);
}

.fx-stat__l {
    color: var(--text-dim);
    font-size: 14px;
}

/* ------------------------------------------------------------
   entity header + status badge
------------------------------------------------------------ */

.fx-badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    border: 1px solid var(--border);
    background: var(--panel-light);
    color: var(--text-dim);
    vertical-align: middle;
}

.fx-badge--open {
    color: var(--success);
    border-color: var(--success);
}

.fx-badge--forthcoming {
    color: var(--warn);
    border-color: var(--warn);
}

.fx-badge--closed {
    color: var(--text-mute);
}

/* "Not stated" — the source published no status. Deliberately NOT styled like
   `closed`: reading as closed is the exact misreading this status exists to
   prevent. Dashed border says "no answer here", not "the answer is no". */
.fx-badge--unknown {
    color: var(--text-dim);
    border-style: dashed;
    border-color: var(--text-mute);
}

/* The one-line explanation under the badge. Quiet, but never hidden — it is
   what separates a claim the publisher made from one we worked out. */
.fx-status-basis {
    margin: -4px 0 14px;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-mute);
    max-width: 70ch;
}

/* ------------------------------------------------------------
   facts table — the key/value block under every entity title
------------------------------------------------------------ */

.fx-facts {
    display: grid;
    grid-template-columns: minmax(9rem, auto) 1fr;
    gap: 10px 20px;
    margin: 20px 0 0;
    padding: 18px 20px;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px;
}

.fx-facts dt {
    color: var(--text-mute);
    font-size: 14px;
}

.fx-facts dd {
    margin: 0;
    color: var(--text);
}

/* ------------------------------------------------------------
   related-entity lists
------------------------------------------------------------ */

.fx-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 10px;
}

.fx-item {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 12px 16px;
}

.fx-item a {
    text-decoration: none;
    font-weight: 600;
}

.fx-item a:hover {
    text-decoration: underline;
}

.fx-item__meta {
    display: block;
    margin-top: 4px;
    font-size: 14px;
    color: var(--text-mute);
}

.fx-empty {
    color: var(--text-mute);
    font-style: italic;
}

.fx-more {
    margin-top: 12px;
    font-size: 14px;
}

/* The explanatory note beside a redacted party (migration 048). Deliberately
   NOT muted like .fx-empty: this is not an absence of data, it is a statement
   about why one field of a complete record is withheld, and styling it as
   greyed-out filler would make it read as the gap it exists to deny. */
.fx-redacted-note {
    margin: 8px 0 16px;
    padding: 10px 12px;
    border-left: 3px solid var(--accent, #6b7cff);
    background: var(--surface-2, rgba(127, 127, 127, .08));
    border-radius: 0 4px 4px 0;
    font-size: 14px;
    line-height: 1.5;
}

/* ------------------------------------------------------------
   attribution — mandatory, never decorative

   Every entity page renders this from its own payload. The SNPSAP aviso
   legal makes the source citation, the last-update date and the
   dissociation statement obligations; they are small print, not optional
   print, so they are dimmed but never hidden.
------------------------------------------------------------ */

.fx-attr {
    margin-top: 40px;
    padding: 16px 20px;
    background: var(--bg-deep);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    font-size: 13px;
    line-height: 1.65;
    color: var(--text-mute);
}

.fx-attr h2 {
    font-size: 14px;
    margin: 0 0 6px;
    color: var(--text-dim);
}

.fx-attr p {
    margin: 0 0 4px;
}

.fx-attr__licence {
    display: block;
}

/* ------------------------------------------------------------
   provenance — the exact document behind the page, and how its
   values have moved. Sits directly above the attribution block:
   attribution says who is owed credit, provenance says what can
   be checked. Same quiet register, since neither is the content
   a visitor came for, but both must be reachable without effort.
------------------------------------------------------------ */

.fx-prov {
    margin-top: 32px;
    padding: 16px 20px;
    background: var(--bg-deep);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    font-size: 13px;
    line-height: 1.65;
    color: var(--text-mute);
}

.fx-prov h2 {
    font-size: 14px;
    margin: 0 0 8px;
    color: var(--text-dim);
}

.fx-prov h3 {
    font-size: 13px;
    margin: 14px 0 6px;
    color: var(--text-dim);
}

.fx-prov__doc {
    display: grid;
    grid-template-columns: minmax(9rem, auto) 1fr;
    gap: 2px 16px;
    margin: 0;
}

.fx-prov__doc dt {
    color: var(--text-dim);
}

.fx-prov__doc dd {
    margin: 0;
    /* Document URLs are long and unbreakable; let them wrap rather than push
       the page into a horizontal scroll on a phone. */
    overflow-wrap: anywhere;
}

.fx-prov__note {
    color: var(--text-mute);
}

.fx-prov__history {
    margin: 0;
    padding-left: 18px;
}

.fx-prov__history li {
    margin-bottom: 3px;
    overflow-wrap: anywhere;
}

.fx-prov__field {
    color: var(--text-dim);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
}

.fx-prov__from {
    text-decoration: line-through;
}

.fx-prov__to {
    color: var(--text-dim);
}

.fx-sources {
    color: var(--text-dim);
    font-size: 14px;
    line-height: 1.7;
    max-width: 80ch;
}

/* ------------------------------------------------------------
   search (D11)
------------------------------------------------------------ */

.fx-search {
    background: var(--panel-alt);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    padding: 16px;
    margin: 0 0 24px;
}

.fx-search__row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 12px;
}

.fx-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 140px;
}

/* The keyword box takes whatever the fixed-width selects leave behind. */
.fx-field--grow {
    flex: 1 1 260px;
}

.fx-field > span {
    color: var(--text-dim);
    font-size: 12px;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.fx-field input,
.fx-field select {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    font: inherit;
    padding: 8px 10px;
    /* Without this a long <select> option stretches the flex row instead of
       being clipped, and the layout jumps as the country list loads. */
    max-width: 100%;
}

.fx-field input:focus-visible,
.fx-field select:focus-visible {
    outline: 2px solid var(--accent-soft);
    outline-offset: 1px;
}

.fx-search__actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* THE LABEL COLOUR IS --on-accent, NEVER --text-strong.
   --text-strong is the ink for text on the PAGE, so it is white in dark and
   near-black in light; --accent-soft is a fill, and it is light-blue in dark and
   dark-blue in light. Pairing the two put near-black on dark blue in the light
   skin (2.36:1) and white on light blue in the dark skin (2.54:1) — both far
   under the 4.5:1 AA line, and the "black text on blue" a user reported across
   all 70 buttons in this app. The theme has always carried --on-accent for
   exactly this, and the rest of the site uses it (.nav a.active, .cookie-accept,
   .msg-box button, .page-nav a.active in common.css / themeV0.01.css).

   The fill is --accent-STRONG rather than --accent: the two are the same value
   in the light skin (#0066cc), so this matches the site's other filled buttons
   exactly there, and in dark it is #2563eb rather than #3b82f6 — 5.17:1 against
   a white label instead of 3.68:1, which is the difference between passing AA
   and not. Measured, not guessed. */
.fx-btn {
    background: var(--accent-strong);
    border: 1px solid var(--accent-strong);
    border-radius: 6px;
    color: var(--on-accent);
    cursor: pointer;
    font: inherit;
    padding: 8px 18px;
    text-decoration: none;
}

/* The secondary action beside a filled one. It sits on the page rather than on
   a fill, so --text-dim is the correct ink here and the border is the only
   thing that makes it a button. */
.fx-btn--quiet {
    background: transparent;
    border-color: var(--border);
    color: var(--text-dim);
}

.fx-count {
    color: var(--text-dim);
    font-size: 14px;
    margin: 0 0 12px;
}

.fx-list--results .fx-item {
    padding-bottom: 14px;
}

.fx-item__excerpt {
    color: var(--text-mute);
    font-size: 14px;
    line-height: 1.6;
    margin: 6px 0 0;
    max-width: 80ch;
}

.fx-item__prog {
    font-size: 13px;
    margin: 4px 0 0;
}

/* aria-busy is the only signal that a fetch is in flight; fading the block
   makes it visible without moving anything, so results do not jump. */
#fx-results[aria-busy="true"] {
    opacity: 0.55;
    transition: opacity 120ms ease-in;
}

.fx-pager {
    align-items: center;
    display: flex;
    gap: 16px;
    margin-top: 20px;
}

.fx-pager__at {
    color: var(--text-dim);
    font-size: 14px;
}

/* ------------------------------------------------------------
   accounts: bookmarks + saved searches (D13)
------------------------------------------------------------ */

.fx-bookmark {
    margin: 0 0 16px;
}

/* The remove button sits inside a list item that is otherwise a stack of
   text, so the form must not introduce a block break of its own — hence
   inline-flex rather than flex.
   FLEX-END, not the baseline alignment inline-block gave us. The class grew a
   second shape it was never sized for: half its uses are a lone button, but the
   other half (checklist "Add a step", the vault uploader, the folder forms)
   wrap the input in .fx-ws-field, which is an inline-flex COLUMN — caption
   above input. An inline-level box takes its baseline from its FIRST line, so
   the submit button was aligning to the tiny grey caption and sitting a whole
   input's height above the field it submits. .fx-savesearch already reached
   flex-end for the same reason on the same shape. */
.fx-inline-form {
    align-items: flex-end;
    display: inline-flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 6px;
}

.fx-savesearch {
    align-items: flex-end;
    background: var(--panel-alt);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0 0 20px;
    padding: 12px 16px;
}

.fx-savesearch__done {
    color: var(--text-dim);
    font-size: 14px;
}

/* aria-pressed carries the state for assistive tech; this is the visual half of
   the same fact, so the two cannot drift apart. */
.fx-btn[aria-pressed="true"] {
    border-color: var(--success);
}

/* ------------------------------------------------------------
   mobile
------------------------------------------------------------ */

/* -- the paid AI summary ------------------------------------------------------
   Visually a panel rather than body copy, because the one thing a reader must
   never be unsure of is which words came from the official source and which
   came from a model. Same border/background tokens as the other panels, so it
   reads as part of the page and not as an advertisement. */
.fx-ai {
    margin: 22px 0;
}

.fx-ai-body {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 16px 18px;
}

.fx-ai-title {
    font-size: 1rem;
    margin: 0 0 8px;
}

.fx-ai-body p {
    margin: 0 0 10px;
}

.fx-ai-body p:last-child {
    margin-bottom: 0;
}

/* The provenance line and the "not enough source text" line. Quieter than the
   summary itself — it is a caveat, not a finding. */
.fx-ai-note,
.fx-ai-wait {
    font-size: 0.9rem;
    color: var(--text-dim);
}

@media (max-width: 560px) {
    .fx-savesearch {
        flex-direction: column;
        align-items: stretch;
    }

    /* Every filter on its own line: at this width two side-by-side selects
       leave neither wide enough to read its own option text. */
    .fx-search__row {
        flex-direction: column;
        gap: 10px;
    }

    .fx-field {
        min-width: 0;
    }

    .fx-nav {
        gap: 12px;
        padding: 10px 14px;
    }

    .fx-wrap {
        padding: 18px 14px 40px;
    }

    /* Two columns of key/value do not fit; stack them and keep the label
       tight against its value so the pairing survives the reflow. */
    .fx-facts {
        grid-template-columns: 1fr;
        gap: 2px 0;
    }

    .fx-facts dd {
        margin-bottom: 12px;
    }
}

/* I6 — the alert form. Deliberately a plain stack of labelled rows rather than
   a grid: it has seven fields of very different widths (a two-character country
   code next to a multi-select), and every grid I tried made the short ones look
   broken at some width. Stacking is boring and correct at every size. */
.fx-alert-form {
    background: var(--panel-alt);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    padding: 14px 16px;
    margin: 12px 0 20px;
}

.fx-alert-form p {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
}

/* --text-dim, not --muted: there is no --muted token in any stylesheet on this
   site, and an undefined var() with no fallback makes the whole declaration
   invalid at computed-value time — so `color` fell back to INHERITING, and every
   label in this form has been rendering at full body weight since I6 shipped.
   The form reads as a wall of equally-loud text with no hierarchy, which is what
   a user reported. Third instance of this exact defect here (see .fx-muted). */
.fx-alert-form label {
    color: var(--text-dim);
    font-size: 0.92rem;
    min-width: 8.5rem;
}

.fx-alert-form input[type="text"],
.fx-alert-form input[type="number"],
.fx-alert-form select {
    background: var(--panel);
    border: 1px solid var(--border-soft);
    border-radius: 6px;
    color: inherit;
    font: inherit;
    padding: 6px 8px;
}

/* The multi-select is the one control that needs room to be usable at all. */
.fx-alert-form select[multiple] {
    min-width: 16rem;
}

/* Country switcher. A row of links rather than a <select>: the set is small
   (only countries with a published page appear), it must work without
   JavaScript under the site's CSP, and a link is crawlable where a form control
   is not — these pages exist to be indexed. */
.fx-countrynav {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin: 4px 0 22px;
}

/* Same undefined-token defect as .fx-alert-form label above. */
.fx-countrynav__l {
    color: var(--text-dim);
    font-size: 0.92rem;
}

.fx-countrynav a {
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 3px 12px;
    text-decoration: none;
}

.fx-countrynav a:hover,
.fx-countrynav a:focus {
    background: var(--panel-alt);
    text-decoration: underline;
}

/* -- W1 workspace: folder + tag filters over the saved list ---------------- */

.fx-ws-filters,
.fx-ws-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin: 12px 0;
}

.fx-ws-filters a,
.fx-ws-tags a {
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 3px 12px;
    text-decoration: none;
    font-size: 0.9rem;
    color: var(--text-dim);
}

.fx-ws-filters a:hover,
.fx-ws-tags a:hover,
.fx-ws-filters a:focus,
.fx-ws-tags a:focus {
    background: var(--panel-alt);
}

.fx-ws-filters a.is-on,
.fx-ws-tags a.is-on {
    border-color: var(--text-strong);
    color: var(--text-strong);
    font-weight: 600;
}

/* Tags are the user's own words, so they read quieter than the folders that
   organise them — a filter row that shouts competes with the list it filters. */
.fx-ws-tags a {
    font-size: 0.82rem;
}

.fx-ws-count {
    font-variant-numeric: tabular-nums;
    color: var(--text-mute);
}

.fx-ws-clear {
    border-style: dashed !important;
}

.fx-ws-folders {
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    padding: 10px 14px;
    margin: 12px 0 18px;
}

.fx-ws-folders > summary {
    cursor: pointer;
    color: var(--text-dim);
    font-size: 0.92rem;
}

.fx-ws-folders .fx-inline-form {
    margin-top: 10px;
}

.fx-ws-field {
    display: inline-flex;
    flex-direction: column;
    gap: 3px;
    font-size: 0.8rem;
    color: var(--text-mute);
}

/* -- W4 deadline calendar -------------------------------------------------- */

.fx-cal-intro { color: var(--text-dim); }

.fx-cal-note {
    color: var(--text-mute);
    font-size: 0.85rem;
    max-width: 62ch;
}

.fx-cal-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin: 16px 0 8px;
}

.fx-cal-head a { text-decoration: none; color: var(--text-dim); }
.fx-cal-head a:hover, .fx-cal-head a:focus { text-decoration: underline; }

.fx-cal {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    margin-bottom: 24px;
}

.fx-cal th {
    text-align: left;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-mute);
    padding: 4px 6px;
    font-weight: 600;
}

.fx-cal td {
    border: 1px solid var(--border-soft);
    vertical-align: top;
    padding: 5px 7px;
    /* A MINIMUM, not a fixed size — `height` on a table cell is a floor, so a
       day with five deadlines grows and its week grows with it. Lowered from 78
       to 68 because most days in a month are empty and the grid was mostly
       whitespace. */
    height: 68px;
}

/* Days from the neighbouring month. --panel-alt is #fafafc against a #ffffff
   panel in the light skin, which is a difference you cannot see, so the padding
   cells read as part of the month. --bg-deep is the token for a recess and
   gives the grid three legible levels: padding, ordinary day, day with work. */
.fx-cal__pad { background: var(--bg-deep); border-color: var(--border-soft) !important; }

/* The date is what the eye uses to navigate the grid, so it is --text-dim
   rather than --text-mute, which sat at the same weight as the deadline text
   under it. */
.fx-cal__n {
    display: block;
    font-size: 0.78rem;
    color: var(--text-dim);
    margin-bottom: 2px;
}

/* --accent, not --text-strong: in the light skin --text-strong is #1d1d1f, so
   "today" was a heavy near-black box that read as an error state rather than a
   position marker. */
.fx-cal__day.is-today {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.fx-cal__day.is-today .fx-cal__n { color: var(--accent); font-weight: 700; }
.fx-cal__day.has-items { background: var(--panel); }

/* A deadline in a month cell has to be readable at a glance and cannot be
   allowed to stretch the column — the grid is fixed so twelve deadlines on one
   day do not make that week three screens tall. */
.fx-cal__item {
    display: block;
    /* 0.72rem is ~11.5px, and the title arriving here has already been cut to 38
       characters by calendar.php before this rule adds an ellipsis on top — two
       truncations stacked on a size that was hard to read to begin with. The
       full title is on the anchor's `title` attribute either way. */
    font-size: 0.78rem;
    line-height: 1.4;
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
}

.fx-cal__why { color: var(--text-mute); }

.fx-cal-feed {
    width: 100%;
    max-width: 62ch;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.78rem;
    padding: 6px 8px;
    border: 1px solid var(--border-soft);
    border-radius: 6px;
    background: var(--panel-alt);
    color: inherit;
}

.fx-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

@media (max-width: 620px) {
    /* The month grid stops being readable long before this; the list below it
       carries the same information in a shape that survives a phone.
       --month, not bare .fx-cal: the insight, trends, help and API-tier tables
       borrow .fx-cal for its header styling and have no list to fall back on,
       so hiding .fx-cal blanked every one of them on a phone. */
    .fx-cal--month { display: none; }
    .fx-cal-head { justify-content: flex-start; }
}

/* ── W2: the application checklist ────────────────────────────────────────
   No JavaScript anywhere in this block — every control is a submit button in
   one form, which is what the strict CSP wants and what makes the feature work
   with scripting off. */

/* Used by funding_deadline_html()'s "(time not stated)" since migration 024 and
   never defined, so that caveat has been rendering at full body weight on every
   call page, search row and calendar entry. Found by adding the second user. */
.fx-muted { color: var(--text-mute); }

.fx-checklist { margin: 0 0 1rem; }

.fx-checklist__group {
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    padding: 0.5rem 0.9rem 0.8rem;
    margin: 0 0 0.8rem;
    background: var(--panel-alt);
}

.fx-checklist__group legend {
    padding: 0 0.4rem;
    font-weight: 600;
    color: var(--text-strong);
}

/* FOUR COLUMNS, AND EVERY CHILD PLACED EXPLICITLY.
   With three columns the scope note and the drop button both wanted column 3,
   so where the drop landed was decided by auto-placement — i.e. by which
   OPTIONAL siblings this particular item happened to have. An item with a note
   and a quote pushed "Hide" to a third row (bottom right, under the quote); an
   item with a note but no quote put it on row 2; an item with neither left it
   on row 1. Three positions down one list, which is the misalignment reported.
   The note and the action are different things and now get a column each. */
.fx-checklist__item {
    display: grid;
    grid-template-columns: 1.4rem 1fr auto auto;
    align-items: baseline;
    gap: 0.1rem 0.4rem;
    padding: 0.35rem 0;
    border-top: 1px solid var(--border-soft);
}
.fx-checklist__item:first-of-type { border-top: 0; }

.fx-checklist__item label { cursor: pointer; }

/* Struck through rather than hidden or moved: a done item that vanishes takes
   the sense of progress with it, and one that jumps to the bottom loses the
   reader's place in a list they are working down. */
.fx-checklist__item.is-done > label { color: var(--text-mute); text-decoration: line-through; }

.fx-checklist__scope {
    grid-area: 1 / 3;
    justify-self: end;
    font-size: 0.72rem;
    color: var(--text-mute);
    white-space: nowrap;
}

.fx-checklist__detail {
    grid-column: 2 / -1;
    margin: 0.15rem 0 0;
    font-size: 0.85rem;
    color: var(--text-dim);
    /* The source's own words, quoted. The rule marks where our sentence stops
       and the portal's begins — the distinction the whole feature rests on. */
    border-left: 2px solid var(--border);
    padding-left: 0.6rem;
}

.fx-checklist__ref { color: var(--text-mute); font-size: 0.78rem; white-space: nowrap; }

/* justify-self, because a <button> is a stretch-by-default grid item: given the
   width of "standard for this programme" above it, "Hide" was rendering as a
   full-column-wide button with its label centred — the stray gap to its right. */
.fx-checklist__drop {
    grid-area: 1 / 4;
    justify-self: end;
    background: none;
    border: 0;
    padding: 0 0.2rem;
    font: inherit;
    font-size: 0.72rem;
    color: var(--text-mute);
    cursor: pointer;
    text-decoration: underline;
}
.fx-checklist__drop:hover, .fx-checklist__drop:focus { color: var(--text); }

@media (max-width: 620px) {
    /* Two columns here, so the note and the action stack under the label
       instead of sharing its row. `auto` for the row: they take whatever comes
       after the label and the quote rather than fighting them for row 1. */
    .fx-checklist__item { grid-template-columns: 1.4rem 1fr; }
    .fx-checklist__scope, .fx-checklist__drop {
        grid-area: auto / 2;
        justify-self: start;
    }
}

/* The same list without the controls, for a visitor with no account (user
   decision 2026-08-11). Not a teaser and not a different list: identical items,
   identical wording, identical citations — what an account adds is checkboxes.
   `<ul>` rather than the signed-in `<fieldset>`, so it is content markup rather
   than form markup. */
.fx-checklist--public { list-style: none; margin: 0 0 1rem; padding: 0; }

.fx-checklist--public .fx-checklist__item {
    grid-template-columns: 1fr auto;
    padding: 0.4rem 0;
}
/* No checkbox and no drop control here, so the columns shift left by one. The
   scope note has to be told: pointing it at column 3 of a two-column grid was
   creating a third, implicit track to hold it. */
.fx-checklist--public .fx-checklist__scope { grid-area: 1 / 2; }
.fx-checklist--public .fx-checklist__detail { grid-column: 1 / -1; }
.fx-checklist__label { font-weight: 500; }

/* ── W3: the document vault ───────────────────────────────────────────────
   The list reuses .fx-list/.fx-item, because a document IS a saved item and
   the workspace already knows how to lay one out. Only the two states the
   vault adds need anything of their own. */

/* A row whose file has moved in the user's own cloud. Struck through rather
   than hidden: the pointer is still theirs to delete, and a row that vanished
   would look like we lost it. */
.fx-vault__gone {
    color: var(--text-mute);
    text-decoration: line-through;
}

.fx-vault code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.85em;
}

.fx-vault input[type="file"] {
    font: inherit;
    font-size: 0.9rem;
    max-width: 100%;
}

/* ── W6: the collaboration layer ──────────────────────────────────────────
   Reuses .fx-list/.fx-item — a comment and a team member are both saved items
   with a line of meta, and the workspace already knows how to lay one out. */

.fx-team__who { font-weight: 600; }

.fx-thread .fx-item { border-left: 2px solid var(--border-soft); padding-left: 0.7rem; }

/* A removed comment keeps its place in the thread. A hole reads as somebody
   being quoted out of context; the tombstone says what happened. */
.fx-thread .is-removed { border-left-color: var(--border); opacity: 0.65; }

.fx-thread textarea, .fx-ws-field textarea {
    width: 100%;
    max-width: 60ch;
    font: inherit;
    font-size: 0.92rem;
    padding: 6px 8px;
    border: 1px solid var(--border-soft);
    border-radius: 6px;
    background: var(--panel-alt);
    color: inherit;
    resize: vertical;
}

.fx-ws-share summary { cursor: pointer; }

/* ── AN1: the API key box ──────────────────────────────────────────────────
   The one-time reveal. Deliberately loud — it is the only moment the secret
   exists outside our hash, and a user who scrolls past it has to issue a new
   key. Bordered in --warn rather than --danger: nothing has gone wrong, but
   something is about to be irretrievable. */
.fx-keybox {
    background: var(--panel-alt);
    border: 1px solid var(--warn);
    border-radius: 10px;
    padding: 14px 16px;
    margin: 12px 0 20px;
}

.fx-keybox p { margin: 0 0 8px; }
.fx-keybox p:last-child { margin-bottom: 0; }

/* The key itself must survive being copied out of a browser: monospace so O and
   0 are distinguishable, and wrapping on any character because it is 68
   characters with no spaces and must never be silently clipped. */
.fx-key {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.85rem;
    background: var(--bg-deep);
    border: 1px solid var(--border-soft);
    border-radius: 6px;
    padding: 8px 10px;
    overflow-wrap: anywhere;
    user-select: all;
}

/* Request/response examples on the API docs page. Horizontally scrollable
   rather than wrapped: a curl line broken mid-URL is a line somebody pastes
   and then has to repair, and these are meant to be copied verbatim. */
.fx-code {
    background: var(--bg-deep);
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    padding: 12px 14px;
    margin: 0 0 16px;
    overflow-x: auto;
    font-size: 0.85rem;
    line-height: 1.5;
}

.fx-code code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    white-space: pre;
    color: var(--text);
}

/* ── Four classes that were used in markup and defined nowhere ─────────────
   Found 2026-08-12 by a scan written while adding AN1's own styles — the same
   family as .fx-muted and the third occasion this has come up. An undefined
   CLASS fails more quietly than an undefined token: the element simply renders
   unstyled, so it looks like a design nobody finished rather than a bug. */

/* The "Generate AI summary" form on a call page. narrative.js decides whether a
   subscriber may see it and reveals it with `form.hidden = false`.

   SCOPED WITH :not([hidden]) ON PURPOSE. `hidden` is only a UA-stylesheet
   `display: none`, so ANY author `display` here — the obvious `display: flex`
   to lay the button out — outranks it and reveals the form to every signed-in
   visitor, including the ones the endpoint would answer 403. The bug would look
   like a product decision, not a stylesheet mistake. */
.fx-ai-ask:not([hidden]) {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
}

/* The dashboard's four counts. Deliberately styled as the stat tile this app
   ALREADY has on the landing page (.fx-stat) rather than as a second look:
   same 150px auto-fit grid, same 28px value, same dim label.

   The four numbers are unrelated counts, not a series, so none of them is
   coloured — a hue here would imply a category that does not exist, and the
   value carries text ink while the card carries the identity. No tabular-nums
   either: that gives every digit the width of a 0, which is right in a column
   of figures and wrong for a large standalone number. */
.fx-dash-stats {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.fx-dash-stats .fx-item {
    color: var(--text-dim);
    font-size: 14px;
    line-height: 1.45;
}

.fx-dash-stats strong {
    display: block;
    font-size: 28px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-strong);
    margin-bottom: 2px;
}

/* Search paging. Emitted server-side by search.php and client-side by search.js,
   which is why they are classes rather than :first-child/:last-child rules. */
.fx-pager__prev,
.fx-pager__next {
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text-dim);
    font-size: 14px;
    padding: 6px 14px;
    text-decoration: none;
}

.fx-pager__prev:hover, .fx-pager__prev:focus,
.fx-pager__next:hover, .fx-pager__next:focus {
    background: var(--panel-hover);
    color: var(--text);
}

/* Pushed to the far edge so the control the reader wants next is always in the
   same place, whether or not a "Previous" exists to balance it. */
.fx-pager__next { margin-left: auto; }

/* W6's per-person checklist progress. A fifth undefined class, and the one the
   hand scan missed because `.fx-team__who` exists and a grep for "fx-team"
   matched it — which is the argument for the derived check over a careful look.

   Same auto-fit grid as the dashboard tiles: each row is a name and "3 of 10
   done", far too short to earn a full-width line each, and a team of six should
   be readable without scrolling. Wider minimum than the stat tiles because a
   name is longer than a number. */
.fx-team {
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

/* ── AN6: the curated insight pages ───────────────────────────────────────
   The caveat. Rendered ABOVE the table it qualifies, because a qualification
   below a fifty-row table is one nobody reads — and every one of these tables
   names real organisations reached by walking several relations, so "does this
   mean what I think" is the reader's first question and deserves an answer
   before the numbers rather than after them. */
.fx-caveat {
    background: var(--panel-alt);
    border-left: 3px solid var(--warn);
    border-radius: 0 8px 8px 0;
    color: var(--text-dim);
    line-height: 1.6;
    margin: 0 0 22px;
    max-width: 70ch;
    padding: 12px 16px;
}

.fx-caveat strong { color: var(--text-strong); }

/* Fifty rows of organisation names will not fit a phone, and the page body must
   never scroll sideways — so the TABLE scrolls inside its own box. */
.fx-tablewrap {
    overflow-x: auto;
    margin-bottom: 8px;
}

.fx-insight {
    table-layout: auto;
    min-width: 32rem;
}

/* height: auto undoes .fx-cal td's 68px day-cell floor, which otherwise makes
   every one-line row here as tall as a calendar week. */
.fx-insight td, .fx-insight th { padding: 7px 10px; height: auto; }

/* The two count columns are read down, compared against each other — the one
   place tabular figures are right, and the opposite of the stat tiles. */
.fx-insight td:nth-child(2),
.fx-insight td:nth-child(3),
.fx-insight th:nth-child(2),
.fx-insight th:nth-child(3) {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* On a phone 32rem is wider than the screen, so the count columns — the answer —
   started off-screen behind a sideways scroll, while the name column would have
   wrapped happily. Let names and headers wrap instead; the wrapper still scrolls
   if a table genuinely cannot fit. :nth-child(n) only to outrank the nowrap above. */
@media (max-width: 620px) {
    .fx-insight { min-width: 0; }
    .fx-insight td, .fx-insight th { padding: 6px; }
    .fx-insight th:nth-child(n) { white-space: normal; }
}

/* I5 trend direction. Colour is never the only signal — the figure carries its
   own sign, so a reader who cannot distinguish these still reads +72.2% and
   -8.0% correctly. --success and --danger are the theme's status pair and are
   not reused for anything categorical. */
.fx-up   { color: var(--success); font-variant-numeric: tabular-nums; }
.fx-down { color: var(--danger);  font-variant-numeric: tabular-nums; }

/* ── AN2: the trend charts ────────────────────────────────────────────────
   Every colour here is a theme token, so the charts follow the light/dark skin
   like the rest of the page. Nothing is an inline style — the strict CSP
   forbids one, and hardening_test enforces that. */

.fx-charts {
    display: grid;
    gap: 18px 20px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    margin: 0 0 10px;
}

.fx-chart-cell { margin: 0; }

.fx-chart {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

.fx-chart__title {
    color: var(--text-strong);
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 2px;
}

/* THIN MARKS, RECESSIVE AXES. The line is the data; the baseline is scaffolding
   and is drawn at border weight so it never competes with it. */
.fx-chart__line {
    fill: none;
    stroke: var(--accent);
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.fx-chart__dot  { fill: var(--accent); }
.fx-chart__axis { stroke: var(--border); stroke-width: 1; }
.fx-chart__bar  { fill: var(--accent); }

/* Text wears TEXT tokens, never the series colour — the mark beside it already
   carries the identity, and a coloured number is harder to read for no gain. */
.fx-chart__value    { fill: var(--text-strong); font-size: 9px; font-weight: 600; }
.fx-chart__tick     { fill: var(--text-mute);   font-size: 8px; }
.fx-chart__rowlabel { fill: var(--text-dim);    font-size: 10px; }

.fx-chart--bars { max-width: 34rem; }

/* A tooltip is attached to every mark as an SVG <title>; this is the cursor
   affordance that says so, since there is no script to add a hover state. */
.fx-chart__dot, .fx-chart__bar { cursor: help; }

/* ── The guide's table of contents (/funding/help) ─────────────────────────
   A nav, not a list of decorations: the guide is long enough that the section
   a visitor came for is below the fold, and the two sections most worth reading
   ("what is in the corpus", "what this app will not tell you") are the first and
   last of eight. Wraps rather than scrolls — it is text, and a sideways-scrolling
   table of contents hides exactly the entries a narrow screen most needs. */
.fx-help-toc {
    background: var(--panel-alt);
    border-radius: 8px;
    margin: 0 0 28px;
    max-width: 70ch;
    padding: 12px 16px;
}

.fx-help-toc ul {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.fx-help-toc a {
    color: var(--text-dim);
    font-size: 0.94rem;
    text-decoration: none;
}

.fx-help-toc a:hover,
.fx-help-toc a:focus { color: var(--accent); text-decoration: underline; }

/* -- "Have a code?" on the subscription block ------------------------------
   Reuses .fx-search__actions for its row layout, so it lines up with the
   Subscribe button above it rather than introducing a second form idiom.    */
.fx-code {
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}
.fx-code label {
    font-size: .88rem;
    color: var(--text-dim, #8a94a6);
}
.fx-code input[type="text"] {
    flex: 1 1 12rem;
    min-width: 0;
    /* Uppercase in the field so it matches the code the person is copying;
       redeem_normalize() is the authority whatever they type. */
    text-transform: uppercase;
    letter-spacing: .04em;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
