/*
 * Frame Check by Clarethium
 * Design language: measurement instrument. Precise, honest, professional.
 * No decoration. Information hierarchy through spacing and weight.
 */

:root {
    /* Light-theme text tokens.
     *
     * Target is WCAG 2.1 AA: 4.5:1 contrast for normal body text,
     * 3:1 for UI components and large text (>=18pt or 14pt bold).
     *
     * Light-mode contrast ratios (on --surface #ffffff):
     *   --text     #111 -> 19.8:1  (AAA)
     *   --text-1   #222 -> 16.0:1  (AAA)
     *   --text-2   #444 ->  9.3:1  (AAA)
     *   --text-3   #595959 -> 7.0:1 (AAA)  -- was #777 (4.48, borderline fail per WebAIM)
     *   --text-4   #707070 -> 4.88:1 (AA)  -- was #aaa  (2.85, fails AA)
     *
     * The --text-4 bump from #aaa to #707070 is a significant darken
     * for the 47 text-color usages of that token. Tertiary / placeholder
     * / caption text now reads more strongly than before; that is the
     * correct fix for the accessibility gap and matches the "proud to
     * ship" bar for anyone reading Frame Check with a mild visual
     * impairment. Borders and pure-decoration usages of --text-4 pick
     * up the same change; visually slightly darker dividers, still
     * subtle, and no longer below the 3:1 non-text-contrast floor. */
    --bg: #fafafa;
    --surface: #ffffff;
    --text: #111;
    --text-1: #222;
    --text-2: #444;
    --text-3: #595959;
    --text-4: #707070;
    --border: #ddd;
    --border-light: #eee;

    --green: #16713a;
    --green-bg: #ecfdf5;
    --green-border: #a7f3d0;

    --yellow: #92620a;
    --yellow-bg: #fefce8;
    --yellow-border: #fde68a;

    --orange: #b84515;
    --orange-bg: #fff7ed;
    --orange-border: #fdba74;

    --red: #b91c1c;
    --red-bg: #fef2f2;
    --red-border: #fca5a5;

    --blue: #1d4ed8;
    --blue-bg: #eff6ff;
    --blue-border: #93c5fd;

    --gray: #555;
    --gray-bg: #f5f5f5;
    --gray-border: #d4d4d4;

    /* Warn tokens for soft cautionary surfaces (PII intake notice,
       intake-context warning notice). The call sites used
       `var(--bg-warn, #fffbeb)` with a hardcoded fallback because
       the token had no definition; references resolved to the
       fallback unconditionally and the design system carried a
       dead var-name. Defining the token here pins the warn-yellow at
       one place; future tweaks ratchet through the token instead of
       multiple call-site fallbacks. --accent-warn is the matching
       amber accent used for left borders + icon glyphs. */
    --bg-warn: #fffbeb;
    --accent-warn: #f59e0b;

    /* Muted surface token for informational-but-not-cautionary
       banners; same dead-token cleanup as --bg-warn. */
    --bg-muted: #f3f4f6;

    --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --mono: "JetBrains Mono", "SF Mono", "Fira Code", Consolas, monospace;
    --max-w: 860px;

    /* Border radius scale.
       --radius-sm is for small chip accents (badges, eyebrow
       labels, tags, code boxes, focus rings inside an element).
       --radius is the default card and form-input radius.
       --radius-lg is for larger surface cards (trust banner,
       privacy ledger columns, error card, etc.). --radius-xl is
       reserved for the largest surface treatments.

       28 instances of `border-radius: 3px` across the file are
       all for the same chip pattern, so a token compresses the
       intent. The 2px and 4px outliers remain literal; they
       are deliberate one-off values for specific accent details
       (hl-fact mark stroke, ai-skeleton-spinner) and don't share
       a semantic with the 3px chip use case. */
    --radius-sm: 3px;
    --radius: 6px;
    --radius-lg: 12px;
    --radius-xl: 16px;

    /* Spacing scale.
       Six tokens covering 0.25 to 2 rem. Use these for gap, margin,
       and padding wherever the value matches one of the steps.
       The scale is roughly 1.5× per step which keeps the rhythm
       legible at the 15px html base font size:

         --space-xs   =  0.25rem  ~  4px
         --space-sm   =  0.5rem   ~  8px
         --space-md   =  0.75rem  ~ 11px
         --space-lg   =  1rem     ~ 15px   (base)
         --space-xl   =  1.5rem   ~ 22px
         --space-2xl  =  2rem     ~ 30px

       Card padding pairs (Y < X by 0.25rem for vertical-rhythm
       comfort, e.g. "1rem 1.25rem") stay as literal value pairs
       because the asymmetry is deliberate and a token cannot
       express the relationship. New components should still
       prefer scale tokens for any non-card-padding spacing.

       Scale chosen by surveying the new modules added in the
       UX session: most common values were 0.5, 0.75, 1, 1.5, 2
       and the scale captures all of them. Earlier modules
       (results.html section cards, framing grid, etc.) use
       arbitrary literal values from before the system existed
       and are not refactored to avoid risking visual regression
       in code that already passes tests. */
    --space-xs:  0.25rem;
    --space-sm:  0.5rem;
    --space-md:  0.75rem;
    --space-lg:  1rem;
    --space-xl:  1.5rem;
    --space-2xl: 2rem;

    /* Brand-aware shadow color so subtle lifts (hover, focus rings)
       remain visible against both the light and dark surfaces.
       Components reference this via var(--shadow). */
    --shadow: rgba(0, 0, 0, 0.05);

    color-scheme: light dark;
}

/* Dark mode.
 *
 * Dark-mode contrast ratios (on --bg #0a0a0a):
 *   --text     #eee   -> 18.2:1  (AAA)
 *   --text-1   #ddd   -> 15.6:1  (AAA)
 *   --text-2   #aaa   ->  9.0:1  (AAA)
 *   --text-3   #999   ->  8.1:1  (AAA)  -- was #888 (6.4, AA); bumped for tier separation
 *   --text-4   #808080 -> 5.5:1 (AA)    -- was #666 (3.9, fails AA small text)
 *
 * Same --text-4 discipline as light mode: the previous #666 fell below
 * the 4.5 threshold for small text. Lightened to #808080 for AA
 * compliance. Text-3 bumped slightly to #999 so the visible tier gap
 * between text-3 and text-4 stays clear (8.1 -> 5.5).
 */
html.dark {
    --bg: #0a0a0a;
    --surface: #161616;
    --text: #eee;
    --text-1: #ddd;
    --text-2: #aaa;
    --text-3: #999;
    --text-4: #808080;
    --border: #2a2a2a;
    --border-light: #1e1e1e;

    --green: #34d399;
    --green-bg: rgba(34, 197, 94, 0.1);
    --green-border: rgba(34, 197, 94, 0.25);

    --yellow: #fbbf24;
    --yellow-bg: rgba(251, 191, 36, 0.1);
    --yellow-border: rgba(251, 191, 36, 0.25);

    --orange: #fb923c;
    --orange-bg: rgba(249, 115, 22, 0.1);
    --orange-border: rgba(249, 115, 22, 0.25);

    --red: #f87171;
    --red-bg: rgba(239, 68, 68, 0.1);
    --red-border: rgba(239, 68, 68, 0.25);

    --blue: #60a5fa;
    --blue-bg: rgba(59, 130, 246, 0.1);
    --blue-border: rgba(59, 130, 246, 0.25);

    --gray: #999;
    --gray-bg: rgba(255, 255, 255, 0.05);
    --gray-border: #333;

    --bg-verified: rgba(34, 197, 94, 0.15);
    --text-verified: #34d399;
    --bg-warning: rgba(249, 115, 22, 0.15);
    --text-warning: #fb923c;

    /* Dark-mode pairs for the warn / muted tokens defined in the
       light-mode block. Values picked to match the dark-mode treatment
       already in place at .pii-intake-notice html.dark overrides so
       visual continuity is preserved when the call sites switch from
       fallback to token resolution. */
    --bg-warn: rgba(245, 158, 11, 0.10);
    --accent-warn: #f59e0b;
    --bg-muted: rgba(156, 163, 175, 0.10);

    /* Stronger shadow in dark mode so the lift remains visible
       against the near-black surface. */
    --shadow: rgba(0, 0, 0, 0.4);
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html { font-size: 15px; -webkit-font-smoothing: antialiased; }

/* Smooth-scroll for anchor navigation (Coverage map badges → Analytical
 * Coverage depth rows). Respects prefers-reduced-motion: the media
 * query in the footer of this file resets scroll-behavior to auto for
 * users who set that preference. Without this rule, anchor clicks jump
 * instantly, which is disorienting on a dense results page. */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* ─────────────────────────────────────────────────────────────────
   Light-theme elevation: subtle shadows on primary container cards.

   The page uses 1px #ddd borders on white cards for definition. Borders
   alone read as flat at the top-down scan; adding a hair-thin shadow
   gives each primary card a small amount of elevation that makes it
   feel purposeful rather than floating. Two-layer shadow (Material
   Design-ish): a 1px tight shadow for edge definition + a 3px soft
   shadow for depth. Total alpha ~6%, deliberately subtle.

   Dark mode: no shadow needed (dark backgrounds naturally produce
   elevation contrast via luminance difference).

   Scoped to top-level containers only. Sub-cards inside these
   containers (v4_2-frame-card, sn-card, fai-takeaway-row) intentionally
   stay shadow-less so the visual hierarchy stays legible: one elevated
   layer per section, not a forest of shadows.
   ───────────────────────────────────────────────────────────────── */
.framing-card,
.v4_2-panel,
.consistency-finding,
.suggestion-card,
.sn-card {
    box-shadow:
        0 1px 1px rgba(0, 0, 0, 0.03),
        0 2px 6px rgba(0, 0, 0, 0.035);
}

/* Reframe card and results caveat already have their own visual weight
 * (thicker left-border + tinted background); no shadow needed. */

html.dark .framing-card,
html.dark .v4_2-panel,
html.dark .consistency-finding,
html.dark .suggestion-card,
html.dark .sn-card {
    box-shadow: none;
}

/* ─────────────────────────────────────────────────────────────────
   Keyboard focus discipline for custom-styled collapsible summaries.

   Every <details><summary> element whose ::-webkit-details-marker is
   hidden also strips the browser's default focus outline on the
   summary's <li> sibling rendering. Without an explicit :focus-visible
   rule, keyboard users tabbing through these elements get no visual
   indicator of where focus landed. This block gives all custom-
   styled summaries across the page a consistent 2px blue outline
   with offset, matching the .perspective-tag-link convention.

   Scope: every custom summary selector with a ::-webkit-details-marker
   hide rule elsewhere in this file. Add new summaries to this list
   when they are introduced. (Native summaries retain the default
   focus ring; they are not listed here.)
   ───────────────────────────────────────────────────────────────── */
.suggestion-header:focus-visible,
.trust-coverage-note-summary:focus-visible,
.sn-verified-toggle:focus-visible,
.sn-unresolved-toggle:focus-visible,
.sn-projection-toggle:focus-visible,
.v4_2-honest-limit-summary:focus-visible,
.v4_2-not-fired-summary:focus-visible,
.v4_2-reliability-note-summary:focus-visible,
.section-summary:focus-visible,
.source-toggle:focus-visible,
.compare-section-toggle:focus-visible,
.group-header-toggle:focus-visible,
.framing-candidates-summary:focus-visible,
.framing-portrait-clinical-summary:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

body {
    font-family: var(--font);
    color: var(--text);
    background: var(--bg);
    line-height: 1.6;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* ── Accessibility ──
   Skip navigation: hidden off-screen until focused. Lets keyboard
   users jump past the header nav directly into the main content. */
.skip-nav {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 9999;
    padding: 0.6rem 1rem;
    background: var(--blue);
    color: #fff;
    font-weight: 600;
    border-radius: 0 0 var(--radius) 0;
    text-decoration: none;
}

/* Using :focus-visible (not :focus) so the skip link appears only for
 * keyboard users (Tab from URL bar), not briefly for mouse users who
 * happen to click near the off-screen element. Matches the rest of
 * the page's focus-discipline convention. */
.skip-nav:focus-visible {
    left: 0;
    outline: 2px solid var(--text);
    outline-offset: 2px;
}

/* Focus-visible indicators for keyboard navigation. Mouse clicks
   do not show the ring, only Tab focus does. */
:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Marks in the annotated document are interactive popover triggers.
   Tab focus on a mark draws a clear ring so keyboard users can see
   which one is focused. */
.annotated-document mark:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* ── Header ── */

header {
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}

nav {
    max-width: var(--max-w);
    margin: 0 auto;
    padding: 0.8rem 1.5rem;
    display: flex;
    align-items: center;
    gap: var(--space-md);
}

.logo {
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--text);
    text-decoration: none;
    letter-spacing: -0.02em;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* Inline SVG logo mark. Mirrors the favicon so the tab icon and
   the in-page brand mark read as the same symbol: a blue frame
   outline with a green verification-check badge in the bottom-
   right corner. Using an inline SVG (instead of the old CSS
   box) lets the mark carry real brand meaning ("frame that has
   been checked") at the point the brand is most visible, and
   keeps the identity consistent from favicon through header. */
.logo-mark {
    display: inline-block;
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    vertical-align: middle;
}

.logo-check {
    font-weight: 400;
    color: var(--text-2);
}

.nav-links {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    margin-left: auto;
}

.nav-link {
    font-size: 0.82rem;
    color: var(--text-3);
    text-decoration: none;
    transition: color 0.15s;
    padding: 0.2rem 0;
    position: relative;
}

.nav-link:hover { color: var(--text); }

/* Focus-visible ring for .logo (home link) and .nav-link elements so
 * keyboard users get a consistent 2px blue outline instead of the
 * browser-default outline (which varies; Chrome: thin blue solid,
 * Firefox: thin dotted, Safari: solid ring). Matches the .btn and
 * <summary> focus discipline established site-wide. */
.logo:focus-visible,
.nav-link:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}

/* Subtle underline grows on hover for non-active links */
.nav-link:not(.nav-active)::after {
    content: '';
    position: absolute;
    bottom: -0.1rem;
    left: 50%;
    right: 50%;
    height: 1.5px;
    background: var(--blue);
    border-radius: 1px;
    transition: left 0.2s, right 0.2s;
}

.nav-link:not(.nav-active):hover::after {
    left: 0;
    right: 0;
}

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

/* Active indicator uses brand blue, matching the logo mark */
.nav-active::after {
    content: '';
    position: absolute;
    bottom: -0.35rem;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--blue);
    border-radius: 1px;
}

.theme-toggle {
    background: none;
    border: 1px solid var(--border);
    border-radius: 50%;
    width: 30px;
    height: 30px;
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--text-3);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color 0.15s, color 0.15s;
    margin-left: var(--space-xs);
    padding: 0;
}

.theme-toggle:hover {
    border-color: var(--text-4);
    color: var(--text);
}

/* ── Main ── */

main {
    max-width: var(--max-w);
    margin: 0 auto;
    padding: 2.5rem 1.5rem;
    flex: 1;
    width: 100%;
}

/* Result page widens main exactly enough to fit the TOC sidebar
 * (240px) + gap (2rem = 32px) alongside an UNCHANGED content
 * column (var(--max-w) - main padding = 860 - 48 = 812px). Math:
 * 240 + 32 + 812 + 48 (main padding) = 1132px. Keeping the
 * content column the same width as the default layout is critical:
 * sections inside .results (manifest, hero, framing cards) were
 * tuned for the 812px column, and a wider column shifts text
 * wraps and table column widths in ways that read as "the
 * manifest broke the layout" even though structurally nothing
 * is broken. Matching the original column width preserves the
 * existing visual rhythm. Other pages leave the main_class block
 * empty in their templates and stay at the default --max-w. */
main.main-with-toc {
    max-width: 1132px;
}

/* ── Result-page TOC sidebar ─── */

/* Two-column grid wrapper for the result page: TOC on the left,
 * content on the right. The grid shrinks the content column on
 * very narrow desktops (around 900-1100px) gracefully via
 * minmax(0, 1fr) which prevents grid-blowout on long content
 * lines. Below the desktop breakpoint, the layout collapses to
 * a single column and the TOC repositions to the top of the page
 * as a sticky pill (handled by .toc-sidebar mobile rules below). */
.result-layout {
    display: grid;
    /* TOC fixed at 240px; content column takes the rest of main's
     * inner width. Gap is 2rem (32px) so 240 + 32 + content =
     * main content area (1132 - 48 padding = 1084), giving
     * content = 812px = the original .results column width before
     * the TOC was added. Tables, manifest summaries, and tightly-
     * laid sections inside .results stay aligned with their
     * pre-TOC visual rhythm. */
    grid-template-columns: 240px minmax(0, 1fr);
    gap: 2rem;
    align-items: start;
}

/* Below desktop breakpoint, stack: content first in source order
 * but TOC is rendered before content in the markup, so on mobile
 * the TOC pill sits at the top of the page, and the content
 * follows. Block layout achieves this directly. The breakpoint
 * is set to 1131px because main.main-with-toc has max-width
 * 1132px; below that viewport width main shrinks and the content
 * column becomes narrower than its original 812px. Collapsing to
 * single-column at the same threshold keeps the TOC + content
 * layout pristine when shown and falls back to the mobile pill
 * at the moment the column would compress. */
@media (max-width: 1131px) {
    .result-layout {
        display: block;
    }
}

/* The .results section inside .result-layout needs min-width: 0
 * so its grid cell does not blow out when it contains long
 * unbroken content (URLs, code, JSON snippets). Without this,
 * grid items default to min-content sizing and can overflow. */
.result-layout > .results {
    min-width: 0;
}

/* Desktop: sticky sidebar. Stays in view as the user scrolls,
 * showing the TOC's current-section highlight as it tracks scroll
 * position via IntersectionObserver. The top offset matches
 * main's top padding so the sidebar's title aligns with the start
 * of the content column. */
.toc-sidebar {
    position: sticky;
    top: var(--space-md);
    align-self: start;
    /* Cap height to viewport so long TOCs (rare, but a future
     * structural-profile expansion could grow nodes) become
     * scrollable inside the sidebar rather than pushing the
     * sticky positioning past the viewport bottom. */
    max-height: calc(100vh - 2 * var(--space-md));
    overflow-y: auto;
    /* No background by default; sidebar reads as a quiet outline.
     * The current-section highlight (below) provides the visual
     * anchor. */
}

.toc-nav {
    /* Padding inside the nav so the title and list breathe
     * inside the sticky frame. */
    padding: 0;
}

.toc-title {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    margin: 0 0 0.75rem 0;
    padding: 0 0.5rem;
    line-height: 1.2;
}

.toc-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.toc-item {
    margin: 0;
    padding: 0;
}

/* TOC link: each section's anchor + its headline state. The
 * label is the section name; the state is the per-analysis
 * metric (trust label, claim count, voice, etc.) that makes the
 * sidebar a structural overview rather than a generic link list.
 * Padding-left provides room for the active-state indicator
 * (the left border below) so labels do not shift on activation. */
.toc-link {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.5rem 0.5rem 0.5rem 0.7rem;
    border-left: 2px solid transparent;
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    color: var(--text-2);
    text-decoration: none;
    line-height: 1.3;
    transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
}

.toc-link:hover {
    color: var(--text);
    background: rgba(0, 0, 0, 0.03);
}

.toc-link:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Active state: the section currently in view. aria-current is
 * set by the IntersectionObserver in the page's nonce'd script.
 * Visual: blue left border, slightly stronger text color, faint
 * background fill. The blue echoes the page's primary accent so
 * the active position reads as part of the brand. */
.toc-link[aria-current="location"] {
    color: var(--text);
    background: var(--blue-bg);
    border-left-color: var(--blue);
    font-weight: 600;
}

.toc-link-label {
    font-size: 0.88rem;
    line-height: 1.3;
}

.toc-link-state {
    font-size: 0.74rem;
    color: var(--text-3);
    line-height: 1.3;
    /* Truncate ellipsis for long state strings (e.g., a future
     * provider list that could grow). The state line is purely
     * informational; the label conveys the navigation target. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.toc-link[aria-current="location"] .toc-link-state {
    color: var(--text-2);
}

/* State variant for warn-class metrics (e.g., math consistency
 * issue count). Muted yellow so the warn signal is visible
 * without dominating the sidebar's quiet register. */
.toc-link-state-warn {
    color: var(--yellow, #b45309);
}

/* ─── Mobile pill ─── */

/* Below the desktop breakpoint, the sidebar becomes a sticky
 * pill at the top of the page. The button is the trigger; the
 * nav (overlay) is hidden until the trigger is tapped. JS toggles
 * aria-expanded on the trigger and a `.toc-mobile-open` class on
 * the sidebar to switch the visibility. The desktop sidebar uses
 * .toc-mobile-trigger { display: none } above the breakpoint so
 * the trigger is invisible to wide-viewport users. */
.toc-mobile-trigger {
    display: none;
}

@media (max-width: 1131px) {
    .toc-sidebar {
        position: sticky;
        top: 0;
        max-height: none;
        overflow: visible;
        margin-bottom: var(--space-md);
        z-index: 30;
        /* Bg fills behind the trigger so it does not look
         * transparent against scrolling content beneath. */
        background: var(--bg);
        /* Border-bottom marks the boundary between TOC chrome and
         * scrolling page content. */
        border-bottom: 1px solid var(--border-light);
    }

    .toc-mobile-trigger {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 0.5rem;
        width: 100%;
        padding: 0.6rem 0.9rem;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        color: var(--text);
        font-family: var(--font);
        font-size: 0.9rem;
        line-height: 1.3;
        cursor: pointer;
        text-align: left;
        transition: border-color 0.12s ease;
    }

    .toc-mobile-trigger:hover {
        border-color: var(--text-3);
    }

    .toc-mobile-trigger:focus-visible {
        outline: 2px solid var(--blue);
        outline-offset: 2px;
    }

    .toc-mobile-trigger-label {
        display: flex;
        align-items: baseline;
        gap: 0.4rem;
        flex: 1;
        min-width: 0;
        overflow: hidden;
    }

    .toc-mobile-trigger-prefix {
        font-size: 0.8rem;
        color: var(--text-3);
        white-space: nowrap;
    }

    .toc-mobile-trigger-current {
        font-weight: 600;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .toc-mobile-trigger-caret {
        font-size: 0.8em;
        color: var(--text-3);
        line-height: 1;
        transition: transform 0.15s ease;
    }

    .toc-mobile-trigger[aria-expanded="true"] .toc-mobile-trigger-caret {
        transform: rotate(180deg);
    }

    /* Overlay: full-width sheet that drops down from the trigger.
     * Hidden by default; shown when the sidebar carries
     * .toc-mobile-open. The nav element renders inside this sheet,
     * inheriting toc-link styles. */
    .toc-nav {
        display: none;
        margin-top: 0.5rem;
        padding: 0.75rem;
        background: var(--surface);
        border: 1px solid var(--border);
        border-radius: var(--radius-md);
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
        max-height: calc(100vh - 8rem);
        overflow-y: auto;
    }

    .toc-sidebar.toc-mobile-open .toc-nav {
        display: block;
    }

    /* Title is redundant on mobile since the trigger says
     * "On this page:". Hide it. */
    .toc-nav .toc-title {
        display: none;
    }
}

html.dark .toc-link:hover {
    background: rgba(255, 255, 255, 0.04);
}

html.dark .toc-link[aria-current="location"] {
    background: rgba(59, 130, 246, 0.12);
}

@media (max-width: 1131px) {
    html.dark .toc-sidebar {
        background: var(--bg);
        border-bottom-color: var(--border);
    }
}

/* Smooth scroll on anchor jumps. Applied at html level so the
 * native anchor jump (when JS is unavailable or fails) still
 * scrolls smoothly. The reduced-motion media query below
 * disables it for users who request reduced motion. */
html {
    scroll-behavior: smooth;
    /* scroll-padding-top so the jumped-to section does not get
     * obscured by the mobile sticky pill at the top. The desktop
     * sidebar is in a column so it does not cover content. The
     * mobile pill is ~50px tall plus margin. */
    scroll-padding-top: 4.5rem;
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    .toc-mobile-trigger-caret,
    .toc-link {
        transition: none;
    }
}

/* ── Hero ── */

.hero { margin-bottom: 2.5rem; }

.hero h1 {
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1.25;
    letter-spacing: -0.02em;
    margin-bottom: var(--space-sm);
}

.hero-sub {
    color: var(--text-2);
    font-size: 0.9rem;
}

.hero-models {
    display: block;
    font-size: 0.78rem;
    color: var(--text-4);
    margin-top: 0.3rem;
}

/* ── Demo preview on home page ── */

/* Compact result preview shown above the form. The user lands and
   immediately sees what the product produces, before investing any
   effort. This is the highest-leverage UX change for first impression:
   show the output, not just describe it. */
.demo-preview {
    margin-bottom: var(--space-2xl);
}

.demo-label {
    display: inline-block;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    background: var(--gray-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.15rem 0.5rem;
    margin-bottom: 0.6rem;
}

.demo-card {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--surface);
}

.demo-trust {
    /* Match the real trust banner styling but more compact for the preview */
    margin: 0;
    padding: 1rem 1.25rem;
    border-radius: 0;
    border-left: 5px solid var(--orange);
    border-bottom: 1px solid var(--border);
}

.demo-headline {
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.3;
    margin: 0.3rem 0 0.4rem 0;
    color: var(--text);
}

.demo-doc {
    padding: 1rem 1.25rem;
    background: var(--surface);
}

.demo-doc p {
    font-family: var(--mono);
    font-size: 0.85rem;
    line-height: 1.7;
    color: var(--text);
    margin: 0;
}

.demo-doc mark {
    cursor: default;
}

.demo-hint {
    padding: 0.6rem 1.25rem;
    font-size: 0.75rem;
    color: var(--text-3);
    background: var(--bg);
    border-top: 1px solid var(--border-light);
    margin: 0;
    text-align: center;
}

/* CTA at the tail of the demo card: "Try this example"; a
   semantically-adjacent invitation to run the same analysis the
   preview demonstrates. Styled as a full-width strip inside the
   card rather than a floating button next to the paste input,
   because the action only makes sense in the context of the
   example above it. Uses the card's own border-top so visually
   it reads as the card's foot, not a stranded element. */
.demo-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    width: 100%;
    padding: 0.7rem 1.25rem;
    border: 0;
    border-top: 1px solid var(--border);
    background: var(--surface);
    color: var(--blue);
    font-size: 0.85rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
    text-align: center;
    letter-spacing: -0.005em;
}
.demo-cta:hover {
    background: var(--blue-bg);
    color: var(--blue);
}
.demo-cta:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: -2px;
    background: var(--blue-bg, #eef4ff);
}
.demo-cta-arrow {
    font-size: 1.05em;
    line-height: 1;
    transition: transform 0.15s ease;
}
.demo-cta:hover .demo-cta-arrow {
    transform: translateX(2px);
}
html.dark .demo-cta {
    background: var(--surface);
    color: #60a5fa;
}
html.dark .demo-cta:hover {
    background: rgba(96, 165, 250, 0.1);
    color: #93bbfe;
}

/* ── Error ── */

.error-banner {
    background: var(--red-bg);
    border: 1px solid var(--red-border);
    color: var(--red);
    padding: 0.6rem 1rem;
    border-radius: var(--radius);
    margin-bottom: var(--space-xl);
    font-size: 0.85rem;
}

/* Cap-hit variant. The user did nothing wrong: the request was
 * refused because the free-tier daily limit was reached. Amber/
 * info treatment matches that posture (a notice, not an error).
 * Same shape as .error-banner so the layout is unchanged; only
 * the colour palette flips. Inherits the role="status" attribute
 * from the template so screen readers announce it as informational
 * rather than an alert. */
.error-banner--cap {
    background: var(--bg-warning, rgba(249, 115, 22, 0.10));
    border-color: var(--text-warning, #fb923c);
    color: var(--text-warning, #fb923c);
}
.error-banner--cap a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.error-banner--cap a:hover {
    text-decoration-thickness: 2px;
}

/* CTA link inside the cap-hit banner. Stronger visual weight
 * than the inline /mcp pointer baked into DAILY_CAP_MESSAGE so
 * the reader has a clear next-step action when bouncing off the
 * cap. Block-display + top margin so it sits below the message
 * text rather than running on. */
.error-banner-cta {
    display: inline-block;
    margin-top: 0.5rem;
    font-weight: 600;
    text-decoration: none;
}
.error-banner-cta:hover {
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ── Form ── */

.profile-form { margin-bottom: 2rem; }

.input-group {
    position: relative;
}

.main-input {
    width: 100%;
    padding: 0.85rem 1rem;
    border: 2px solid var(--border);
    border-radius: var(--radius-lg);
    font-family: var(--mono);
    font-size: 0.85rem;
    line-height: 1.6;
    background: var(--surface);
    color: var(--text);
    resize: none;
    overflow-y: auto;
    height: 200px;
    transition: border-color 0.2s, box-shadow 0.2s;
}

.main-input:focus {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.1);
}

.main-input::placeholder {
    color: var(--text-4);
    font-family: var(--font);
    font-style: italic;
}

/* Privacy microcopy sits between the textarea and the input footer
   so it is visible at the point of decision (pasting text) without
   competing with the submit button for attention. Small, muted, and
   carries a dotted-underline link to the /privacy page for the
   full posture. */
.input-privacy-note {
    font-size: 0.78rem;
    color: var(--text-3);
    margin: 0.55rem 0 0.1rem;
    line-height: 1.45;
}
.input-privacy-note a {
    color: var(--text-3);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}
.input-privacy-note a:hover {
    color: var(--text-2);
    text-decoration-style: solid;
}

/* Daily-cap notice. Sits below the privacy microcopy so the two
 * "before you submit" expectations (privacy + cap) read as
 * stacked context, not buried in a tooltip. Same visual register
 * as input-privacy-note (small, muted, dotted-link) so they read
 * as siblings rather than a primary + secondary distinction.
 * Margin-top gives the two notes a small separator without
 * adding a divider rule that would over-emphasise the boundary. */
.input-cap-note {
    font-size: 0.78rem;
    color: var(--text-3);
    margin: 0.25rem 0 0.1rem;
    line-height: 1.45;
}
.input-cap-note a {
    color: var(--text-3);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}
.input-cap-note a:hover {
    color: var(--text-2);
    text-decoration-style: solid;
}

.input-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: var(--space-md);
}

.char-count {
    font-size: 0.7rem;
    font-family: var(--mono);
    color: var(--text-4);
    transition: color 0.2s;
}

.char-count.char-warn { color: var(--yellow); }
.char-count.char-over { color: var(--red); font-weight: 600; }

.input-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-sm);
}

/* Cloudflare Turnstile widget styling reuses the working pattern from
   .rerun-form .cf-turnstile (the "Re-run analysis" / "Check another
   document" button on results.html, which has rendered cleanly across
   the production lifecycle). Key elements:

     display: inline-block            so the widget participates in the
                                       flex row at its intrinsic size.
     transform: scale(0.8)             shrinks the visible widget to 80%
                                       so it fits next to the button
                                       without dominating the row vertically.
     transform-origin: left center     anchors the scale to the left edge
                                       so the widget shrinks toward the
                                       button rather than away from it.

   Same scoped rule replicated for .rerun-form so visual treatment is
   uniform across every Turnstile-bearing form surface. */
.input-actions .cf-turnstile {
    display: inline-block;
    transform: scale(0.8);
    transform-origin: left center;
    /* Negative margins absorb the layout void left by scale(0.8). The
       widget's CSS transform shrinks visuals 300x65 -> 240x52 but the
       layout box stays 300x65, leaving 60px to the right and 13px below
       the visual that the flex row otherwise gaps across. -60px on the
       right pulls the button flush. -5px on the bottom is the calibrated
       partial absorption (full -13px overshoots, leaving the widget
       overlapping privacy microcopy below); -5px tightens vertical
       rhythm without intruding on neighboring text. */
    margin-right: -60px;
    margin-bottom: -5px;
}

textarea, input[type="text"] {
    width: 100%;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-family: var(--mono);
    font-size: 0.82rem;
    line-height: 1.55;
    background: var(--surface);
    color: var(--text);
    resize: vertical;
    transition: border-color 0.15s;
}

textarea:focus, input[type="text"]:focus {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 2px rgba(29, 78, 216, 0.08);
}

/* ── Source expand ── */

.source-expand {
    margin-top: var(--space-md);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
}

.source-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: 0.65rem 1rem;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--text-2);
    user-select: none;
    list-style: none;
}

.source-toggle::-webkit-details-marker { display: none; }

.source-toggle:hover { color: var(--text); }

.source-toggle-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--blue-bg);
    color: var(--blue);
    font-size: 0.75rem;
    font-weight: 700;
    transition: transform 0.2s;
}

.source-expand[open] .source-toggle-icon {
    transform: rotate(45deg);
}

.source-toggle-hint {
    font-size: 0.72rem;
    color: var(--text-4);
    margin-left: auto;
}

.source-body {
    padding: 0 1rem 1rem;
}

.source-input {
    border-color: var(--blue-border);
    background: var(--blue-bg);
}

.source-input:focus {
    border-color: var(--blue);
}

/* (.advanced-inner / .advanced-toggle removed: the nested
   <details> element on the home form was flattened into two
   sibling collapsibles. The styles below still apply to the
   form fields inside the surviving details element.) */

.field-row {
    margin-bottom: 0.6rem;
}

.field-label {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-2);
    margin-bottom: 0.2rem;
}

.checkbox-label {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    cursor: pointer;
    font-size: 0.82rem;
    color: var(--text-2);
}

.checkbox-label input[type="checkbox"] {
    width: 0.9rem;
    height: 0.9rem;
}

/* ── Field hint (small explanatory text under a form field) ── */

.field-hint {
    font-size: 0.74rem;
    color: var(--text-3);
    line-height: 1.45;
    margin-top: var(--space-xs);
}

/* ── Instruments panel (home page "What this measures") ──
   Replaces the previous generic 1/2/3 value-props grid with a
   panel that names the actual measurement instruments: the five
   framing dimensions, the nine source-network APIs, and the
   privacy posture. The layout is information-dense by design;
   the user should be able to learn what gets measured without
   leaving the home page or reading the about page. */

.instruments-panel {
    margin-top: 2.5rem;
    padding-top: var(--space-2xl);
    border-top: 1px solid var(--border-light);
}

.instruments-header {
    margin-bottom: 1.25rem;
}

.instruments-eyebrow {
    display: inline-block;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    background: var(--gray-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.15rem var(--space-sm);
    margin-bottom: var(--space-sm);
}

.instruments-title {
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--text);
    letter-spacing: -0.01em;
    margin: 0;
}

.instruments-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-xl);
    /* Default grid behavior is align-items: stretch, which would
       force the 5-item framing column and the 4-item privacy
       column to grow to the height of the 9-item sources column,
       leaving visible empty space below their last bullets. start
       lets each column take its natural height instead. The result
       is a top-aligned grid where each column ends with its last
       bullet, no padding-out, no chrome. */
    align-items: start;
}

.instrument-col {
    display: flex;
    flex-direction: column;
}

.instrument-col-title {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 0.2rem;
    letter-spacing: -0.005em;
}

.instrument-col-sub {
    font-size: 0.74rem;
    color: var(--text-3);
    line-height: 1.45;
    margin-bottom: 0.7rem;
}

.instrument-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.instrument-list li {
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.5;
    padding-left: 0.85rem;
    position: relative;
}

.instrument-list li::before {
    content: "·";
    position: absolute;
    left: 0;
    top: -0.05rem;
    color: var(--text-4);
    font-weight: 700;
    font-size: 1rem;
}

.instrument-list li strong {
    color: var(--text);
    font-weight: 600;
}

.instrument-source-list li {
    font-family: var(--font);
}

.instrument-list a {
    color: var(--blue);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 0.15s;
}

.instrument-list a:hover {
    border-bottom-color: var(--blue);
}

.btn {
    display: inline-block;
    padding: 0.6rem 1.5rem;
    border-radius: var(--radius);
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    border: none;
    transition: opacity 0.15s, background 0.15s, transform 0.1s;
    font-family: var(--font);
}

.btn:hover { opacity: 0.85; }
.btn:active { transform: scale(0.97); }

/* Unified focus-visible ring for all .btn variants (primary, secondary,
 * ghost, submit). .btn-reframe and .btn-danger have their own scoped
 * focus-visible rules that override with tier-colored outlines; this
 * base rule ensures every other .btn-* variant gets a visible focus
 * indicator without depending on the browser's default outline, which
 * has different styling across Chrome / Safari / Firefox. */
.btn:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Transparent 1px border on .btn-primary so its outer height matches
 * the bordered secondary and ghost variants. Without this the
 * bordered variants are 2px taller (border adds to auto-height with
 * the global box-sizing: border-box reset only kicking in when an
 * explicit height/width is set). The transparent border is invisible
 * but contributes the same 2px the other variants get from their
 * solid border, so primary, secondary, and ghost buttons land on the
 * same line height when sat side by side in .result-actions. */
.btn-primary { background: var(--text); color: var(--surface); border: 1px solid transparent; }
.btn-secondary { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
.btn-ghost { background: none; color: var(--text-3); border: 1px solid var(--border-light); }

/* MCP-conversion variant: brand-blue background instead of the
 * default btn-primary dark. The MCP upgrade card is the page's
 * conversion CTA ("install Frame Check inside your AI"); btn-primary
 * black on the dark/page side puts it in direct visual competition
 * with Save & share (also btn-primary). Mapping the conversion CTA
 * to a different color class makes the action class legible at a
 * glance: dark = "save/act on THIS page", blue = "go elsewhere
 * (install MCP, follow research)". The blue ties to the MCP card's
 * blue-tinted palette and to the brand accent, so the differentiation
 * stays on-brand instead of introducing a foreign color.
 *
 * The transparent border keeps height parity with btn-secondary /
 * btn-ghost, same approach as btn-primary above. */
.btn-mcp {
    background: var(--blue);
    color: #fff;
    border: 1px solid transparent;
}

.btn-mcp:hover {
    /* Slightly darker on hover. Lighter tints can be added via
     * color-mix() but keeping this simple for compatibility. */
    background: var(--blue);
    filter: brightness(0.92);
}

html.dark .btn-mcp {
    /* Dark mode --blue is #60a5fa (lighter, intended for accents
     * and links, not for solid-button backgrounds). White text on
     * that color fails WCAG AA contrast (~2.5:1). Override to a
     * mid-saturation blue (#2563eb) that holds white text at AA
     * (~4.7:1) while still reading as "the blue conversion CTA"
     * paired with the lighter --blue used elsewhere on dark mode.
     * Hardcoded hex rather than a token because no existing token
     * carries this specific darker-blue role; introducing a new
     * token (--blue-strong, --blue-cta) for one rule is more
     * surface area than the override is worth. */
    background: #2563eb;
    color: #fff;
}
.btn-ghost:hover { color: var(--text); border-color: var(--border); }

.btn.loading {
    opacity: 0.6;
    cursor: wait;
}

/* Disabled button state. Browser defaults vary (Chrome greys, Safari
   barely changes). The Save button uses this state while the AI
   interpret fetch is in flight so the user does not race the
   snapshot. The visual treatment must read as "deliberately gated"
   rather than "broken." Hover effects suppressed because they would
   suggest interactivity that is not currently possible. */
.btn[disabled],
.btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn[disabled]:hover,
.btn:disabled:hover {
    opacity: 0.55;
}

.btn[disabled]:active,
.btn:disabled:active {
    transform: none;
}

/* ── Explanation grid ── */

.explanation { margin-top: 3rem; }

.explanation h2 {
    font-size: 1.1rem;
    margin-bottom: var(--space-lg);
}

/* ════════════════════════════════════════════
   RESULTS PAGE
   ════════════════════════════════════════════ */

.results {
    display: flex;
    flex-direction: column;
    gap: var(--space-xl);
}

/* ── Frame Check Hero ── */
/* The structural finding above the trust banner. This is the
   first thing the user sees: the frame check result, the
   missing perspectives, and the portrait synthesis.         */

.frame-check-hero {
    padding: 1.75rem 2rem;
    border-radius: var(--radius-lg);
    background: var(--blue-bg);
    border-left: 5px solid var(--blue);
}

.frame-check-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--blue);
    margin-bottom: 0.25rem;
}

.frame-check-question {
    font-size: 1.1rem;
    font-weight: 500;
    color: var(--blue, #4a90d9);
    font-style: italic;
    margin: 0 0 0.5rem 0;
    line-height: 1.4;
}

.frame-check-context {
    font-size: 0.75rem;
    color: var(--text-3);
    margin: 0 0 var(--space-sm) 0;
    line-height: 1.4;
}

.frame-check-headline {
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1.45;
    color: var(--text-1);
    margin: 0 0 var(--space-md) 0;
}

/* Portrait: the synthesis sentence answering the hero question.
   Bumped from 0.92rem to 1rem so the answer carries at least the
   same content weight as the italic question above. The question
   is the hook (italic, blue, prominent); the portrait is the
   answer (normal weight, plain color). When the answer was smaller
   than the hook the eye learned to skim past it. */
.frame-check-portrait {
    font-size: 1rem;
    line-height: 1.6;
    color: var(--text-1);
    margin: 0 0 var(--space-lg) 0;
}

.frame-check-perspectives {
    margin-top: var(--space-md);
}

.perspectives-label {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-3);
    margin-bottom: var(--space-sm);
}

.perspective-tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
}

.perspective-tag {
    display: inline-flex;
    flex-direction: column;
    padding: 0.4rem 0.75rem;
    border-radius: var(--radius-sm);
    font-size: 0.8rem;
    line-height: 1.35;
}

.perspective-missing {
    background: var(--orange-bg);
    border: 1px solid var(--orange);
}

/* Addressed-perspective tag. The hero now shows all five
   analytical dimensions (causes, risks, stakeholders, trends,
   uncertainty) with their state, not just the missing ones, so
   the reader sees the full coverage map (the positive side and
   the negative side together) instead of a half-frame negative
   audit. The covered tag uses the green palette to distinguish
   it from the missing variant without reading as celebratory. */
.perspective-addressed {
    background: var(--green-bg);
    border: 1px solid var(--green-border);
}

.perspective-name {
    font-weight: 600;
    color: var(--text-1);
    text-transform: capitalize;
}

.perspective-question {
    font-weight: 400;
    color: var(--text-3);
    font-size: 0.75rem;
}

/* State chip inside the perspective tag (addressed / not addressed).
   Sits below the question so the user can see the dimension's name,
   the question it answers, and whether the document took it on, in
   one tag without ambiguity. */
.perspective-state {
    margin-top: 0.2rem;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
}

.perspective-addressed .perspective-state { color: var(--green); }
.perspective-missing .perspective-state { color: var(--orange); }

/* Clickable perspective tag. The top-of-page Coverage map is now a
 * jump-link surface: each badge anchors to the corresponding row in
 * the Analytical Coverage depth card so a reader who wants to drill
 * in can do so with a single click instead of scanning the whole
 * page. Keyboard-reachable via the native <a> semantics. */
.perspective-tag-link {
    text-decoration: none;
    color: inherit;
    cursor: pointer;
    transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}

.perspective-tag-link:hover,
.perspective-tag-link:focus-visible {
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
    filter: brightness(1.02);
    outline: none;
}

.perspective-tag-link:focus-visible {
    outline: 2px solid var(--blue, #4a90d9);
    outline-offset: 2px;
}

/* Target highlight: when a user clicks a coverage-map badge, the
 * anchored framing-cat row flashes briefly so the eye can find it
 * in the dense depth view. The highlight fades on its own; there's
 * no JS involved. */
.framing-cat:target {
    animation: framingCatPulse 1.8s ease 1;
}

@keyframes framingCatPulse {
    0%   { box-shadow: 0 0 0 2px rgba(74, 144, 217, 0.55); background: rgba(74, 144, 217, 0.08); }
    60%  { box-shadow: 0 0 0 2px rgba(74, 144, 217, 0.25); background: rgba(74, 144, 217, 0.04); }
    100% { box-shadow: 0 0 0 0 rgba(74, 144, 217, 0); background: transparent; }
}

/* Respect reduced motion: dial the pulse to a one-shot color change
 * without the box-shadow scale animation. */
@media (prefers-reduced-motion: reduce) {
    .framing-cat:target {
        animation: none;
        background: rgba(74, 144, 217, 0.08);
        transition: background 2s ease;
    }
}

/* ── Frame Suggestions ── */

.frame-check-suggestions {
    margin-top: var(--space-md);
}

.suggestions-label {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-3);
    margin-bottom: var(--space-sm);
}

.suggestion-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.suggestion-card {
    padding: 0.55rem 0.8rem;
    border-radius: var(--radius-sm);
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--blue, #4a90d9);
    font-size: 0.8rem;
    line-height: 1.4;
    /* Position relative so the absolutely-positioned
     * .suggestion-fvs-id below anchors to the card's top-right
     * corner rather than to a higher-up positioned ancestor. */
    position: relative;
}

.suggestion-card[open] {
    padding-bottom: 0.75rem;
}

/* Content inside suggestion cards fades in when expanded.
   The <details> element doesn't support height animation
   natively, but a fade gives perceived smoothness without
   fighting the browser's built-in expand behavior. */
.suggestion-card[open] .suggestion-definition,
.suggestion-card[open] .suggestion-question,
.suggestion-card[open] .suggestion-link,
.suggestion-card[open] .btn-reframe {
    animation: suggestionFadeIn 0.25s ease;
}

@keyframes suggestionFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}

.suggestion-header {
    display: flex;
    flex-direction: column;
    cursor: pointer;
    list-style: none;
    transition: background 0.15s;
    border-radius: var(--radius-sm);
    margin: -0.25rem -0.35rem;
    /* Right padding reserves space for the absolutely-positioned
     * FVS-id label that anchors top-right of each card. Without it
     * a long suggestion-name could collide with the badge. */
    padding: 0.25rem 4rem 0.25rem 0.35rem;
    position: relative;
}

.suggestion-header:hover {
    background: rgba(74, 144, 217, 0.08);
}

.suggestion-header::-webkit-details-marker {
    display: none;
}

.suggestion-name {
    font-weight: 600;
    color: var(--text-1);
}

.suggestion-signal {
    font-weight: 400;
    color: var(--text-3);
    font-size: 0.75rem;
}

.suggestion-definition {
    margin: 0.5rem 0 0.35rem;
    padding: 0.5rem 0.7rem;
    background: rgba(0, 0, 0, 0.035);
    border-left: 2px solid var(--border);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    color: var(--text-2);
    font-size: 0.78rem;
    line-height: 1.55;
}

.suggestion-question {
    font-weight: 400;
    color: var(--blue, #4a90d9);
    font-size: 0.78rem;
    font-style: italic;
    margin: 0.2rem 0 0;
}

/* FVS-id label anchored to the suggestion-header's top-right.
 * Replaces the prior .suggestion-link "Full entry: worked example,
 * generation affordances, vocabulary connections" line that
 * repeated under every card; the badge-shaped identifier doubles
 * as the library link, mirroring the takeaway-questions-frame-id
 * treatment for cross-surface consistency. The default muted
 * color reads as "this is the frame's identifier"; hover and
 * focus reveal it as a link. */
.suggestion-fvs-id {
    position: absolute;
    top: 0.45rem;
    right: 0.6rem;
    font-size: 0.7rem;
    font-family: var(--mono);
    color: var(--text-4);
    line-height: 1.2;
    text-decoration: none;
    transition: color 0.15s;
    /* Higher z-index than the summary so the link is clickable
     * over the details disclosure area; without this, clicking
     * the FVS-id would toggle the <details> instead of navigating
     * to the library entry. */
    z-index: 1;
}

a.suggestion-fvs-id:hover,
a.suggestion-fvs-id:focus-visible {
    color: var(--blue);
    text-decoration: underline;
    outline: none;
}

a.suggestion-fvs-id:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
    border-radius: 2px;
}

/* V4.2 cross-reference chip on Layer A suggestion cards.
 * Two states: "confirms" (V4.2 judge agreed, green semantic) and
 * "did not fire" (V4.2 judge disagreed, neutral gray semantic). The
 * chip makes the two-layer relationship visible to the reader without
 * demoting either signal. Layer A stays as a heuristic hero; V4.2's
 * verdict is carried inline, not hidden in a footnote or pushed into
 * a separate section the reader has to cross-reference manually. */
/* V4.2 cross-reference chip: sits on its own row under the suggestion
 * name line rather than inline next to the name. Round-5 operator
 * feedback flagged the inline layout as cramped (the chip appeared
 * touching the name text on wrapped lines). Own row, self-aligned
 * flex-start so the chip takes only its content width. */
.suggestion-v4_2-chip {
    display: inline-block;
    align-self: flex-start;
    margin-top: 0.35rem;
    padding: 0.18rem 0.6rem;
    border-radius: 999px;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    cursor: help;
    white-space: nowrap;
}

.suggestion-v4_2-confirmed {
    background: var(--green-bg);
    color: var(--green);
    border: 1px solid var(--green-border, rgba(22, 113, 58, 0.2));
}

.suggestion-v4_2-confirmed::before {
    content: "\2713 ";
    font-weight: 900;
}

.suggestion-v4_2-not-fired {
    background: var(--gray-bg, rgba(0, 0, 0, 0.04));
    color: var(--text-3);
    border: 1px solid var(--border-light);
}

.suggestion-v4_2-not-fired::before {
    content: "\2013 ";
    font-weight: 900;
    opacity: 0.7;
}

html.dark .suggestion-v4_2-confirmed {
    background: rgba(22, 113, 58, 0.18);
    border-color: rgba(22, 113, 58, 0.4);
    color: #4ade80;
}

html.dark .suggestion-v4_2-not-fired {
    background: rgba(255, 255, 255, 0.05);
    border-color: rgba(255, 255, 255, 0.1);
}

/* Source Network coverage explainer.
 *
 * Small, subtle expander rendered below the trust-counts breakdown
 * when any claim landed as unresolved. Answers the operator-observed
 * reader question "why only N of M verified?" without pushing a loud
 * disclosure banner into the trust-hero. Collapsed by default; the
 * reader who cares gets the detail on one click.
 */
/* "Why N of M?" disclosure. Lives at the bottom of the trust
 * banner as a footer-style affordance: positive verdict-headline
 * + meter + counts carry the at-a-glance message at the top of
 * the banner, this disclosure is the click-to-expand "show me
 * the breakdown" footer below them.
 *
 * Visual register integrated rather than overlaid: transparent
 * background (the banner's own colored bg shows through), a
 * subtle top-border that marks the break between the banner's
 * primary content and the footer disclosure, lower padding so it
 * doesn't bulk out the banner. The summary line is small and
 * lower-contrast to read as supporting affordance, not a
 * primary action. */
.trust-coverage-note {
    margin-top: var(--space-md);
    padding-top: var(--space-sm);
    border-top: 1px solid var(--border-light);
    font-size: 0.8rem;
    color: var(--text-3);
}

.trust-coverage-note-summary {
    cursor: pointer;
    list-style: none;
    padding: 0.2rem 0;
    color: var(--text-3);
    font-weight: 500;
    user-select: none;
    /* Inline disclosure-arrow + label so the entire summary line
     * is one self-contained affordance the user can click. */
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    transition: color 0.15s ease;
    border-radius: var(--radius-sm);
}

.trust-coverage-note-summary::-webkit-details-marker { display: none; }

.trust-coverage-note-summary::before {
    content: "\25B8"; /* black right-pointing small triangle */
    display: inline-block;
    font-size: 0.75em;
    color: var(--text-4);
    transition: transform 0.15s ease;
    transform-origin: 35% 50%;
}

.trust-coverage-note[open] > .trust-coverage-note-summary::before {
    transform: rotate(90deg);
}

.trust-coverage-note-summary:hover,
.trust-coverage-note-summary:focus-visible {
    color: var(--text-2);
    background: transparent;
    outline: none;
}

.trust-coverage-note-summary:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.trust-coverage-note-body {
    margin-top: 0.5rem;
    padding: 0 0 0 1.1rem;
    border-top: none;
}

.trust-coverage-note-text {
    margin: 0.55rem 0;
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--text-2);
}

.trust-coverage-list {
    margin: 0.4rem 0;
    padding-left: 1.3rem;
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--text-2);
}

.trust-coverage-list li {
    margin-bottom: 0.3rem;
}

/* Doc-specific breakdown variant: denser bullets, slight emphasis on
 * the leading count so the reader can scan "3 verified, 2 contradicted,
 * 1 disputed, 4 not found, 2 future projections" at a glance before
 * reading the rest of the explainer. */
.trust-coverage-list-doc li {
    margin-bottom: 0.4rem;
    line-height: 1.55;
}

.trust-coverage-list-doc strong {
    color: var(--text);
}

html.dark .trust-coverage-note {
    border-top-color: var(--border);
}

html.dark .trust-coverage-note-summary:hover,
html.dark .trust-coverage-note-summary:focus-visible {
    color: var(--text);
}

/* ── Trust Banner ── */

/* Vertical breathing room above and below the banner. The banner
 * lives inside group-verification under the section heading
 * "Source matches and reliability" and above the verification
 * summary + per-claim cards. Without explicit margins those
 * sibling elements sit too close to the banner's strong visual
 * register, making the banner read as squeezed. */
.trust-banner {
    padding: 1.5rem 1.75rem;
    border-radius: var(--radius-lg);
    border-left: 5px solid var(--gray);
    transition: background 0.6s ease, border-left-color 0.6s ease;
    margin: var(--space-md) 0 var(--space-lg) 0;
}

.trust-banner.trust-high       { background: var(--green-bg);  border-left-color: var(--green); }
.trust-banner.trust-moderate   { background: var(--yellow-bg); border-left-color: var(--yellow); }
.trust-banner.trust-low        { background: var(--orange-bg); border-left-color: var(--orange); }
.trust-banner.trust-very-low   { background: var(--red-bg);    border-left-color: var(--red); }
.trust-banner.trust-unknown    { background: var(--gray-bg);   border-left-color: var(--gray); }

/* Analyzing state: trust banner when verification is in progress */
#trust-banner-live.trust-unknown {
    background: var(--blue-bg);
    border-left-color: var(--blue);
}

#trust-banner-live.trust-unknown .trust-label {
    color: var(--blue);
    animation: pulse 1.5s ease-in-out infinite;
}

.trust-level {
    margin-bottom: 0.4rem;
}

.trust-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.trust-high .trust-label       { color: var(--green); }
.trust-moderate .trust-label   { color: var(--yellow); }
.trust-low .trust-label        { color: var(--orange); }
.trust-very-low .trust-label   { color: var(--red); }
.trust-unknown .trust-label    { color: var(--gray); }

.verdict-headline {
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1.3;
    margin-bottom: var(--space-sm);
}

.verdict-meta {
    display: flex;
    gap: var(--space-md);
    font-size: 0.75rem;
    color: var(--text-3);
    margin-bottom: 0.3rem;
    flex-wrap: wrap;
}

/* Method-composition chip in the verdict-meta row. Replaces the
   prior cost-in-dollars pill (which signalled the wrong thing for
   every population: visitors don't pay it, researchers want token
   counts not money, and Phase 1 reading $0 implied "nothing was
   checked"). The chip names what was actually run: structural
   analysis (deterministic, no LLM), source checks (count of
   numerical claims tested against authoritative sources), and AI
   narrative (when the async /api/ai-interpret call returns).
   Construct-honesty is preserved by naming the method, not by
   pricing it. */
.method-summary {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3em;
    font-size: 0.78rem;
    color: var(--text-2);
}

.method-summary-text {
    font-variant-numeric: tabular-nums;
}

.verdict-mode {
    font-size: 0.8rem;
    color: var(--text-2);
    margin-bottom: 0.6rem;
}

.trust-reasons {
    list-style: none;
    margin-top: 0.4rem;
}

.trust-reasons li {
    font-size: 0.82rem;
    color: var(--text-2);
    padding: 0.15rem 0 0.15rem 1.1rem;
    position: relative;
}

.trust-reasons li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.55rem;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.4;
}

/* ── Trust dimensions: visible verification breakdown ──
   A three-segment meter (verified / contradicted / unchecked) with
   counts below. Sits between the headline and the trust reasons so
   the user sees both the verdict and its components in one glance. */
.trust-dimensions {
    margin: 0.6rem 0 0.5rem 0;
}

.trust-meter {
    display: flex;
    height: 8px;
    width: 100%;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 4px;
    overflow: hidden;
}

html.dark .trust-meter {
    background: rgba(255, 255, 255, 0.08);
}

.meter-seg {
    height: 100%;
    transition: width 0.6s ease;
}

.meter-verified {
    background: var(--green);
}

.meter-contradicted {
    background: var(--red);
}

.meter-unchecked {
    background: rgba(0, 0, 0, 0.12);
}

html.dark .meter-unchecked {
    background: rgba(255, 255, 255, 0.12);
}

.trust-counts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 1rem;
    margin-top: 0.4rem;
    font-size: 0.78rem;
    color: var(--text-2);
}

.trust-counts > span {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-xs);
}

.tc-num {
    font-weight: 700;
    font-size: 0.88rem;
}

.tc-verified .tc-num     { color: var(--green); }
.tc-contradicted .tc-num { color: var(--red); }
.tc-unchecked .tc-num    { color: var(--text-3); }
.tc-projections .tc-num  { color: var(--text-3); }
.tc-projections          { color: var(--text-3); cursor: help; }

/* Math-error chip on the trust banner. Styled as a peer of the
 * verified/contradicted/unchecked counts in the row, with red color
 * carrying the severity signal. Hover underline reveals it is a link
 * (anchor into the Mathematical consistency section); no dotted
 * border-bottom in the rest state so it reads as a sibling count,
 * not a louder foreign element. */
.tc-math-error {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-xs);
    color: var(--red);
    text-decoration: none;
}

.tc-math-error:hover {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

.tc-math-error .tc-num {
    color: var(--red);
}

/* Tier breakdown of the verified count: one pill per reliability
   tier that produced a verifying verdict, so a reader scanning the
   trust banner sees at-a-glance whether "3 of 5 verified" leans
   on providers with measured reliability (strong/moderate) or on
   uncalibrated providers. The pills are anchors: clicking lands on
   the calibration corpus, matching the per-card tier badges below.
   Placed under .trust-counts inside .trust-dimensions so the
   layout reads as "totals, then composition." */
.trust-tier-breakdown {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin-top: 0.35rem;
    font-size: 0.78rem;
}
.trust-tier-label {
    color: var(--text-3);
    letter-spacing: 0.02em;
    text-transform: uppercase;
    font-size: 0.68rem;
    font-weight: 600;
    margin-right: 0.15rem;
}
.trust-tier {
    display: inline-flex;
    align-items: baseline;
    gap: 0.2rem;
    padding: 0.1em 0.5em;
    border: 1px solid;
    border-radius: 99px;
    text-decoration: none;
    font-weight: 500;
    line-height: 1.4;
    transition: background 0.1s ease;
}
.trust-tier:hover {
    background: rgba(0, 0, 0, 0.04);
    text-decoration: none;
}
html.dark .trust-tier:hover {
    background: rgba(255, 255, 255, 0.08);
}
.trust-tier-strong       { color: var(--green);  border-color: var(--green); }
.trust-tier-moderate     { color: var(--yellow); border-color: var(--yellow); }
.trust-tier-weak         { color: var(--red);    border-color: var(--red); }
.trust-tier-uncalibrated {
    color: var(--text-3);
    border-color: var(--border);
    font-style: italic;
}

/* Per-provider verified-source list (legacy two-row treatment).
 * Used by compare.html and compare_saved.html where a tier-count
 * row above + a provider list row below render together. Kept
 * intact for those surfaces; results.html uses the single-row
 * .sn-providers-row variant below. The two structures coexist
 * because compare and results have different rendering paths
 * (compare's JS-built fragments inline these class names) and
 * unifying them is out of scope for this pass. */
.trust-providers {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.5rem;
    font-size: 0.78rem;
    color: var(--text-2);
}

.trust-providers-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-4);
    margin-right: 0.2rem;
}

.trust-provider-item {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
}

.trust-provider-name {
    font-weight: 500;
    color: var(--text);
}

.trust-provider-f1 {
    font-family: var(--mono);
    font-size: 0.72rem;
    color: var(--text-3);
}

.trust-provider-tier {
    font-size: 0.66rem;
    font-weight: 600;
    text-transform: lowercase;
    letter-spacing: 0.02em;
    padding: 0.05rem 0.35rem;
    border-radius: 3px;
    border: 1px solid var(--border);
}

.trust-provider-tier-strong       { color: var(--green);  border-color: var(--green); }
.trust-provider-tier-moderate     { color: var(--yellow); border-color: var(--yellow); }
.trust-provider-tier-weak         { color: var(--red);    border-color: var(--red); }
.trust-provider-tier-uncalibrated {
    color: var(--text-3);
    border-color: var(--border);
    font-style: italic;
}

/* Single-row provider list used by results.html. Replaces the
 * legacy .trust-providers two-row treatment for that surface.
 * The previous shape stacked six small chips ("via", "2 strong",
 * "Source:", "Wikipedia", "F1 0.89", "strong") that read as
 * cluttered, particularly on narrow viewports where they wrapped
 * vertically. This format consolidates the same info into one
 * prose line per provider: "Verified via Wikipedia (F1 0.89,
 * strong)". The tier color lives on the pill border so the
 * reliability dimension stays visible without a separate badge.
 * Each pill is a single anchor to /corpus/calibration so the
 * methodology dive survives without dedicated tier-row pills.
 * Uses .sn-prov-* class names rather than the legacy
 * .trust-provider-* names so this rule does not collide with
 * compare.html / compare_saved.html which still render the
 * two-row legacy structure. */
.sn-providers-row {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem 0.55rem;
    margin-top: 0.5rem;
    font-size: 0.82rem;
    color: var(--text-2);
    line-height: 1.5;
}

.sn-prov-prefix {
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--text-3);
}

.sn-prov-pill {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 99px;
    text-decoration: none;
    color: inherit;
    transition: background 0.1s ease;
}

.sn-prov-pill:hover {
    background: rgba(0, 0, 0, 0.04);
    text-decoration: none;
}

html.dark .sn-prov-pill:hover {
    background: rgba(255, 255, 255, 0.06);
}

.sn-prov-name {
    font-weight: 600;
    color: var(--text);
}

.sn-prov-meta {
    font-size: 0.72rem;
    font-family: var(--mono);
    color: var(--text-3);
    line-height: 1.2;
}

.sn-prov-sep {
    color: var(--text-4);
    margin: 0 0.05rem;
}

.sn-prov-tier-strong       { border-color: var(--green); }
.sn-prov-tier-moderate     { border-color: var(--yellow); }
.sn-prov-tier-weak         { border-color: var(--red); }
.sn-prov-tier-uncalibrated {
    border-color: var(--border);
    font-style: italic;
}

/* ── Claim density disclosure ──
   Rendered adjacent to the trust meter so the reader can see the
   denominator alongside the meter's ratios. The meter segments
   answer "what share of claims were verified"; density answers
   "how many claims was that out of." Comparing documents across
   density requires knowing both. REFINEMENT_AUDIT.md bite 5:
   "verbosity sensitivity" documented as a systematic failure
   mode of ratio-based trust meters. Visual weight is small
   relative to the meter (the meter is the primary signal; density
   is a calibrating caveat) but still first-class, not a tooltip. */

.trust-density {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-top: 0.6rem;
    padding: 0.5rem 0.7rem;
    background: var(--surface-2, rgba(0, 0, 0, 0.03));
    border-left: 3px solid var(--text-3);
    border-radius: 0 var(--radius) var(--radius) 0;
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.55;
}

.trust-density-headline {
    font-size: 0.85rem;
    color: var(--text);
}

.trust-density-headline strong {
    font-weight: 700;
    color: var(--text);
}

.trust-density-note {
    font-style: italic;
    color: var(--text-3);
}

/* ── Findings / Guidance cards ── */

.findings-card, .guidance-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1rem 1.25rem;
}

.findings-card h2, .guidance-card h2 {
    font-size: 0.85rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
    margin-bottom: var(--space-sm);
}

.findings-list, .guidance-list {
    list-style: none;
}

.findings-list li, .guidance-list li {
    font-size: 0.88rem;
    padding: 0.25rem 0 0.25rem 1.2rem;
    position: relative;
    line-height: 1.5;
}

.findings-list li::before {
    content: "\2022";
    position: absolute;
    left: 0.2rem;
    color: var(--text-3);
}

.guidance-list li::before {
    content: "\2192";
    position: absolute;
    left: 0;
    color: var(--blue);
}

/* ── Grounding Decomposition (G/F/P) ── */

.gfp-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.9rem 1rem;
    margin-top: var(--space-sm);
}

.gfp-header {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.gfp-label {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-1);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.gfp-status {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
}

.gfp-regime-chip {
    font-size: 0.7rem;
    font-weight: 600;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    text-transform: lowercase;
    letter-spacing: 0.02em;
    margin-left: auto;
    cursor: help;
    border: 1px solid transparent;
}

.gfp-regime-diagnostic {
    color: var(--text-2);
    background: rgba(120, 160, 120, 0.12);
    border-color: rgba(120, 160, 120, 0.3);
}

.gfp-regime-transition {
    color: var(--text-2);
    background: rgba(200, 170, 100, 0.14);
    border-color: rgba(200, 170, 100, 0.35);
}

.gfp-regime-saturated {
    color: var(--text-1);
    background: rgba(200, 120, 100, 0.16);
    border-color: rgba(200, 120, 100, 0.4);
}

.gfp-bar {
    display: flex;
    height: 24px;
    border-radius: 4px;
    overflow: hidden;
    margin-bottom: 0.4rem;
}

.gfp-seg {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.72rem;
    font-weight: 600;
    color: #fff;
    min-width: 0;
    overflow: hidden;
    transition: width 0.3s ease;
}

.gfp-seg:first-child { border-radius: 4px 0 0 4px; }
.gfp-seg:last-child  { border-radius: 0 4px 4px 0; }
.gfp-seg:only-child  { border-radius: 4px; }

.gfp-grounded  { background: var(--green); }
.gfp-framed    { background: var(--yellow); color: #fff; }
.gfp-projected { background: var(--red); }

.gfp-legend {
    display: flex;
    gap: 1rem;
    font-size: 0.78rem;
    color: var(--text-2);
    margin-bottom: 0.4rem;
}

.gfp-legend-item {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

.gfp-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
}

.gfp-dot-g { background: var(--green); }
.gfp-dot-f { background: var(--yellow); }
.gfp-dot-p { background: var(--red); }

.gfp-definitions {
    font-size: 0.72rem;
    color: var(--text-3);
    margin: 0 0 0.4rem 0;
    line-height: 1.4;
}

.gfp-projection-alert {
    display: flex;
    gap: 0.6rem;
    background: var(--red-bg);
    border: 1px solid var(--red);
    border-radius: 4px;
    padding: 0.6rem 0.8rem;
    margin-top: 0.5rem;
}

.gfp-alert-icon {
    font-weight: 700;
    color: var(--red);
    font-size: 0.9rem;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    border: 2px solid var(--red);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.gfp-alert-body {
    flex: 1;
}

.gfp-alert-text {
    font-size: 0.82rem;
    color: var(--text-1);
    margin: 0 0 0.3rem 0;
}

.gfp-recommendation-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

.gfp-recommendation {
    font-size: 0.75rem;
    color: var(--text-3);
    margin: 0 0 0.2rem 0;
}

.gfp-copy-btn {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-3);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.15rem 0.5rem;
    cursor: pointer;
    flex-shrink: 0;
    transition: color 0.15s, border-color 0.15s;
}

.gfp-copy-btn:hover,
.gfp-copy-btn:focus-visible {
    color: var(--text-1);
    border-color: var(--text-3);
    outline: 2px solid var(--text-3);
    outline-offset: 1px;
}

.gfp-prohibition {
    display: block;
    font-size: 0.8rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: 0.4rem 0.6rem;
    color: var(--text-1);
    cursor: text;
    user-select: all;
    word-break: break-word;
}

.gfp-clean {
    font-size: 0.8rem;
    color: var(--green);
    margin: 0.3rem 0 0 0;
}

.gfp-projected-details {
    margin-top: 0.5rem;
}

.gfp-projected-details summary {
    font-size: 0.78rem;
    color: var(--text-3);
    cursor: pointer;
}

.gfp-projected-details summary:hover {
    color: var(--text-2);
}

.gfp-projected-list {
    list-style: none;
    padding: 0;
    margin: 0.3rem 0 0 0;
}

.gfp-projected-list li {
    font-size: 0.78rem;
    padding: 0.3rem 0;
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.gfp-projected-list li:last-child {
    border-bottom: none;
}

.gfp-p-sentence {
    color: var(--text-1);
}

.gfp-p-reason {
    color: var(--text-3);
    font-size: 0.72rem;
    font-style: italic;
}

/* ── Section Groups ── */

.section-group {
    margin-top: var(--space-sm);
}

.group-header {
    margin-bottom: 0.6rem;
}

.group-header h2 {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--text);
}

.group-note {
    font-size: 0.78rem;
    color: var(--text-3);
    margin-top: 0.15rem;
}

/* Verification summary block at the head of group-verification.
   Holds the per-bucket coverage-note, tier breakdown, per-provider F1,
   and claim density disclosure. These were trimmed out of the trust
   banner (where they were evidence-about-evidence sitting ~1700 lines
   from the per-claim cards they qualify) and consolidated here.

   Bottom margin separates the summary from the sn-card stack below;
   inner blocks supply their own top-margins, so the wrapper carries
   only spacing that does not naturally belong on its children. */
.verification-summary {
    margin-bottom: 0.9rem;
}

/* ── Section Cards (details/summary) ── */

.section-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-bottom: var(--space-sm);
    overflow: hidden;
}

.section-card[open] {
    border-color: var(--border);
}

.section-summary {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.7rem 1rem;
    cursor: pointer;
    user-select: none;
    gap: var(--space-lg);
    list-style: none;
}

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

.section-summary::before {
    content: "\25B8";
    font-size: 0.7rem;
    color: var(--text-4);
    flex-shrink: 0;
    transition: transform 0.15s;
    width: 0.8rem;
}

.section-card[open] > .section-summary::before {
    transform: rotate(90deg);
}

.section-summary:hover { background: var(--bg); }

/* Separator + breathing room when a section-card is expanded.
 *
 * The operator flagged that the summary's hover background sat
 * directly adjacent to the body text below, making the transition
 * feel cramped ("the black is pretty much getting very close to
 * the text below"). A hairline border + a few pixels of top
 * padding on the body gives the hover highlight a clean visual
 * terminus before body content begins, without adding structural
 * weight when the card is collapsed.
 */
.section-card[open] > .section-summary {
    border-bottom: 1px solid var(--border-light);
}

.section-card[open] > .section-body {
    padding-top: 0.75rem;
}

.section-title-area {
    flex: 1;
    min-width: 0;
}

.section-title {
    font-weight: 600;
    font-size: 0.88rem;
    display: block;
}

.section-one-liner {
    font-size: 0.78rem;
    color: var(--text-3);
    display: block;
    margin-top: 0.1rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── Section badges ── */

.section-badge {
    display: inline-block;
    padding: 0.12rem 0.5rem;
    border-radius: var(--radius-sm);
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    white-space: nowrap;
    flex-shrink: 0;
}

.section-badge.severity-info     { background: var(--gray-bg);   color: var(--gray);   border: 1px solid var(--gray-border); }
.section-badge.severity-low      { background: var(--green-bg);  color: var(--green);  border: 1px solid var(--green-border); }
.section-badge.severity-moderate { background: var(--yellow-bg); color: var(--yellow); border: 1px solid var(--yellow-border); }
.section-badge.severity-high     { background: var(--orange-bg); color: var(--orange); border: 1px solid var(--orange-border); }
.section-badge.severity-critical { background: var(--red-bg);    color: var(--red);    border: 1px solid var(--red-border); }

/* ── Section body ── */

.section-body {
    padding: 0 1rem 1rem 1.8rem;
}

.section-explanation {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.6;
    margin-bottom: var(--space-md);
}

.section-reference {
    font-size: 0.72rem;
    font-family: var(--mono);
    color: var(--text-4);
    margin-bottom: var(--space-md);
}

/* ── Metrics row ── */

.metrics-row {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: var(--space-md);
}

.metric-card {
    flex: 1;
    min-width: 120px;
    padding: 0.6rem 0.75rem;
    background: var(--bg);
    border-radius: 4px;
    border-left: 3px solid var(--border);
}

.metric-card.severity-low      { border-left-color: var(--green); }
.metric-card.severity-moderate  { border-left-color: var(--yellow); }
.metric-card.severity-high      { border-left-color: var(--orange); }
.metric-card.severity-info      { border-left-color: var(--text-4); }

.metric-value {
    display: block;
    font-family: var(--mono);
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1.2;
}

.metric-label {
    display: block;
    font-size: 0.72rem;
    color: var(--text-3);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-top: 0.15rem;
}

.metric-detail {
    display: block;
    font-size: 0.72rem;
    color: var(--text-4);
    margin-top: 0.1rem;
}

/* ── Flagged items ── */

.flagged-section h3 {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    margin-bottom: var(--space-sm);
}

.flagged-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.flagged-item {
    background: var(--bg);
    padding: 0.5rem 0.75rem;
    border-radius: 4px;
    border-left: 3px solid var(--orange-border);
    font-size: 0.82rem;
}

.flagged-value {
    font-family: var(--mono);
    font-weight: 700;
    color: var(--orange);
}

.flagged-type {
    font-size: 0.72rem;
    color: var(--text-4);
    margin-left: var(--space-sm);
}

.flagged-context {
    font-size: 0.78rem;
    color: var(--text-2);
    margin: 0.2rem 0;
    font-style: italic;
    line-height: 1.4;
}

.flagged-reason {
    display: block;
    font-size: 0.72rem;
    color: var(--text-3);
    margin-top: 0.15rem;
}

.more-items {
    font-size: 0.78rem;
    color: var(--text-4);
    text-align: center;
    padding: 0.3rem;
}

/* ── Examples ── */

.examples-section h3 {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    margin-bottom: 0.4rem;
}

.examples-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.example-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0.5rem;
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: 0.78rem;
}

.example-tag small {
    font-size: 0.65rem;
    text-transform: uppercase;
    opacity: 0.7;
}

.example-tag.stable { background: var(--green-bg); color: var(--green); }
.example-tag.unstable { background: var(--red-bg); color: var(--red); }

/* ── Tags ── */

.section-tags {
    margin-top: var(--space-sm);
    display: flex;
    gap: 0.4rem;
}

.tag {
    font-size: 0.65rem;
    color: var(--text-4);
    border: 1px solid var(--border-light);
    padding: 0.08rem 0.4rem;
    border-radius: var(--radius-sm);
}

/* ── Research program card ──
   Identity statement at the bottom of every results page. Two
   internal links (methodology, frame library), one follow link to
   the author's writing, one muted metadata line (author + license).
   No forms, no captures. Reads as a closing signature rather than a
   promotional block. Dark mode works via existing CSS vars without
   an override; focus states use the global :focus-visible treatment
   plus one explicit rule below for keyboard-only card navigation. */
.research-program {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.1rem 1.4rem;
    margin-top: var(--space-lg);
    background: var(--surface);
}

.research-program-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text);
    margin: 0 0 0.4rem 0;
    line-height: 1.45;
}

.research-program-lede {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.55;
    margin: 0 0 0.9rem 0;
    max-width: 60ch;
}

.research-program-links {
    list-style: none;
    padding: 0;
    margin: 0 0 0.7rem 0;
    display: flex;
    gap: 1.2rem;
    flex-wrap: wrap;
}

.research-program-links li { margin: 0; }

.research-program-follow {
    margin: 0 0 0.9rem 0;
    font-size: 0.85rem;
}

/* Shared link treatment for every anchor inside the card so the
   two read-the-method links and the follow link look consistent. */
.research-program a {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--blue);
    text-decoration: none;
}

.research-program a:hover {
    text-decoration: underline;
}

.research-program a:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 3px;
    border-radius: 2px;
}

.research-program-meta {
    margin: 0;
    font-size: 0.72rem;
    color: var(--text-3);
    line-height: 1.5;
    letter-spacing: 0.01em;
}

/* ── Collapsible Sections ── */

.section-group-collapsible {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    margin-top: var(--space-lg);
    background: var(--surface);
}

.group-header-toggle {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.85rem 1.25rem;
    cursor: pointer;
    user-select: none;
    list-style: none;
}

.group-header-toggle::-webkit-details-marker { display: none; }

.group-header-toggle::before {
    content: "\25B8";
    font-size: 0.7rem;
    color: var(--text-4);
    margin-right: var(--space-sm);
    transition: transform 0.2s;
}

.section-group-collapsible[open] > .group-header-toggle::before {
    transform: rotate(90deg);
}

.group-header-toggle h2 {
    font-size: 0.9rem;
    font-weight: 600;
    margin: 0;
}

.toggle-hint {
    font-size: 0.75rem;
    color: var(--text-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.section-group-collapsible > .section-card,
.section-group-collapsible > .claims-list,
.section-group-collapsible > .insight-card {
    margin: 0 0.75rem 0.75rem;
}

/* (.limitations-toggle removed: the limitations section is no
   longer collapsible. See .results-caveat module further down
   for the always-visible replacement.) */

/* ── Mathematical Consistency ── */

.consistency-finding {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    margin-bottom: var(--space-sm);
    border-left: 3px solid var(--border);
}

.consistency-error { border-left-color: var(--red); background: rgba(239, 68, 68, 0.05); }
.consistency-warning { border-left-color: var(--yellow); background: rgba(245, 158, 11, 0.06); }

.consistency-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.3rem;
}

.consistency-type {
    font-weight: 700;
    font-size: 0.85rem;
}

.consistency-detail {
    font-size: 0.85rem;
    color: var(--text);
    line-height: 1.5;
}

.consistency-context {
    font-size: 0.78rem;
    color: var(--text-2);
    font-style: italic;
    margin-top: 0.4rem;
    line-height: 1.5;
    padding-left: 0.7rem;
    border-left: 2px solid var(--border-light);
}

.consistency-context-label {
    font-style: normal;
    font-weight: 600;
    color: var(--text-3);
    margin-right: 0.3rem;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Structured math-breakdown for consistency findings that carry a
 * populated `values` dict (growth_rate today; future findings can
 * opt in). Renders stated-vs-computed side by side plus the
 * magnitude, so the reader sees the specific discrepancy at a
 * glance instead of parsing a prose sentence. */
.consistency-math {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin: 0.5rem 0 0.4rem;
    padding: 0.55rem 0.75rem;
    background: var(--surface);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
}

.consistency-math-row {
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    flex-wrap: wrap;
}

.consistency-math-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
    min-width: 6rem;
}

.consistency-math-value {
    font-family: var(--mono);
    font-size: 0.88rem;
    color: var(--text);
    font-weight: 500;
}

.consistency-math-stated {
    color: var(--red);
    font-weight: 700;
}

.consistency-math-actual {
    color: var(--green);
    font-weight: 700;
}

.consistency-math-delta {
    padding-top: 0.3rem;
    border-top: 1px solid var(--border-light);
    margin-top: 0.15rem;
}

.consistency-context-link {
    display: inline-block;
    margin-top: 0.5rem;
    font-size: 0.78rem;
    color: var(--blue);
    text-decoration: none;
}

.consistency-context-link:hover {
    text-decoration: underline;
}

html.dark .consistency-math {
    background: var(--surface);
    border-color: var(--border);
}
html.dark .consistency-error { background: rgba(239, 68, 68, 0.1); }
html.dark .consistency-warning { background: rgba(245, 158, 11, 0.1); }

html.dark .consistency-math-delta {
    border-top-color: rgba(255, 255, 255, 0.1);
}

.consistency-values {
    display: flex;
    gap: var(--space-md);
    margin-top: 0.4rem;
    flex-wrap: wrap;
}

.cv {
    font-family: var(--mono);
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.15rem 0.5rem;
    background: rgba(0,0,0,0.04);
    border-radius: var(--radius-sm);
}

/* ── Annotated Document ── */

.annotated-document {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.5rem 1.75rem;
    font-family: Georgia, "Times New Roman", serif;
    font-size: 0.88rem;
    line-height: 1.75;
    color: var(--text);
    max-height: 500px;
    overflow-y: auto;
    /* overflow-x: hidden + overflow-wrap: break-word suppresses
     * the horizontal scrollbar that browsers spawn on
     * overflow-y: auto containers when content can be wider than
     * the column. The CSS spec couples the two axes: setting one
     * to auto auto-promotes the other to auto, so without an
     * explicit hidden the container shows a horizontal scrollbar
     * any time a long URL, monospace block, or unbreakable string
     * exceeds the column width. The compare model cards hit this
     * regularly because the annotated document sits inside a
     * minmax(0, 1fr) grid item that can shrink narrower than the
     * native min-content of long sentences. break-word forces
     * unbreakable words (URLs, IDs) to wrap at the container
     * edge instead of triggering overflow. */
    overflow-x: hidden;
    overflow-wrap: break-word;
    word-wrap: break-word;
    box-shadow: 0 1px 3px rgba(0,0,0,0.04);
    /* Bottom margin so the FACT / HEDGED / PREDICTION legend that
     * sits BELOW the side-by-side model cards has breathing room
     * away from the document text above. Without this, the legend
     * crowds the bottom edge of the annotated-document container
     * and reads as an extension of the same block instead of as
     * its own labelled key. */
    margin-bottom: var(--space-md);
}

.annotated-document .doc-h2 {
    font-family: var(--font);
    font-weight: 700;
    font-size: 1rem;
    margin: 1.25rem 0 0.4rem;
    color: var(--text);
    border-bottom: 1px solid var(--border-light);
    padding-bottom: 0.2rem;
}

.annotated-document .doc-h2:first-child { margin-top: 0; }

.annotated-document .doc-h3 {
    font-family: var(--font);
    font-weight: 600;
    font-size: 0.92rem;
    margin: 1rem 0 0.3rem;
    color: var(--text);
}

.annotated-document .doc-h4 {
    font-family: var(--font);
    font-weight: 600;
    font-size: 0.85rem;
    margin: 0.75rem 0 0.2rem;
    color: var(--text-2);
}

.annotated-document .doc-break {
    height: 0.6rem;
}

.annotated-document .doc-bullet {
    padding-left: var(--space-lg);
}

/* Claim highlights.
   Marks are clickable: clicking opens the source-network popover.
   Padding is slightly increased on touch devices to give fingers a
   reasonable target without breaking inline flow.

   Two transition timings:
   - background/border-bottom-color: 0.5s, slow. Used when verification
     state CHANGES (hl-fact -> hl-verified, etc.) after SSE updates; the
     slower duration reads as "the verdict is arriving" rather than a
     jitter.
   - box-shadow: 0.15s, fast. Used for the :hover ring which should
     respond to the pointer immediately (micro-interaction feedback). */
.annotated-document mark {
    padding: 0.05rem 0.25rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: inherit;
    transition:
        background 0.5s ease,
        border-bottom-color 0.5s ease,
        box-shadow 0.15s ease;
}

/* Hover affordance on interactive marks. Subtle ring deepens the
 * mark when hovered so the clickability is clear. Uses box-shadow
 * instead of outline because outline doesn't respect border-radius
 * on older browsers, and box-shadow renders inside the inline flow
 * without pushing surrounding text. */
.annotated-document mark:hover {
    box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.08);
}

html.dark .annotated-document mark:hover {
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.12);
}

/* Framing highlights (initial state, before verification) */
mark.hl-fact {
    background: rgba(234, 88, 12, 0.12);
    border-bottom: 2px solid var(--orange);
}

mark.hl-hedged {
    background: rgba(22, 163, 74, 0.10);
    border-bottom: 2px solid var(--green);
}

mark.hl-prediction {
    background: rgba(37, 99, 235, 0.10);
    border-bottom: 2px solid var(--blue);
}

/* Verification status highlights (replace framing after SSE) */
mark.hl-verified {
    background: rgba(22, 163, 74, 0.15);
    border-bottom: 2px solid var(--green);
}

mark.hl-contradicted {
    background: rgba(185, 28, 28, 0.15);
    border-bottom: 2px solid var(--red);
}

mark.hl-partial {
    background: rgba(146, 98, 10, 0.12);
    border-bottom: 2px solid var(--yellow);
}

mark.hl-outdated {
    background: rgba(146, 98, 10, 0.08);
    border-bottom: 2px dashed var(--yellow);
}

mark.hl-unresolved {
    background: rgba(0, 0, 0, 0.04);
    border-bottom: 2px dotted var(--text-4);
}

/* Legend */
.legend {
    display: inline-flex;
    gap: var(--space-md);
    margin-left: var(--space-sm);
}

.legend-item {
    font-size: 0.72rem;
}

.legend-item mark {
    font-size: 0.72rem;
    padding: 0.05rem 0.3rem;
    color: inherit;
}

/* ── Claims Analysis ── */

.insight-card {
    padding: 0.75rem 1rem;
    border-radius: var(--radius);
    border-left: 3px solid var(--border);
    margin-bottom: var(--space-md);
}

.insight-card.severity-moderate {
    background: var(--yellow-bg);
    border-left-color: var(--yellow);
}

.insight-card.severity-low {
    background: var(--green-bg);
    border-left-color: var(--green);
}

.insight-text {
    font-size: 0.85rem;
    color: var(--text);
    line-height: 1.5;
}

.insight-indicators {
    list-style: none;
    margin-top: 0.4rem;
}

.insight-indicators li {
    font-size: 0.78rem;
    color: var(--text-2);
    padding: 0.1rem 0 0.1rem 1rem;
    position: relative;
}

.insight-indicators li::before {
    content: "\25B8";
    position: absolute;
    left: 0;
    color: var(--text-4);
    font-size: 0.65rem;
}

.claims-purpose {
    font-size: 0.78rem;
    color: var(--text-3);
    line-height: 1.55;
    margin: 0.4rem 0 0.85rem;
    max-width: 56ch;
}

/* Structural Profile intro: a brief explanation of what the
   collapsed groups below contain, before the user sees three
   identical-looking toggles with no context. The eyebrow label
   matches the eyebrow pattern used by the home page instruments
   panel and the about-page caveats. */
.structural-profile-intro {
    margin: var(--space-xl) 0 var(--space-md);
    padding-top: var(--space-lg);
    border-top: 1px solid var(--border-light);
}

.structural-profile-intro-label {
    display: inline-block;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    background: var(--gray-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.15rem var(--space-sm);
    margin-bottom: var(--space-sm);
}

.structural-profile-intro p {
    font-size: 0.82rem;
    color: var(--text-2);
    line-height: 1.6;
    margin: 0;
}

.claims-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.claim-item {
    background: var(--surface);
    border: 1px solid var(--border-light);
    border-radius: 4px;
    padding: 0.5rem 0.75rem;
    border-left: 3px solid var(--border);
}

.claim-item.claim-stated_as_fact { border-left-color: var(--orange); }
.claim-item.claim-hedged { border-left-color: var(--green); }
.claim-item.claim-prediction { border-left-color: var(--blue); }

.claim-header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-bottom: 0.2rem;
}

.claim-numbers {
    font-family: var(--mono);
    font-weight: 700;
    font-size: 0.88rem;
    color: var(--text);
}

.claim-verify-badge {
    display: none;
    font-size: 0.75rem;
    font-weight: 700;
    width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    border-radius: 50%;
    flex-shrink: 0;
}

.cvb-verified { background: var(--green-bg); color: var(--green); }
.cvb-contradicted { background: var(--red-bg); color: var(--red); }
.cvb-partial { background: var(--yellow-bg); color: var(--yellow); }
.cvb-outdated { background: var(--yellow-bg); color: var(--yellow); }
.cvb-unresolved { background: var(--gray-bg); color: var(--gray); }

.claim-framing-badge {
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-sm);
}

.framing-fact {
    background: var(--orange-bg);
    color: var(--orange);
    border: 1px solid var(--orange-border);
}

.framing-hedged {
    background: var(--green-bg);
    color: var(--green);
    border: 1px solid var(--green-border);
}

.framing-prediction {
    background: var(--blue-bg);
    color: var(--blue);
    border: 1px solid var(--blue-border);
}

.precision-flag {
    font-size: 0.6rem;
    color: var(--text-4);
    border: 1px solid var(--border);
    padding: 0.05rem 0.3rem;
    border-radius: var(--radius-sm);
}

.claim-sentence {
    font-size: 0.8rem;
    color: var(--text-2);
    line-height: 1.45;
}

.claim-hedge-words {
    display: block;
    font-size: 0.7rem;
    color: var(--green);
    margin-top: 0.15rem;
}

/* ── Pipeline Results ── */

.pipeline-stats {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 0.4rem;
}

/* Web-grounding scope note: explains the 1-claim-per-request cap
 * so the "web verified" count doesn't read as a partial result. */
.pipeline-scope-note {
    margin: 0 0 var(--space-md) 0;
    padding: 0.5rem 0 0.5rem 0.7rem;
    border-left: 2px solid var(--text-4);
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--text-3);
}

.pipeline-scope-note a {
    color: var(--blue);
    text-decoration: none;
}

.pipeline-scope-note a:hover {
    text-decoration: underline;
}

html.dark .pipeline-scope-note { color: var(--text-3); border-left-color: var(--text-4); }

.pipeline-improvement {
    font-size: 0.82rem;
    color: var(--green);
    font-weight: 600;
    margin-bottom: var(--space-lg);
    padding: 0.5rem 0.75rem;
    background: var(--green-bg);
    border-radius: 4px;
}

.evidence-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1rem 1.25rem;
    margin-bottom: 0.6rem;
    transition: border-left-color 0.4s ease, background 0.4s ease, box-shadow 0.4s ease;
}

.evidence-card.severity-low { box-shadow: 0 1px 3px rgba(22, 163, 74, 0.08); }
.evidence-card.severity-high { box-shadow: 0 1px 3px rgba(185, 28, 28, 0.08); }
.evidence-card.severity-moderate { box-shadow: 0 1px 3px rgba(146, 98, 10, 0.08); }

/* ── Verification Cards (live feed) ── */

.verify-card {
    display: flex;
    gap: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    margin-bottom: 0.6rem;
    transition: border-color 0.4s ease, box-shadow 0.4s ease;
}

.verify-card-status {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    min-height: 60px;
    flex-shrink: 0;
    font-size: 1.1rem;
    font-weight: 700;
    transition: background 0.4s ease, color 0.4s ease;
}

.verify-card-content {
    flex: 1;
    padding: 0.85rem 1rem;
    min-width: 0;
}

.verify-card-header {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
    margin-bottom: 0.3rem;
}

.verify-value {
    font-family: var(--mono);
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--text);
}

.verify-badge {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.12rem 0.5rem;
    border-radius: 4px;
    transition: background 0.3s ease, color 0.3s ease;
}

.verify-confidence {
    font-size: 0.7rem;
    font-family: var(--mono);
    color: var(--text-3);
}

.verify-context {
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.45;
    margin: 0;
}

.verify-evidence {
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    transition: max-height 0.4s ease, opacity 0.3s ease, margin 0.3s ease;
    margin-top: 0;
}

.verify-evidence.revealed {
    max-height: 200px;
    opacity: 1;
    margin-top: var(--space-sm);
}

.verify-evidence-text {
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.5;
    margin: 0;
    padding: 0.5rem 0.6rem;
    background: var(--bg);
    border-radius: var(--radius);
    border-left: 3px solid var(--border);
}

.verify-evidence-url {
    display: none;
    font-size: 0.72rem;
    color: var(--blue);
    text-decoration: none;
    margin-top: var(--space-xs);
}

.verify-evidence-url.revealed {
    display: inline-block;
}

.verify-evidence-url:hover { text-decoration: underline; }

.verify-evidence-url.source-tier-1 { color: var(--green); font-weight: 600; }
.verify-evidence-url.source-tier-2 { color: var(--blue); }

/* ── Card States ── */

/* Checking: shimmer animation */
.verify-card.checking {
    border-color: var(--blue-border);
}

.verify-card.checking .verify-card-status {
    background: var(--blue-bg);
    color: var(--blue);
}

/* Verify-card checking state uses the flowing-border loader pattern.
 * @property --loading-angle and @keyframes loader-sweep are declared
 * in the .loading-spinner block (search "Frame-shaped loader"). */
.verify-card.checking .verify-icon {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--border);
    border-radius: 3px;
    position: relative;
}

/* Shared flowing-border ::before rule is consolidated further down in
 * this file (search "Flowing-border loader: shared ::before rule").
 * This selector is included in that combined rule, so no per-spinner
 * ::before block is needed here. */

.verify-card.checking .verify-badge {
    background: var(--blue-bg);
    color: var(--blue);
    border: 1px solid var(--blue-border);
    animation: pulse 1.5s ease-in-out infinite;
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

/* Verified: solid, settled, green */
.verify-card.verified {
    border-color: var(--green-border);
    box-shadow: 0 1px 4px rgba(22, 163, 74, 0.1);
}

.verify-card.verified .verify-card-status {
    background: var(--green);
    color: white;
}

.verify-card.verified .verify-badge {
    background: var(--green-bg);
    color: var(--green);
    border: 1px solid var(--green-border);
}

.verify-card.verified .verify-evidence-text {
    border-left-color: var(--green);
}

/* Contradicted: attention-demanding, red */
.verify-card.contradicted {
    border-color: var(--red-border);
    box-shadow: 0 1px 4px rgba(185, 28, 28, 0.1);
}

.verify-card.contradicted .verify-card-status {
    background: var(--red);
    color: white;
}

.verify-card.contradicted .verify-badge {
    background: var(--red-bg);
    color: var(--red);
    border: 1px solid var(--red-border);
}

.verify-card.contradicted .verify-evidence-text {
    border-left-color: var(--red);
    background: var(--red-bg);
}

/* Partially verified: amber, nuanced */
.verify-card.partial {
    border-color: var(--yellow-border);
    box-shadow: 0 1px 4px rgba(146, 98, 10, 0.08);
}

.verify-card.partial .verify-card-status {
    background: var(--yellow);
    color: white;
}

.verify-card.partial .verify-badge {
    background: var(--yellow-bg);
    color: var(--yellow);
    border: 1px solid var(--yellow-border);
}

.verify-card.partial .verify-evidence-text {
    border-left-color: var(--yellow);
}

/* Outdated: muted, historical */
.verify-card.outdated {
    border-color: var(--yellow-border);
    opacity: 0.85;
}

.verify-card.outdated .verify-card-status {
    background: var(--yellow-bg);
    color: var(--yellow);
}

.verify-card.outdated .verify-badge {
    background: var(--yellow-bg);
    color: var(--yellow);
    border: 1px solid var(--yellow-border);
}

/* Unresolved: uncertain, gray */
.verify-card.unresolved {
    border-color: var(--gray-border);
}

.verify-card.unresolved .verify-card-status {
    background: var(--gray-bg);
    color: var(--gray);
}

.verify-card.unresolved .verify-badge {
    background: var(--gray-bg);
    color: var(--gray);
    border: 1px solid var(--gray-border);
}

/* ── Progress Bar ── */

.verify-progress {
    height: 4px;
    background: rgba(0,0,0,0.06);
    border-radius: 2px;
    margin: 0.75rem 0 0.4rem;
    overflow: hidden;
}

.verify-progress-bar {
    height: 100%;
    width: 0%;
    background: var(--blue);
    border-radius: 2px;
    transition: width 0.5s ease;
}

.verify-progress-bar.complete {
    background: var(--green);
}

.verify-progress-bar.error {
    background: var(--red);
}

.verify-progress-status {
    display: flex;
    justify-content: space-between;
    font-size: 0.72rem;
    color: var(--text-3);
    margin-bottom: var(--space-xs);
}

.evidence-card.severity-low      { border-left-color: var(--green); }
.evidence-card.severity-moderate  { border-left-color: var(--yellow); }
.evidence-card.severity-high      { border-left-color: var(--orange); }

.evidence-header {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
    margin-bottom: 0.3rem;
}

.confidence-score {
    font-size: 0.72rem;
    color: var(--text-3);
    font-family: var(--mono);
}

.evidence-chain {
    margin-top: 0.4rem;
    padding-left: var(--space-sm);
    border-left: 2px solid var(--border-light);
}

.chain-step {
    padding: 0.25rem 0 0.25rem 0.5rem;
    font-size: 0.78rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.chain-stage {
    font-weight: 600;
    color: var(--text-3);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    min-width: 100px;
}

.chain-detail {
    color: var(--text-2);
    flex: 1;
    min-width: 150px;
}

.chain-url {
    font-size: 0.7rem;
    color: var(--blue);
    text-decoration: none;
}

.chain-url:hover {
    text-decoration: underline;
}

/* ── Verification (legacy) ── */

.verification-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.25rem;
}

.verification-summary {
    font-size: 0.88rem;
    color: var(--text-2);
    margin-bottom: var(--space-md);
}

.verification-stats {
    display: flex;
    gap: var(--space-md);
    flex-wrap: wrap;
    margin-bottom: var(--space-md);
}

.vstat {
    font-size: 0.82rem;
    font-weight: 600;
    padding: 0.2rem 0.6rem;
    border-radius: 4px;
}

.vstat-verified    { background: var(--green-bg);  color: var(--green); }
.vstat-false       { background: var(--red-bg);    color: var(--red); }
.vstat-unverifiable { background: var(--orange-bg); color: var(--orange); }
.vstat-time        { background: var(--gray-bg);   color: var(--text-3); }

.verification-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}

.verification-table th {
    text-align: left;
    padding: 0.4rem 0.6rem;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.verification-table td {
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--border-light);
    vertical-align: top;
}

.evidence-cell {
    font-size: 0.78rem;
    color: var(--text-2);
    max-width: 300px;
}

/* ── Limitations ──
   Promoted from a buried collapsible to an always-visible yellow
   caveat card matching the about-page treatment. The constraint
   ("what this tool cannot detect") is the most important context
   for calibrating trust in everything above, so it earns the
   visible weight. Lives at the bottom of the results page so
   the user reads the verdict and details first, then encounters
   the caveat as a deliberate closing note. */

/* Limitations caveat. Operator's call (2026-05-05): the prior
 * yellow callout treatment (background + border-left + boxed
 * padding) gave this surface visual weight on par with the
 * verdict-hero. The caveat is important context (what the tool
 * does NOT measure) but it is meta-info, not a load-bearing
 * finding. Reduced to a collapsible disclosure: closed by
 * default, summary line shows the label, click expands the
 * bullet list. Native <details> element so no JS, accessibility
 * is built-in (keyboard, screenreader announce expansion). */
.results-caveat {
    display: block;
    margin-top: var(--space-lg);
    border-top: 1px solid var(--border-light);
    padding-top: var(--space-md);
}

/* Flex layout keeps the chevron vertically centered with the
 * label text regardless of font-size, line-height, or zoom
 * level. The prior absolute-positioned chevron drifted on the
 * vertical axis depending on context (operator-flagged
 * 2026-05-05). */
.results-caveat-summary {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    list-style: none;
    cursor: pointer;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-2);
    line-height: 1.4;
}

/* Default <details>/<summary> marker varies across browsers and
 * is sometimes a triangle, sometimes a circle. Replace with a
 * consistent chevron rotated by [open] state so the affordance
 * is the same on every renderer. */
.results-caveat-summary::-webkit-details-marker { display: none; }
.results-caveat-summary::marker { content: ""; }
.results-caveat-summary::before {
    content: "";
    flex-shrink: 0;
    width: 0;
    height: 0;
    border-left: 4px solid currentColor;
    border-top: 3px solid transparent;
    border-bottom: 3px solid transparent;
    transition: transform 0.15s ease;
}
.results-caveat[open] > .results-caveat-summary::before {
    transform: rotate(90deg);
}

.results-caveat-summary:hover,
.results-caveat-summary:focus-visible {
    color: var(--text);
    outline: none;
}

.results-caveat-list {
    list-style: none;
    padding: 0;
    margin: 0.6rem 0 0 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.results-caveat-list li {
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.55;
    padding-left: 1.2rem;
    position: relative;
}

/* Marker tied to muted text color now that the surface is no
 * longer a yellow warning callout. Reads as a list bullet, not
 * a danger indicator. */
.results-caveat-list li::before {
    content: "×";
    position: absolute;
    left: 0.2rem;
    top: 0;
    color: var(--text-3);
    font-weight: 700;
    font-size: 0.9rem;
    line-height: 1.6;
}

.results-caveat-footnote {
    font-size: 0.72rem;
    color: var(--text-3);
    margin-top: 0.7rem;
    padding-top: 0.55rem;
    border-top: 1px dashed var(--yellow-border);
    font-style: italic;
    line-height: 1.55;
}

/* (.limitations-card / .limitation-list / .thresholds-note rules
   removed: the previous collapsible treatment let users finish
   the analysis without ever seeing what the tool cannot detect.
   The .results-caveat module above replaces it.) */

/* ── Actions ── */

.result-actions {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
    align-items: center;
    /* Mirror the /check input-footer top spacing so the post-results
     * action row (Check another document / Compare with another /
     * Re-run / Save / Download / Print) has the same breathing room
     * above it that the Check submit button has on /check. Both
     * surfaces use --space-md (12px) for visual parity across the
     * primary CTA rows on /check, /compare, and /results. */
    margin-top: var(--space-md);
}

/* Right-align the secondary action cluster so the primary action
 * (Save & share on runner views, Check your own document on saved
 * views) anchors left and the rest of the cluster sits on the
 * right. Mirrors the .input-footer pattern on /check. The first
 * item of the secondary cluster carries .action-cluster-start;
 * margin-left:auto on that item pushes everything after it to the
 * right edge. Survives flex-wrap on narrow viewports: when the row
 * wraps, the auto-margin collapses and the buttons stack naturally.
 *
 * The rule applies to the Check another link on runner views and to
 * the Copy share link button on saved views (no Save & share on the
 * saved branch, so Check your own document IS the primary and the
 * cluster starts at Copy share link). */
.result-actions .action-cluster-start {
    margin-left: auto;
}

/* Re-run analysis form sits inline with the other action buttons.
 * The embedded turnstile widget (when configured) renders above the
 * button; without turnstile, just the button shows. */
.rerun-form {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
}

.rerun-form .cf-turnstile {
    display: inline-block;
    transform: scale(0.8);
    transform-origin: left center;
    /* Same scale-void absorption as .input-actions .cf-turnstile so the
       Re-run button sits flush against the scaled widget's right edge. */
    margin-right: -60px;
    margin-bottom: -5px;
}

/* Overflow action menu (used by the Export trigger inside
 * .result-actions). Holds Print + Download JSON behind a single
 * disclosure trigger.
 *
 * Positioning: the panel is absolutely positioned and anchored to
 * the trigger's right edge on viewports wide enough that the strip
 * stays on a single line. On narrow viewports the strip wraps and
 * the trigger may sit far from the viewport's right edge, so the
 * mobile media query below switches the anchor to the trigger's left
 * edge to keep the panel in-frame.
 *
 * ARIA disclosure pattern: trigger reports aria-haspopup="menu" +
 * aria-expanded; panel uses role="menu" with role="menuitem"
 * children. Handlers (toggle, focus management, keyboard nav,
 * click-outside-close) live in the page's nonce'd inline script in
 * templates/results.html. */
.action-menu {
    position: relative;
    display: inline-block;
}

/* Open-state visual feedback on the trigger. The border-color + text
 * contrast on their own match the btn-ghost hover state, so without
 * an additional signal the user cannot distinguish "hovering" from
 * "menu is open" once the panel is on screen. The subtle fill below
 * differentiates the two states; combined with the caret rotation,
 * the open state reads clearly. */
.action-menu-trigger[aria-expanded="true"] {
    color: var(--text);
    border-color: var(--border);
    background: rgba(0, 0, 0, 0.04);
}

html.dark .action-menu-trigger[aria-expanded="true"] {
    background: rgba(255, 255, 255, 0.06);
}

.action-menu-caret {
    display: inline-block;
    margin-left: 0.3rem;
    font-size: 0.85em;
    line-height: 1;
    /* vertical-align: middle anchors the small caret to the center
     * of the surrounding text line. Without it, the inline-block
     * sits on the line's baseline (default vertical-align: baseline)
     * and its smaller font-size + line-height: 1 shifts the button's
     * effective baseline downward by a fraction of a pixel,
     * producing a visible height difference between this trigger
     * and other buttons in the same flex row. */
    vertical-align: middle;
    transition: transform 0.15s ease;
}

.action-menu-trigger[aria-expanded="true"] .action-menu-caret {
    transform: rotate(180deg);
}

.action-menu-panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 50;
    min-width: 240px;
    padding: 0.4rem 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
}

.action-menu-panel[hidden] {
    display: none;
}

.action-menu-item {
    display: block;
    width: 100%;
    padding: 0.6rem 1rem;
    background: none;
    border: none;
    color: var(--text);
    font-family: var(--font);
    font-size: 0.95rem;
    line-height: 1.4;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    box-sizing: border-box;
    /* Match buttons' transition register so hover state changes feel
     * consistent with the rest of the row. */
    transition: background 0.12s ease, color 0.12s ease;
}

.action-menu-item:hover,
.action-menu-item:focus-visible {
    background: rgba(0, 0, 0, 0.05);
    color: var(--text);
    outline: none;
}

html.dark .action-menu-panel {
    background: var(--surface);
    border-color: var(--border);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}

html.dark .action-menu-item:hover,
html.dark .action-menu-item:focus-visible {
    background: rgba(255, 255, 255, 0.06);
}

/* Narrow viewport: when the action row wraps and the trigger may sit
 * far from the right edge, anchor the panel to the trigger's left
 * edge instead so it extends rightward into the viewport rather than
 * leftward off-screen. 480px matches the row's natural wrap point on
 * the runner view (Save & share + Compare + the strip fit cleanly
 * above this width but stack below it). */
@media (max-width: 480px) {
    .action-menu-panel {
        right: auto;
        left: 0;
    }
}

/* ─── Tactile button polish ─── */

/* Default .btn hover used to be opacity-fade, which reads as "the
 * button is fading" rather than "responding." Replaced with a
 * subtle lift (translateY -1px) plus a soft shadow, eased. The
 * active state presses the lift back to baseline and removes the
 * shadow, so click-down feels tactile. Applied to every .btn
 * variant (primary, secondary, ghost) so the entire button system
 * reads consistently.
 *
 * Reduced-motion users see no motion: prefers-reduced-motion media
 * query at the bottom of this section disables the transform but
 * keeps the shadow change so the affordance is still visible.
 *
 * The default .btn block above (around line 1020) sets
 * `transition: opacity 0.15s, background 0.15s, transform 0.1s`;
 * we extend it here without touching that base rule so existing
 * .btn-* variants and disabled states keep their existing timing. */
.btn:not(:disabled):not([disabled]):hover {
    opacity: 1;
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

.btn:not(:disabled):not([disabled]):active {
    transform: translateY(0) scale(0.98);
    box-shadow: none;
}

html.dark .btn:not(:disabled):not([disabled]):hover {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

@media (prefers-reduced-motion: reduce) {
    .btn:not(:disabled):not([disabled]):hover,
    .btn:not(:disabled):not([disabled]):active {
        transform: none;
    }
}

/* ─── Save state visualizations ─── */

/* In-flight saving state: pulses the button's opacity gently while
 * the /api/save fetch is running. Without this the click → resolve
 * window (typically 100-500ms, can be longer on slow networks) is a
 * silent gap during which the button just shows "Saving..." text
 * with no visual feedback that something is happening. The pulse
 * ties the disabled-state opacity (0.55, set by the .btn[disabled]
 * rule when JS sets btn.disabled = true on click) into a slow
 * oscillation so the button reads as "actively working" rather
 * than "stuck."
 *
 * Animation duration 1.2s matches a typical comfortable breathing
 * rhythm; faster pulses read as alarmed, slower as inert. The
 * range stays inside disabled territory (0.55 to 0.85) so the
 * button does not visually toggle between disabled and enabled
 * states. JS adds .btn-saving on click and either removes it on
 * error path or lets rewireAsCopyButton clone it away on success
 * (the clone does not carry the class, so the pulse stops). */
@keyframes btn-saving-pulse {
    0%, 100% { opacity: 0.55; }
    50%      { opacity: 0.85; }
}

.btn.btn-saving,
.btn.btn-saving:disabled,
.btn.btn-saving[disabled] {
    animation: btn-saving-pulse 1.2s ease-in-out infinite;
    cursor: wait;
}

/* When the Save button is rewired to a Copy button (after a
 * successful save), it briefly carries .save-btn-success which
 * paints a soft green pulse to mark the moment. Without this the
 * transition reads as a label-swap; with it, the save action feels
 * like an event that happened.
 *
 * The pulse fires once via CSS animation; JS adds the class on
 * save-success, the animation runs, and the class is removed by
 * animationend so subsequent clicks (Copy → "Copied" → revert)
 * do not retrigger the pulse. */
@keyframes save-success-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5); }
    50%  { box-shadow: 0 0 0 8px rgba(16, 185, 129, 0.0); }
    100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.0); }
}

.save-btn-success {
    animation: save-success-pulse 0.7s ease-out;
}

/* ─── MCP card save-state reaction ─── */

/* After a successful save, the MCP upgrade card briefly highlights
 * to signal that the user's just-created artifact has a natural
 * next step (install Frame Check inside their AI to read every
 * response with the same lens). The highlight is subtle: a soft
 * border-color shift + box-shadow pulse, animated once.
 *
 * The handoff is the design intent: the user invested in the
 * analysis (saved it), and the conversion CTA reacts to that
 * investment instead of sitting passively below.
 *
 * Animation fires once via JS adding .mcp-upgrade-card-saved-pulse;
 * the class is left on for ~1.5s then removed by JS so the card
 * settles back to its resting state. */
@keyframes mcp-saved-pulse {
    0%   {
        border-color: var(--blue-border);
        box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.0);
    }
    35%  {
        border-color: var(--blue);
        box-shadow: 0 0 0 6px rgba(59, 130, 246, 0.12);
    }
    100% {
        border-color: var(--blue-border);
        box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.0);
    }
}

.mcp-upgrade-card-saved-pulse {
    animation: mcp-saved-pulse 1.4s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .btn.btn-saving,
    .save-btn-success,
    .mcp-upgrade-card-saved-pulse {
        animation: none;
    }
}

/* Apply-this-frame widget on each library entry.
   The catalog-as-tool idea in concrete form: each library entry doubles
   as a working tool for its frame. The widget lets a reader test
   whether THIS specific frame fires on text they care about,
   instead of running the full analyzer and hoping the frame shows
   up. Visually echoes the frame-check-hero on the result page
   (blue palette + left border) so the same product identity reads
   on both surfaces. */
.apply-frame {
    margin-top: var(--space-2xl);
    padding: var(--space-lg);
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-left: 5px solid var(--blue);
    border-radius: var(--radius-lg);
}

.apply-frame-h {
    font-size: 1.1rem;
    font-weight: 700;
    margin: 0 0 0.5rem 0;
    color: var(--text-1);
    line-height: 1.3;
}

.apply-frame-intro {
    font-size: 0.9rem;
    color: var(--text-2);
    margin: 0 0 var(--space-md) 0;
    line-height: 1.55;
}

.apply-frame-textarea {
    width: 100%;
    box-sizing: border-box;
    font-family: var(--mono);
    font-size: 0.85rem;
    line-height: 1.5;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    resize: vertical;
    min-height: 90px;
}

.apply-frame-textarea:focus {
    outline: 2px solid var(--blue);
    outline-offset: 1px;
    border-color: var(--blue);
}

.apply-frame-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.6rem;
    flex-wrap: wrap;
}

.apply-frame-status {
    font-size: 0.85rem;
    color: var(--text-3);
}

.apply-frame-result {
    margin-top: var(--space-md);
}

/* Detected: positive verdict, green palette. Mirrors the
   verified-claim styling pattern used on result.html sn-cards so
   the affirmative signal reads consistently across surfaces. */
.apply-frame-yes {
    padding: var(--space-md);
    background: var(--green-bg);
    border: 1px solid var(--green-border);
    border-radius: var(--radius-sm);
}

/* Not detected: neutral verdict, surface palette. Not red, because
   not-detected is not failure: it just means the text does not
   match THIS frame's structural signature. The other_frames hint
   below turns the negative into a positive (other frames found). */
.apply-frame-no {
    padding: var(--space-md);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.apply-frame-badge {
    display: inline-block;
    padding: 0.2rem 0.55rem;
    border-radius: 3px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    margin-bottom: 0.5rem;
}

.apply-frame-badge-yes {
    background: var(--green);
    color: var(--surface);
}

.apply-frame-badge-no {
    background: var(--text-3);
    color: var(--surface);
}

.apply-frame-signal,
.apply-frame-question,
.apply-frame-other {
    margin: 0.5rem 0;
    font-size: 0.9rem;
    line-height: 1.55;
    color: var(--text-1);
}

.apply-frame-error {
    padding: var(--space-sm) var(--space-md);
    background: var(--orange-bg);
    border-left: 3px solid var(--orange);
    border-radius: var(--radius-sm);
    color: var(--text-1);
    font-size: 0.9rem;
    margin: 0;
    line-height: 1.5;
}

/* Save-pending hint. Sits below the result-actions row when the
   Save button is disabled because /api/ai-interpret is still in
   flight. The hint explains the gating so the disabled state reads
   as "we are protecting your snapshot quality" rather than "the
   button is broken." JS hides this once AI lands (success, error,
   or daily-limit). */
.save-pending-hint {
    margin: 0.5rem 0 0 0;
    font-size: 0.8rem;
    color: var(--text-3);
    line-height: 1.5;
    font-style: italic;
}

/* /mcp install path. The install steps are top-of-fold because the
   MCP CTA on the result page sends conversion-intent traffic here.
   Each step has a heading, an optional explainer note, and a
   command block with a Copy button. Visual cadence: H3 numbered
   step heading -> note -> command block -> next step. The Copy
   button on every command removes the friction of select-and-copy
   that previously broke the install flow on mobile and dampened
   conversion on desktop. */

.install-overview {
    color: var(--text-2);
    font-size: 0.95rem;
    line-height: 1.6;
    margin: 0 0 var(--space-md) 0;
}

.install-step-h {
    margin-top: var(--space-xl);
    margin-bottom: var(--space-sm);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-1);
}

.install-client-h {
    margin-top: var(--space-md);
    margin-bottom: 0.4rem;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-1);
}

.install-step-note {
    color: var(--text-2);
    font-size: 0.88rem;
    line-height: 1.6;
    margin: 0 0 0.5rem 0;
}

/* PII / credential intake notice on result page.
 * Sits directly under the trust banner. Yellow caveat treatment so
 * the user notices it at the verdict surface; copy is informational
 * (Frame Check does not block on PII) so the styling avoids the
 * stronger error-banner red treatment. The icon character is the
 * Unicode warning sign rendered via &#9888; in the template.
 * Adapts to dark mode via the same accent-warn token used by
 * .mcp-distribution-note. */
.pii-intake-notice {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 0.6rem 0 1rem 0;
    padding: 0.55rem 0.85rem;
    background: var(--bg-warn, #fffbeb);
    border-left: 3px solid var(--accent-warn, #f59e0b);
    border-radius: 0 4px 4px 0;
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--text-1);
}
.pii-intake-icon {
    color: var(--accent-warn, #f59e0b);
    font-weight: 700;
    flex-shrink: 0;
}
.pii-intake-link {
    color: var(--blue, #2563eb);
    text-decoration: underline;
    text-underline-offset: 2px;
    margin-left: 0.2rem;
}
html.dark .pii-intake-notice {
    background: rgba(245, 158, 11, 0.08);
    border-left-color: #f59e0b;
}

/* Frame Mirror in-flow confirmation under the trust banner.
 * Single muted line that appears only when the user has opted in to
 * Mirror (cookie present). Visually quiet so it does not compete
 * with the verdict-headline above; uses .mirror- prefix to scope
 * away from any future page-wide ".inflow" classes. Adapts to dark
 * mode via the same var() tokens the rest of the page uses. */
.mirror-inflow-note {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin: 0.6rem 0 1rem 0;
    padding: 0.45rem 0.75rem;
    background: var(--surface-2, #f5f5f5);
    border-left: 3px solid var(--accent-success, #10b981);
    border-radius: 0 4px 4px 0;
    font-size: 0.86rem;
    color: var(--text-2, #595959);
}
.mirror-inflow-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0;
}
.mirror-inflow-note a {
    color: var(--blue, #2563eb);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.mirror-inflow-icon {
    color: var(--accent-success, #10b981);
    font-weight: 700;
}
.mirror-inflow-sep {
    color: var(--text-4, #9ca3af);
    user-select: none;
}
.mirror-inflow-privacy {
    margin: 0;
    font-size: 0.72rem;
    color: var(--text-3, #6b7280);
}
html.dark .mirror-inflow-note {
    background: rgba(16, 185, 129, 0.06);
    border-left-color: #10b981;
    color: var(--text-2);
}

/* Distribution-state honesty banner on /mcp.
 * Sits above the install steps and names the current install path
 * (clone-only) versus the planned PyPI release. The styling matches
 * the .about-caveat yellow callout pattern used elsewhere on the
 * site so the visual language for "scope-bounding admission" is
 * consistent across pages. Bordered, modest left accent, slightly
 * raised background; works in both light and dark themes via
 * var() tokens. Replace this banner with a pip-install command
 * block when frame-check-mcp 0.8.0 lands on PyPI per STRATEGY §14. */
.mcp-distribution-note {
    background: var(--bg-warn, #fffbeb);
    border: 1px solid var(--border-warn, #fde68a);
    border-left: 4px solid var(--accent-warn, #f59e0b);
    border-radius: var(--radius-sm);
    padding: 0.75rem 1rem;
    margin: 0 0 var(--space-md) 0;
    font-size: 0.9rem;
    line-height: 1.55;
    color: var(--text-1);
}
.mcp-distribution-note strong {
    color: var(--text-1);
}
.mcp-distribution-note code {
    font-family: var(--mono);
    font-size: 0.85em;
}
html.dark .mcp-distribution-note {
    background: rgba(245, 158, 11, 0.08);
    border-color: rgba(245, 158, 11, 0.35);
    border-left-color: #f59e0b;
}

.install-cmd {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    margin: 0 0 var(--space-md) 0;
}

.install-cmd pre {
    flex: 1;
    margin: 0;
    overflow-x: auto;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.7rem 0.85rem;
    font-size: 0.85rem;
    line-height: 1.5;
}

.install-cmd code {
    font-family: var(--mono);
    background: none;
    padding: 0;
    white-space: pre;
}

.install-copy-btn {
    flex-shrink: 0;
    align-self: flex-start;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-3);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.45rem 0.75rem;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

.install-copy-btn:hover,
.install-copy-btn:focus-visible {
    color: var(--text-1);
    border-color: var(--text-3);
    outline: 2px solid var(--text-3);
    outline-offset: 1px;
}

.install-try-list {
    margin: 0 0 var(--space-md) 0;
    padding-left: 1.2rem;
    color: var(--text-2);
    line-height: 1.7;
}

.install-try-list li {
    margin-bottom: 0.4rem;
}

/* Mobile: the command block needs to stack so the Copy button does
   not crush the pre on narrow viewports. The pre auto-scrolls
   horizontally for long lines (the JSON snippet is the worst
   offender). */
@media (max-width: 600px) {
    .install-cmd {
        flex-direction: column;
        align-items: stretch;
    }
    .install-copy-btn {
        align-self: flex-end;
    }
}

/* MCP install upgrade card. Distribution thesis is MCP-first
   (see STRATEGY.md), so the result page treats MCP as the natural
   next step (read every AI response with the same structural lens),
   not a muted footer hint. Replaces the prior .result-actions-hint
   paragraph (capped at 60ch with no card affordance) which read as
   a footnote despite being a strategic conversion path. The card
   spans the result column, sits below primary actions so it does
   not compete with save/download/print, and pairs the conceptual
   upgrade with a single clear CTA. */
.mcp-upgrade-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-lg);
    margin-top: var(--space-lg);
    padding: var(--space-lg);
    /* Use the same blue-tinted palette as the frame-check-hero at
       the top of the page. Visually bookends the result: the hero
       is the analysis (Frame Check on this document), the MCP card
       is the upgrade (Frame Check on every AI response). The earlier
       background var(--surface-2, rgba(0,0,0,0.03)) was buggy: that
       variable is undefined and the rgba fallback rendered as
       invisible black tint over the dark-mode page background, so
       dark-mode users saw a card with no visible bounding box. */
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-left: 5px solid var(--blue);
    border-radius: var(--radius-lg);
    flex-wrap: wrap;
}

.mcp-upgrade-body {
    flex: 1 1 320px;
    min-width: 0;
}

.mcp-upgrade-title {
    font-size: 1rem;
    font-weight: 600;
    margin: 0 0 0.4rem 0;
    color: var(--text);
    line-height: 1.3;
}

.mcp-upgrade-text {
    font-size: 0.85rem;
    color: var(--text-2);
    margin: 0;
    line-height: 1.55;
}

.mcp-upgrade-cta {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* ── About page ── */

.about h1 {
    font-size: 1.5rem;
    font-weight: 700;
    margin-bottom: var(--space-md);
    letter-spacing: -0.02em;
}

.about-lead {
    font-size: 1rem;
    color: var(--text-2);
    line-height: 1.7;
    margin-bottom: var(--space-2xl);
    max-width: 640px;
}

/* Banner variant for draft / pre-ratification surfaces.
 * Used on /terms while the page awaits counsel review.
 * Reuses the trust-banner moderate-level palette so the
 * visual signal is consistent across surfaces. */
.about-lead-draft {
    border-left: 3px solid var(--yellow);
    background: var(--yellow-bg);
    padding: 1rem 1.25rem;
    border-radius: var(--radius-sm);
}

.about h2 {
    font-size: 1.05rem;
    font-weight: 700;
    margin-top: var(--space-2xl);
    margin-bottom: 0.6rem;
    letter-spacing: -0.01em;
}

.about p {
    font-size: 0.88rem;
    color: var(--text-2);
    line-height: 1.7;
    margin-bottom: var(--space-md);
}

.about-methods {
    display: grid;
    gap: var(--space-md);
    margin: 1rem 0 1.5rem;
}

.about-method {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem 1.15rem;
}

.about-method h3 {
    font-size: 0.9rem;
    font-weight: 700;
    margin-bottom: 0.35rem;
}

.about-method p {
    font-size: 0.82rem;
    margin-bottom: 0;
}

.about-principles {
    list-style: none;
    padding: 0;
    margin: 0.75rem 0 1.5rem;
}

.about-principles li {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.65;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border-light);
}

.about-principles li:last-child { border-bottom: none; }

.about-principles strong {
    color: var(--text);
}

.about ul:not(.about-principles) {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.65;
    padding-left: 1.25rem;
    margin-bottom: var(--space-lg);
}

.about ul:not(.about-principles) li {
    margin-bottom: 0.3rem;
}

.about-cta {
    display: flex;
    gap: 0.6rem;
    margin-top: var(--space-2xl);
    padding-top: var(--space-xl);
    border-top: 1px solid var(--border);
}

/* ── About: caveat card (yellow warning state for "out of scope") ──
   Replaces a flat paragraph with a visually distinct card so the
   constraint cannot be missed. The constraint is the most-misread
   piece of about-page content (users skim past it and assume Frame
   Check measures things it doesn't), so it earns its visual weight. */

.about-caveat {
    background: var(--yellow-bg);
    border: 1px solid var(--yellow-border);
    border-left: 4px solid var(--yellow);
    border-radius: var(--radius);
    padding: 0.85rem 1.1rem 0.95rem;
    margin: 0.75rem 0 1.5rem;
}

.about-caveat-label {
    display: inline-block;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--yellow);
    margin-bottom: 0.35rem;
}

.about-caveat p {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.65;
    margin: 0;
}

/* ── About: construct blindspots (definition list) ──
   Definition-list format because each blindspot is a named failure
   mode with an explanation. The visual weight of <dt> creates the
   scanable list; <dd> carries the worked example. Same compounding
   principle as the privacy ledger: the specificity IS the proof,
   not a claim that the tool is honest. */

.about-lead-narrow {
    font-size: 0.92rem;
    color: var(--text-2);
    line-height: 1.65;
    max-width: 640px;
    margin: 0 0 1rem;
}

.about-blindspots {
    margin: 0.75rem 0 1.5rem;
    padding: 0;
    max-width: 720px;
}

.about-blindspots dt {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--text);
    margin-top: 1rem;
    line-height: 1.45;
}

.about-blindspots dt:first-of-type {
    margin-top: 0;
}

.about-blindspots dd {
    font-size: 0.86rem;
    color: var(--text-2);
    line-height: 1.65;
    margin: 0.35rem 0 0;
    padding: 0;
}

/* ── About: privacy ledger (side-by-side IS / IS NOT contrast) ──
   The two-column layout makes the asymmetry immediate. The recorded
   column reads green (structural derivatives that compound
   over years); the never-recorded column reads gray (a deliberate
   absence). The contrast IS the proof, not a paragraph saying so. */

.privacy-ledger {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-lg);
    margin: var(--space-lg) 0 var(--space-md);
}

.privacy-col {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1rem 1.15rem 1.1rem;
}

.privacy-col-recorded {
    border-left: 4px solid var(--green);
}

.privacy-col-never {
    /* var(--gray) is #555 in light mode and #999 in dark mode,
       which gives the "absent" column a visible accent in both
       themes. var(--text-4) was #aaa light / #666 dark, and the
       dark variant disappeared against the dark surface. Both
       columns are informationally important (the recorded one
       is the substantive signal, the never column is the privacy guarantee),
       so they should have equal visual weight. */
    border-left: 4px solid var(--gray);
}

.privacy-col-head {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-bottom: 0.3rem;
}

.privacy-col-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 50%;
    font-family: var(--mono);
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1;
}

.privacy-col-recorded .privacy-col-icon {
    background: var(--green-bg);
    color: var(--green);
    border: 1px solid var(--green-border);
}

.privacy-col-never .privacy-col-icon {
    background: var(--gray-bg);
    color: var(--gray);
    border: 1px solid var(--gray-border);
}

.privacy-col-title {
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--text);
    margin: 0;
    letter-spacing: -0.005em;
}

.privacy-col-sub {
    font-size: 0.74rem;
    color: var(--text-3);
    line-height: 1.5;
    margin-bottom: 0.7rem;
}

.privacy-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.privacy-list li {
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.5;
    padding-left: 0.9rem;
    position: relative;
}

.privacy-col-recorded .privacy-list li::before {
    content: "✓";
    position: absolute;
    left: 0;
    top: 0;
    color: var(--green);
    font-weight: 700;
    font-size: 0.78rem;
}

.privacy-col-never .privacy-list li::before {
    content: "×";
    position: absolute;
    left: 0;
    top: 0;
    color: var(--text-4);
    font-weight: 700;
    font-size: 0.85rem;
    line-height: 1.4;
}

.about p.privacy-footnote {
    font-size: 0.78rem;
    color: var(--text-3);
    line-height: 1.6;
    margin-top: 0.85rem;
    margin-bottom: var(--space-lg);
    font-style: italic;
}

.about p.privacy-footnote a {
    color: var(--blue);
    text-decoration: none;
    border-bottom: 1px solid var(--blue-border);
    transition: border-color 0.15s;
}

.about p.privacy-footnote a:hover {
    border-bottom-color: var(--blue);
}

/* Privacy ledger collapses to one column on narrow screens */
@media (max-width: 700px) {
    .privacy-ledger {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }
}

/* ── Error page ──
   Reuses the trust-banner card pattern from results.html so the
   error feels like a measurement of failure rather than a generic
   web app crash. Two visual states (404 vs 500) sharing one
   structural shell. */

.error-card-wrap {
    margin: 1rem 0 2.5rem 0;
}

.error-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 5px solid var(--gray);
    border-radius: var(--radius-lg);
    padding: 1.5rem 1.75rem;
}

.error-card-404 {
    border-left-color: var(--gray);
    background: var(--gray-bg);
}

.error-card-500 {
    border-left-color: var(--red);
    background: var(--red-bg);
}

.error-card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-md);
    margin-bottom: 0.6rem;
}

.error-card-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--gray);
}

.error-card-500 .error-card-label {
    color: var(--red);
}

.error-card-code {
    font-family: var(--mono);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text-3);
    letter-spacing: 0.02em;
}

.error-card-500 .error-card-code {
    color: var(--red);
    opacity: 0.7;
}

.error-card-headline {
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--text);
    margin-bottom: var(--space-sm);
    letter-spacing: -0.01em;
}

.error-card-detail {
    font-size: 0.88rem;
    color: var(--text-2);
    line-height: 1.6;
    margin-bottom: 1.25rem;
    max-width: 56ch;
}

.error-card-next {
    margin-top: 1.25rem;
    padding-top: var(--space-lg);
    border-top: 1px solid var(--border-light);
}

.error-card-next-label {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    margin-bottom: var(--space-sm);
}

.error-next-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}

.error-next-list li {
    font-size: 0.88rem;
    color: var(--text);
    padding-left: 1.1rem;
    position: relative;
    line-height: 1.5;
}

.error-next-list li::before {
    content: "→";
    position: absolute;
    left: 0;
    top: 0;
    color: var(--blue);
    font-weight: 600;
}

.error-next-list a {
    color: var(--text);
    text-decoration: none;
    font-weight: 600;
    border-bottom: 1px solid var(--border);
    transition: border-color 0.15s;
}

.error-next-list a:hover {
    border-bottom-color: var(--blue);
    color: var(--blue);
}

.error-next-hint {
    display: block;
    font-size: 0.78rem;
    color: var(--text-3);
    font-weight: 400;
    line-height: 1.45;
    margin-top: 0.1rem;
}

.error-card-footer {
    margin-top: 1.25rem;
    padding-top: 0.85rem;
    border-top: 1px dashed var(--border-light);
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-sm);
}

.error-card-footer-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
}

.error-card-footer-value {
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--text-2);
    background: var(--surface);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-sm);
    padding: 0.15rem 0.4rem;
    user-select: all;
}

html.dark .error-card-404 {
    background: rgba(255, 255, 255, 0.03);
}

html.dark .error-card-500 {
    background: rgba(239, 68, 68, 0.07);
}

html.dark .error-card-footer-value {
    background: var(--bg);
    border-color: var(--border);
}

/* ── Footer ── */

footer {
    border-top: 1px solid var(--border-light);
    background: var(--surface);
    padding: 1.25rem 1.5rem;
    margin-top: 3rem;
}

.footer-inner {
    max-width: var(--max-w);
    margin: 0 auto;
}

.footer-top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-lg);
    margin-bottom: var(--space-sm);
    flex-wrap: wrap;
}

.footer-brand {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
}

.footer-name {
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--text-3);
}

.footer-by {
    font-size: 0.72rem;
    color: var(--text-4);
}

.footer-brand-link {
    color: var(--text-3);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color 0.15s, border-color 0.15s;
}

.footer-brand-link:hover {
    color: var(--text);
    border-bottom-color: var(--text-4);
}

.footer-links {
    display: flex;
    gap: 1.1rem;
    flex-wrap: wrap;
}

.footer-links a {
    font-size: 0.72rem;
    color: var(--text-3);
    text-decoration: none;
    transition: color 0.15s;
}

.footer-links a:hover {
    color: var(--text);
}

.footer-method {
    font-size: 0.72rem;
    color: var(--text-4);
    line-height: 1.5;
    margin: 0;
}

footer .caveat {
    font-size: 0.72rem;
    color: var(--text-4);
    line-height: 1.5;
    margin-top: 0.2rem;
}

/* Named-authorship line: small, muted, bottom of every page. The
   author attribution is the named-authorship line;
   showing it globally is cheap and compounds trust across every
   page a reader lands on. */
.footer-author {
    font-size: 0.72rem;
    color: var(--text-4);
    line-height: 1.5;
    margin: 0.5rem 0 0 0;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border-light);
}

.footer-author a {
    color: var(--text-3);
    text-decoration: none;
}

.footer-author a:hover {
    color: var(--text-2);
    text-decoration: underline;
}

/* ── Examples page ── */

.example-gallery {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xl);
}

.example-entry {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.25rem;
    background: var(--surface);
}

.example-meta {
    margin-bottom: var(--space-md);
}

.example-meta h2 {
    font-size: 1.05rem;
    margin-bottom: 0.2rem;
}

.example-desc {
    font-size: 0.82rem;
    color: var(--text-2);
}

.example-tags {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.4rem;
}

/* Example hook: a one-sentence "what this demonstrates" lead.
   Sits between the example metadata and the trust banner so the user
   sees the takeaway before reading the analysis. Treatment matches
   the framing portrait card on the results page so the same design
   language signals "synthesis" across pages. */
.example-hook {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--blue);
    border-radius: var(--radius-lg);
    padding: 0.75rem 1rem;
    margin-bottom: var(--space-md);
}

html.dark .example-hook {
    background: var(--bg);
}

.example-hook-label {
    display: block;
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--blue);
    margin-bottom: 0.3rem;
}

.example-hook-text {
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--text);
    margin: 0;
}

.etag {
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    color: var(--text-3);
}

.etag-source { background: var(--green-bg); color: var(--green); border-color: var(--green-border); }
.etag-nosource { background: var(--gray-bg); color: var(--gray); border-color: var(--gray-border); }

.example-claims-summary {
    font-size: 0.8rem;
    color: var(--text-2);
    margin-top: var(--space-sm);
}

.example-framing-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
    margin: 0.5rem 0;
}

.example-framing-stat {
    font-size: 0.72rem;
    font-family: var(--mono);
    color: var(--text-3);
    background: var(--gray-bg);
    padding: 0.15rem 0.45rem;
    border-radius: var(--radius-sm);
}

.etag-voice {
    background: var(--blue-bg);
    color: var(--blue);
    border-color: var(--blue-border);
}

/* Example annotated document wrap with bottom fade.
   Caps the height of the preview and adds a fade-out gradient
   over the bottom 60px so users see "more below, scroll to read"
   without a hard crop. The previous treatment used inline
   max-height: 300px which hard-cropped with no affordance. */
.example-doc-wrap {
    position: relative;
    margin-top: var(--space-sm);
}

.example-doc-wrap .annotated-document {
    max-height: 380px;
    overflow-y: auto;
}

.example-doc-fade {
    /* Hidden by default. The JS overflow detector adds
       .is-overflowing to the wrap when the annotated document
       actually exceeds the max-height, and the rule below
       reveals the fade. Without this, every example shows the
       fade gradient over the bottom of valid content even when
       the content fits and there is nothing to scroll to. */
    position: absolute;
    left: 1px;
    right: 1px;
    bottom: 1px;
    height: 60px;
    pointer-events: none;
    background: linear-gradient(
        to bottom,
        rgba(255, 255, 255, 0) 0%,
        var(--surface) 100%
    );
    border-bottom-left-radius: var(--radius);
    border-bottom-right-radius: var(--radius);
    display: none;
}

.example-doc-wrap.is-overflowing .example-doc-fade {
    display: block;
}

html.dark .example-doc-fade {
    background: linear-gradient(
        to bottom,
        rgba(22, 22, 22, 0) 0%,
        var(--surface) 100%
    );
}

.example-actions {
    margin-top: var(--space-md);
}

.example-footer-cta {
    text-align: center;
    margin-top: var(--space-2xl);
    padding: var(--space-xl);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
}

.example-footer-cta p {
    font-size: 0.88rem;
    color: var(--text-2);
    margin-bottom: var(--space-md);
}

/* ── Compare Page ── */

.compare-input {
    width: 100%;
    padding: 0.75rem 1rem;
    border: 2px solid var(--border);
    border-radius: var(--radius-lg);
    font-size: 0.95rem;
    font-family: var(--font);
    background: var(--surface);
    color: var(--text);
    transition: border-color 0.2s;
}

.compare-input:focus {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.1);
}

.compare-banner {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.25rem 1.5rem;
    margin-bottom: var(--space-lg);
}

.compare-banner h2 { font-size: 1.1rem; margin-bottom: 0.5rem; }

/* Documents grid: two textareas side by side on desktop,
   stacked on mobile. */
.docs-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-md);
}

.doc-field { display: flex; flex-direction: column; gap: 0.4rem; }

.doc-field .field-label {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
}

@media (max-width: 900px) {
    .docs-grid { grid-template-columns: 1fr; }
}

/* Progressive comparison: status text and progress bar inside the
   compare banner. The wrap is hidden when the stream completes. */
.compare-progress-wrap {
    margin-top: var(--space-sm);
}

.compare-status {
    font-size: 0.82rem;
    color: var(--text-2);
    margin-bottom: 0.4rem;
}

.compare-progress {
    height: 4px;
    background: var(--gray-bg);
    border-radius: 2px;
    overflow: hidden;
}

.compare-progress-bar {
    height: 100%;
    width: 0%;
    background: var(--blue);
    transition: width 0.4s ease;
    border-radius: 2px;
}

.compare-progress-bar.error {
    background: var(--red);
}

/* Model card skeleton (shown while waiting for response) */
.model-skeleton {
    padding: 1.5rem 1rem;
    text-align: center;
    background: var(--bg);
    border-radius: var(--radius);
    margin-top: var(--space-sm);
}

.compare-stats { display: flex; gap: 0.5rem; flex-wrap: wrap; }

.cstat {
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.2rem 0.6rem;
    border-radius: 4px;
    background: var(--bg);
    color: var(--text-2);
}

.cstat-agree { background: var(--green-bg); color: var(--green); }
.cstat-disagree { background: var(--red-bg); color: var(--red); }

.compare-section { margin-bottom: 1.25rem; }
.compare-section h3 { font-size: 0.9rem; margin-bottom: 0.4rem; }
.compare-note { font-size: 0.78rem; color: var(--text-2); margin-bottom: 0.6rem; }
.compare-note.compare-note-tight { margin-top: 0.3rem; margin-bottom: 0; }

/* ── Voice + Epistemic Tags (compare model cards) ── */

.framing-voice-epist {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    margin-bottom: var(--space-sm);
}

.voice-tag, .epist-tag {
    display: inline-block;
    padding: 0.2rem 0.55rem;
    border-radius: var(--radius-sm);
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: capitalize;
}

.voice-tag-promo {
    background: var(--orange-bg);
    color: #9a3412;
    border: 1px solid var(--orange);
}

.voice-tag-prescriptive {
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #f59e0b;
}

.voice-tag-neutral {
    background: var(--blue-bg);
    color: #1e40af;
    border: 1px solid var(--blue);
}

.epist-high {
    background: var(--green-bg);
    color: #166534;
    border: 1px solid var(--green);
}

.epist-mid {
    background: var(--yellow-bg);
    color: #854d0e;
    border: 1px solid var(--yellow);
}

.epist-low {
    background: var(--orange-bg);
    color: #9a3412;
    border: 1px solid var(--orange);
}

/* Structural framing diff (zero-LLM, always available)
   Legacy prose path: only shown when structural_framing_cards is
   missing (old saved JSON written before the structured refactor).
   New surfaces render the cards inside .framing-structural below. */
.compare-framing-structural {
    font-size: 0.88rem;
    font-weight: 500;
    line-height: 1.55;
    color: var(--text-1);
    margin-bottom: var(--space-sm);
}

/* AI-enhanced framing comparison (Grok, when available) */
.compare-framing-text {
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--text-1);
    margin: 0;
}

/* Structured framing comparison (zero-LLM, always available).
   Replaces the single dense prose paragraph with a structured
   layout: a labelled method tag, an opening headline, a list of
   per-dimension comparison cards (label + side-by-side values +
   reader implication), any unique-omission tags, and a shared
   blind-spots closing with its consequence. The prose form is
   still emitted on the server for backward compatibility with
   saves written before this refactor; the legacy path uses
   .framing-structural-legacy and renders the old paragraph
   inside the same labelled container. */
.framing-structural {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    margin-bottom: 1.2rem;
}
.framing-method-tag {
    display: inline-block;
    align-self: flex-start;
    font-size: 0.72rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--text-3);
    background: var(--surface);
    border: 1px solid var(--border);
    padding: 0.2rem 0.55rem;
    border-radius: 99px;
    font-weight: 600;
}
.framing-method-tag-ai {
    color: #4338ca;
    background: #eef2ff;
    border-color: #c7d2fe;
}
html.dark .framing-method-tag {
    background: var(--surface-2, #1a1d24);
    border-color: var(--border, #2a2f3a);
    color: var(--text-3);
}
html.dark .framing-method-tag-ai {
    color: #a5b4fc;
    background: #1e1b4b;
    border-color: #3730a3;
}
.framing-headline {
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1.5;
    color: var(--text-1);
    margin: 0;
}
.framing-cards {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}
.framing-card {
    display: grid;
    grid-template-columns: minmax(9rem, 11rem) 1fr;
    gap: 0.4rem 1.2rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    align-items: start;
}
html.dark .framing-card {
    background: var(--surface-2, #1a1d24);
    border-color: var(--border, #2a2f3a);
}
.framing-card-label {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--text-3);
    font-weight: 600;
    padding-top: 0.1rem;
}
.framing-card-values {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.3rem 0.6rem;
    font-size: 0.9rem;
    color: var(--text-1);
}
.framing-card-a, .framing-card-b {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    line-height: 1.3;
}
/* Symmetric variant. When a card's a_value equals b_value, the
 * renderer emits one .framing-card-shared in place of the
 * A-vs-B pair. Same column layout as .framing-card-a/b so a
 * mixed list (some symmetric cards, some asymmetric) reads as
 * one cohesive grid. */
.framing-card-shared {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    line-height: 1.3;
}
.framing-card-name {
    font-size: 0.72rem;
    color: var(--text-3);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
}
.framing-card-v {
    font-weight: 600;
}
.framing-card-sep {
    font-size: 0.75rem;
    color: var(--text-3);
    padding: 0 0.1rem;
    font-weight: 400;
    align-self: center;
}
.framing-card-note {
    grid-column: 1 / -1;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-2);
    margin: 0;
}
@media (max-width: 540px) {
    .framing-card {
        grid-template-columns: 1fr;
    }
    .framing-card-note { grid-column: 1; }
}
.framing-uniques {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.framing-unique {
    font-size: 0.85rem;
    color: var(--text-2);
    margin: 0;
    line-height: 1.5;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
}
.framing-unique-tag {
    display: inline-block;
    font-size: 0.78rem;
    padding: 0.1rem 0.5rem;
    border-radius: 99px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text-2);
    font-weight: 500;
}
html.dark .framing-unique-tag {
    background: var(--surface-2, #1a1d24);
    border-color: var(--border, #2a2f3a);
}
.framing-shared-blind {
    padding: 0.7rem 0.9rem;
    border: 1px dashed var(--border);
    border-radius: 6px;
    background: var(--surface);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
html.dark .framing-shared-blind {
    background: var(--surface-2, #1a1d24);
    border-color: var(--border, #2a2f3a);
}
.framing-shared-blind-head {
    font-size: 0.85rem;
    color: var(--text-2);
    margin: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
}
.framing-shared-blind-note {
    font-size: 0.82rem;
    color: var(--text-2);
    margin: 0;
    line-height: 1.5;
}
.framing-ai {
    margin-top: 0.25rem;
    padding-top: 0.8rem;
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Degraded variant: AI narrative could not load (timeout, daily
 * cap, missing key, etc.). Same layout as the success state but
 * the body text reads as a status note rather than insight, so
 * the prose color is muted. The framing-method-tag stays at the
 * same visual register so the reader knows the AI layer was
 * attempted; only the absence is acknowledged. */
.framing-ai-degraded .compare-framing-text {
    color: var(--text-3);
    font-size: 0.85rem;
}

.compare-section-toggle {
    cursor: pointer;
    list-style: none;
}
.compare-section-toggle::-webkit-details-marker { display: none; }
.compare-section-toggle h3 { display: inline; }

.disagree-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: var(--red-bg);
    border: 1px solid var(--red-border);
    border-radius: var(--radius);
    padding: 0.6rem 1rem;
    margin-bottom: 0.4rem;
}

.disagree-values { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.disagree-val { font-family: var(--mono); font-weight: 700; font-size: 0.88rem; }
.disagree-a { color: var(--blue); }
.disagree-b { color: var(--orange); }
.disagree-vs { font-size: 0.7rem; color: var(--text-3); font-weight: 600; }
.disagree-diff { font-size: 0.72rem; color: var(--red); font-weight: 600; }

.disagree-context {
    margin-top: 0.3rem;
    padding-top: 0.3rem;
    border-top: 1px solid var(--border-light);
}
.disagree-ctx {
    font-size: 0.72rem;
    color: var(--text-3);
    line-height: 1.4;
    margin-bottom: 0.15rem;
}
.disagree-ctx strong {
    color: var(--text-2);
}

/* Compare takeaway-questions panel. Inherits the takeaway-questions-
 * panel styling from /check (background, padding, border, etc.) so
 * the two panels read as the same visual class across surfaces.
 * The compare variant adds per-model sub-blocks: each block carries
 * a model name heading + the model's frame entries.
 *
 * The .compare-takeaway-model-block establishes a small inset for
 * each model's frames so the panel does not read as one continuous
 * list when frames are detected on both sides. The h4 model name
 * acts as a subheading; the inner ul re-uses the same
 * .takeaway-questions-list / .takeaway-questions-frame styling
 * /check's panel uses, so per-frame rendering is identical across
 * surfaces. */
.compare-takeaway-questions {
    /* Inherits all base panel styling from .takeaway-questions-panel */
}

/* Action subsection inside the takeaway-questions panel. The
 * outer <section> picks up .takeaway-questions-section's margin-
 * top so the visual separation from the absent-dimensions
 * subsection above matches the gap between Frames and Absent
 * subsections. .compare-takeaway-actions itself carries .fai-
 * takeaway-actions as a second class so the existing flex/gap
 * rules from the mid-page Take-to-LLM row apply unchanged.
 * .compare-takeaway-actions-hint mirrors .fai-takeaway-hint
 * (small, muted prose) but fits the wider panel context. */
.compare-takeaway-actions {
    margin-top: 0.4rem;
}

.compare-takeaway-actions-hint {
    margin: 0.6rem 0 0 0;
    font-size: 0.78rem;
    color: var(--text-3);
    line-height: 1.5;
}

.compare-takeaway-model-block {
    margin-top: var(--space-sm);
}

.compare-takeaway-model-block:first-child {
    margin-top: 0;
}

.compare-takeaway-model-name {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-2);
    margin: 0 0 0.5rem 0;
    padding: 0.2rem 0;
    border-bottom: 1px dashed var(--border-light);
}

/* Comparison verdict hero. The compare-page equivalent of /check's
 * verdict-headline at the top of the result page: a single
 * sentence that gives the reader an at-a-glance read of what the
 * comparison says before they scroll into details. Composed
 * structurally on the server (comparison._compose_compare_verdict)
 * from agreed/diverged counts + shared blind spots; zero LLM cost.
 *
 * Visual treatment mirrors .frame-check-hero (blue-bg + 5px blue
 * left-stripe + radius-lg) so the verdict-at-the-top pattern
 * reads as the same shape on both surfaces. The label eyebrow
 * names the section so the headline does not have to compete
 * with surrounding chrome for identity. The skeleton state holds
 * the slot during the SSE phase so the layout does not shift
 * when the comparison data lands. */
.compare-verdict-hero {
    margin: var(--space-md) 0 var(--space-lg) 0;
    padding: 1.5rem 1.75rem;
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-left: 5px solid var(--blue);
    border-radius: var(--radius-lg);
}

.compare-verdict-headline {
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1.5;
    color: var(--text);
    margin: 0;
    /* Cap line length around 70 characters so the verdict reads
     * like a sentence rather than a banner stretched across a
     * wide column. The .compare-verdict-hero parent already caps
     * at the .results column width (~812px with TOC); this keeps
     * the prose from extending the full width on lighter content
     * (short subjects + small counts). */
    max-width: 60ch;
}

/* Eyebrow line above the verdict headline. Surfaces only when the
 * verdict promoted a frame's library probe question to the h2 and
 * demoted the original "Both responses operate in <Frame>" sentence
 * to a supporting eyebrow. Carries a full sentence (not a short
 * label) so it stays sentence-cased rather than uppercasing the
 * way the short-label eyebrows on /check (.fai-takeaway-label) do;
 * uppercasing a 7-10 word verdict reads as shouty banner copy. */
.compare-verdict-eyebrow {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-3);
    line-height: 1.4;
    margin: 0 0 0.45rem 0;
    max-width: 60ch;
}

/* "Sharpening with AI..." hint that sits right under the hero h2
 * while the async /api/compare-framing call is running. Tells a
 * reader who has already seen the deterministic library probe
 * land in the h2 that the question itself will upgrade once the
 * LLM returns. Hidden by default; the JS handlers reveal on
 * async_sessions and hide on every fetchFramingComparison
 * terminal branch. */
.compare-verdict-sharpening {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0.55rem 0 0.1rem;
    font-size: 0.78rem;
    color: var(--text-3);
    font-style: italic;
}
.compare-verdict-sharpening-spinner {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    background: var(--blue);
    animation: compare-spinner-pulse 1.4s ease-in-out infinite;
    flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
    .compare-verdict-sharpening-spinner {
        animation: none;
        opacity: 0.7;
    }
}

/* No-frame-detected note. Surfaces below the verdict headline
 * when the verdict used the count-fallback path (no frames
 * detected on either side). Quiet visual register, brand-blue
 * link family but smaller / muted; tells the reader Frame Check
 * tried and found nothing rather than leaving them to assume
 * frames are not a feature. Mutually exclusive with the verbatim
 * CTA below by data: verbatim implies shared frames detected. */
.compare-verdict-note {
    margin-top: var(--space-sm);
    font-size: 0.82rem;
    color: var(--text-3);
    line-height: 1.5;
    max-width: 60ch;
}

/* Verbatim CTA below the verdict-hero headline. Surfaces only
 * when the verdict declared verbatim alignment; routes the reader
 * to /check on the shared text (the single-doc analysis surface
 * with V4.2 frame analysis, source verification, and the rest of
 * the features /compare gates off for cost). */
.compare-verdict-cta {
    margin-top: var(--space-sm);
}

.compare-verdict-cta-link {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--blue);
    text-decoration: none;
}

.compare-verdict-cta-link:hover,
.compare-verdict-cta-link:focus-visible {
    text-decoration: underline;
}

.compare-verdict-skeleton {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text-3);
    font-size: 0.95rem;
    line-height: 1.5;
}

.compare-verdict-spinner {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    background: var(--blue);
    animation: compare-spinner-pulse 1.4s ease-in-out infinite;
    flex-shrink: 0;
}

/* Small pulsing-dot keyframe shared by the verdict spinner. The
 * keyframe was originally co-located with the
 * compare-ai-loading thin pulse line (now retired); the verdict
 * spinner kept depending on it after the loader was removed, so
 * the keyframe lives standalone now under a name that no longer
 * implies the retired loader. */
@keyframes compare-spinner-pulse {
    0%, 100% { opacity: 0.35; transform: scale(0.85); }
    50%      { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .compare-verdict-spinner {
        animation: none;
        opacity: 0.7;
    }
}

/* AI-narrative subsection on /compare. The narrative was
 * relocated up into the takeaway-questions panel so it sits
 * beside the structural takeaway content; the loader is now the
 * standard ai-skeleton shape (matches /check) inside the
 * compare-takeaway-ai-section heading. The prior thin pulse line
 * (.compare-ai-loading) was retired because the page now uses a
 * single skeleton + content swap, the same pattern /check uses,
 * so a separate intermediate loading line was redundant. The
 * tag chip on the section heading carries the AI-narrative label;
 * no extra row needed inside the body. */
.compare-takeaway-ai-section .compare-takeaway-ai-tag {
    margin-left: 0.5rem;
    vertical-align: middle;
}

.agreed-tags { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.agreed-tag {
    font-family: var(--mono);
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.15rem 0.5rem;
    background: var(--green-bg);
    color: var(--green);
    border-radius: 4px;
}

/* Agreed-numbers context cards. Replaces the bare-tag treatment
 * (just the value as a green chip with no context for what the
 * number means). Each card shows the agreed value plus the
 * sentence each model used when citing it, so the reader sees
 * "Both models cited $383B in: 'Apple reported total revenue
 * of $383 billion' / 'Apple's FY2024 revenue was $383 billion'"
 * instead of just "$383".
 *
 * Card structure: a green-tinted left-stripe (the agreed-tier
 * brand color, matching the .agreed-tag fill) at the start of
 * each entry, then the value as a monospace badge, then the
 * per-model contexts as labelled sentences. Stacks vertically;
 * truncation handled by the +N more note in the JS / template
 * after 12 cards. */
.agreed-cards {
    list-style: none;
    margin: 0.5rem 0 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.agreed-card {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0.55rem 0.8rem;
    background: var(--surface);
    border: 1px solid var(--border-light);
    border-left: 3px solid var(--green);
    border-radius: var(--radius-sm);
    line-height: 1.4;
}

.agreed-card-value {
    flex-shrink: 0;
    font-family: var(--mono);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--green);
    padding: 0.05rem 0.45rem;
    background: var(--green-bg);
    border-radius: 3px;
    line-height: 1.4;
}

.agreed-card-contexts {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.agreed-card-context {
    margin: 0;
    font-size: 0.82rem;
    color: var(--text-2);
    line-height: 1.5;
}

.agreed-card-model {
    font-weight: 600;
    color: var(--text);
    margin-right: 0.25rem;
}

.agreed-card-sentence {
    color: var(--text-2);
}

.agreed-more-note {
    margin: 0.6rem 0 0 0;
    font-size: 0.78rem;
    color: var(--text-3);
    font-style: italic;
}

/* Progressive-disclosure for agreed-numbers cards. Earlier
 * iteration capped at 12 cards with a "+N more (truncated)"
 * note; the operator pushed back that even 12 was noise on
 * comparisons where the two responses share many numbers. New
 * model: render all cards, hide the 6th and beyond by default,
 * surface a button that toggles a single class on the parent
 * <ul> to reveal them. CSS :nth-child counts <li> siblings of
 * .agreed-card so the rule applies cleanly to whatever count
 * the SSE/Jinja render produces; the button lives outside the
 * <ul> so it stays visible when the cards collapse.
 *
 * display: list-item on the override restores the default <li>
 * box; using `display: ` (browser default) without a value
 * would be invalid, and `display: block` would lose any future
 * list-marker treatment. */
.agreed-cards .agreed-card:nth-child(n+6) {
    display: none;
}
.agreed-cards.is-expanded .agreed-card:nth-child(n+6) {
    display: list-item;
}

.agreed-show-all-btn {
    margin-top: 0.7rem;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.4rem 0.85rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--blue);
    background: var(--surface);
    border: 1px solid var(--blue-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-decoration: none;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

.agreed-show-all-btn:hover,
.agreed-show-all-btn:focus-visible {
    background: var(--blue-bg);
    border-color: var(--blue);
}

@media (max-width: 540px) {
    .agreed-card {
        flex-direction: column;
        align-items: stretch;
    }
    .agreed-card-value {
        align-self: flex-start;
    }
}

/* Blind spots */
.blind-spot-tags { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 0.4rem; }
.blind-spot-tag {
    font-size: 0.82rem;
    font-weight: 600;
    padding: 0.2rem 0.6rem;
    background: var(--orange-bg);
    color: var(--orange);
    border: 1px solid var(--orange-border);
    border-radius: 4px;
}
.blind-spot-unique { margin-top: 0.3rem; }
.blind-spot-note {
    display: block;
    font-size: 0.75rem;
    color: var(--text-3);
    margin-bottom: 0.15rem;
}

/* Per-model framing in comparison cards */
.model-framing {
    border-top: 1px solid var(--border-light);
    padding-top: 0.6rem;
    margin-bottom: 0.6rem;
}
.model-framing .framing-temporal-bars { margin-top: 0.5rem; }

/* V4.2 disclosure note above the compare-models grid. Sets
   reader expectation for what the compare flow does and does
   not provide. Visually subdued: this is a calibration note,
   not a primary action surface. */
/* Footnote-style note above the per-doc model cards. Operator's
 * call (2026-05-05): the prior callout treatment (background +
 * border-left + boxed padding) read as a major surface for what
 * is meta-info about /compare's scope. Reduced to inline footnote
 * register: smaller font, muted color, no chrome. The link is the
 * load-bearing element (action: go to /check); the prose is the
 * setup. */
.compare-v4_2-note {
    margin: var(--space-md) 0 var(--space-sm);
    font-size: 0.75rem;
    color: var(--text-3);
    line-height: 1.5;
}
.compare-v4_2-note a {
    color: var(--blue);
    text-decoration: underline;
}
.compare-v4_2-note a:hover,
.compare-v4_2-note a:focus-visible {
    text-decoration: none;
}

/* Compare model cards: side-by-side on wide viewports, stacked
 * on narrow ones. The previous treatment hardcoded
 * grid-template-columns: 1fr 1fr with no responsive breakpoint;
 * each panel ended up at ~390px on the default 860px main, and
 * ~290px on the new 1132px main with the TOC sidebar consuming
 * 240px + 32px gap. ~290px cards with annotated documents
 * (long sentences, monospace tables, claim numbers) overflowed.
 *
 * The minmax(0, 1fr) keeps grid items from blowing out their
 * column when they contain long unbroken content (URLs, code,
 * claim values without breakpoints); without min-width: 0 grid
 * items default to min-content sizing and force the column wider
 * than 1fr.
 *
 * The 880px breakpoint stacks the cards on viewports where two
 * 280px+ cards plus gap would not fit comfortably alongside the
 * TOC sidebar (240px) plus main padding (48px). A typical
 * laptop-narrow desktop (~1100px viewport) still gets side-by-
 * side cards; a tablet-width viewport (~900px) stacks them
 * cleanly. The check page's TOC layout uses the same 1131px
 * mobile breakpoint, so above that the compare grid stays
 * two-column with the wider main; below, the result-layout
 * already collapses to single-column and compare-models follows. */
.compare-models {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-lg);
    margin-top: var(--space-lg);
}

@media (max-width: 880px) {
    .compare-models {
        grid-template-columns: minmax(0, 1fr);
    }
}

.model-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1rem 1.25rem;
    min-width: 0;
    /* min-width: 0 inside a grid item lets it shrink below its
     * content's intrinsic min-content width. Without this, a
     * model card with a long unbroken URL or sentence forces its
     * grid column wider than the 1fr allocation, which cascades
     * into horizontal overflow on the .comparison-results
     * container. */
}

.model-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.6rem; }
.model-header h3 { font-size: 0.95rem; }
.model-words { font-size: 0.72rem; color: var(--text-3); }

/* Per-doc FVS chips on the model card. Surfaces the frames doing
 * structural work in this specific document as small badge-links
 * inside the card content. The takeaway-questions panel at the
 * page top shows the same data per-model; the chips here are
 * inline with the document so the reader does not need to scroll
 * back up to see what frame the doc operates with.
 *
 * Compound layout: each chip is a single inline-flex pill with
 * the frame name (left) + the FVS-NNN identifier (right). Click
 * opens the library entry. The flex row wraps on narrow viewports
 * so a card with three chips never blows out the column width.
 *
 * Visual register subtle by design: brand-blue tinted pill
 * matching the verdict-action-btn family, but smaller so the
 * chips read as inline metadata rather than a CTA cluster. */
.model-fvs-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 0.7rem;
}

.model-fvs-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.55rem;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--text);
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-radius: 999px;
    text-decoration: none;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

.model-fvs-chip:hover,
.model-fvs-chip:focus-visible {
    background: var(--surface);
    border-color: var(--blue);
    text-decoration: none;
}

.model-fvs-chip-name {
    font-weight: 600;
}

.model-fvs-chip-id {
    font-family: var(--mono);
    font-size: 0.66rem;
    font-weight: 600;
    color: var(--blue);
    letter-spacing: 0.02em;
}

.model-metrics { display: flex; gap: 0.75rem; margin-bottom: 0.75rem; }
.model-metric { text-align: center; }
.mm-value { display: block; font-family: var(--mono); font-size: 1.1rem; font-weight: 700; }
.mm-label { display: block; font-size: 0.65rem; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.03em; }
/* Stability display */
.stability-summary {
    display: flex;
    gap: var(--space-xl);
    flex-wrap: wrap;
}
.stability-model {
    font-size: 0.82rem;
    color: var(--text-2);
}
.stability-stable {
    font-weight: 700;
    color: var(--green);
}
.stability-changed {
    font-weight: 700;
    color: var(--orange);
}

.mm-value-good { color: var(--green); }
.mm-value-warn { color: var(--yellow); }
.mm-value-bad { color: var(--red); }

.model-response summary, .model-claims-detail summary {
    font-size: 0.78rem; color: var(--text-3); cursor: pointer; padding: 0.3rem 0; list-style: none;
}
.model-response summary::-webkit-details-marker, .model-claims-detail summary::-webkit-details-marker { display: none; }

.model-text {
    font-size: 0.78rem;
    line-height: 1.6;
    color: var(--text-2);
    max-height: 300px;
    overflow-y: auto;
    padding: var(--space-sm);
    background: var(--bg);
    border-radius: var(--radius);
    margin-top: 0.3rem;
    white-space: pre-wrap;
}

.model-claim {
    display: flex; align-items: center; gap: 0.4rem;
    font-size: 0.78rem; padding: 0.15rem 0.4rem; border-radius: var(--radius-sm);
}
.mc-fact { border-left: 3px solid var(--orange); }
.mc-hedged { border-left: 3px solid var(--green); }
.mc-nums { font-family: var(--mono); font-weight: 600; }
.mc-framing { font-size: 0.6rem; font-weight: 700; text-transform: uppercase; color: var(--text-3); }

.compare-examples { margin-top: 2rem; }
.compare-examples h2 { font-size: 0.95rem; color: var(--text-3); margin-bottom: 0.6rem; }

/* Documents-mode example cards.
   Heavier than topic example buttons because each card carries a
   title, two labels, a description, and a hook. The whole card is a
   button so a single click submits the preset doc pair. */
.doc-example-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 0.6rem;
}

.doc-example-form { margin: 0; }

.doc-example-btn {
    display: block;
    width: 100%;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.75rem 0.9rem;
    cursor: pointer;
    transition: border-color 0.2s, box-shadow 0.2s;
}
.doc-example-btn:hover {
    border-color: var(--blue);
    /* Use a brand-aware shadow color so the lift is visible in dark
       mode. The light-mode value is rgba(0,0,0,0.04) which would be
       invisible against the near-black surface in dark mode. */
    box-shadow: 0 1px 4px var(--shadow, rgba(0, 0, 0, 0.04));
}
.doc-example-btn:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.doc-example-title {
    display: block;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 0.3rem;
}

.doc-example-pair {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.35rem;
    flex-wrap: wrap;
}
.doc-example-label {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--text-2);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.1rem 0.4rem;
}
.doc-example-vs {
    font-size: 0.65rem;
    text-transform: uppercase;
    color: var(--text-3);
    letter-spacing: 0.04em;
}

.doc-example-desc {
    display: block;
    font-size: 0.74rem;
    color: var(--text-3);
    margin-bottom: 0.3rem;
}
.doc-example-hook {
    display: block;
    font-size: 0.74rem;
    color: var(--text-2);
    line-height: 1.4;
    border-left: 2px solid var(--border);
    padding-left: 0.55rem;
}

/* Anchor variant of doc-example-btn used when a precomputed
   saved-comparison hash is available. Same visual treatment as the
   button form, but the cursor and underline behavior need to be
   reset because <a> defaults differ from <button>. */
.doc-example-link {
    text-decoration: none;
    color: inherit;
}
.doc-example-link:visited { color: inherit; }

.doc-example-badge {
    display: inline-block;
    margin-top: 0.4rem;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--blue);
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-radius: var(--radius-sm);
    padding: 0.1rem 0.4rem;
}

/* Save bar on the live compare result page. Hidden by default;
   the SSE complete handler reveals it. Lays out as a single row of
   {button, status text, optional saved link}. */
.compare-save-bar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.8rem;
    flex-wrap: wrap;
}
.compare-save-status {
    font-size: 0.78rem;
    color: var(--text-3);
}
.compare-save-link {
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--blue);
    text-decoration: underline;
}
.compare-save-link:hover { text-decoration: none; }

/* compare-save-url: code-style box rendered in place of the
   compare-save-link after a successful save. user-select: all
   means a single click selects the whole URL for manual copy
   even when the user prefers that over the rewired button. */
.compare-save-url {
    display: inline-block;
    font-family: var(--mono);
    font-size: 0.78rem;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--green-border);
    border-radius: var(--radius-sm);
    padding: 0.3rem 0.5rem;
    user-select: all;
    word-break: break-all;
    line-height: 1.4;
    max-width: 100%;
}

html.dark .compare-save-url {
    background: var(--bg);
}

/* Saved comparison view: small adjustments on top of the live
   compare layout to remove progress affordances and tighten the
   spacing for a static artifact. */
.comparison-saved .compare-banner-vs {
    display: inline-block;
    font-size: 0.7rem;
    text-transform: uppercase;
    color: var(--text-3);
    letter-spacing: 0.05em;
    margin: 0 0.35rem;
    vertical-align: middle;
}
.compare-saved-subtitle {
    font-size: 0.85rem;
    color: var(--text-3);
    margin-top: 0.2rem;
}

/* Diff affordance on the saved-compare page. Collapsed by default
   so it does not compete with the main comparison content. The form
   builds a /compare/diff/{a}/{b} URL via JS because GET forms cannot
   construct path segments from input values. */
.diff-affordance {
    margin-top: 1.2rem;
    padding: 0.7rem 0.95rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 0.85rem;
}
.diff-affordance summary {
    cursor: pointer;
    color: var(--text-2);
    font-weight: 500;
    user-select: none;
}
.diff-affordance summary:hover { color: var(--blue); }
.diff-affordance[open] summary { margin-bottom: 0.6rem; }

.diff-affordance-form {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
}
.diff-affordance-form input {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.78rem;
    padding: 0.35rem 0.55rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    /* Grow to fill available space, but never shrink below 12rem
       (192px) so the input stays usable on narrow phones. The
       previous min-width: 16rem (256px) was too aggressive on
       375px viewports: flex-wrap rescued the layout but left
       the input cramped against the right edge. */
    flex: 1 1 12rem;
    min-width: 0;
    background: var(--bg);
    color: var(--text);
}
.diff-affordance-form input:focus {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 2px var(--blue-bg);
}
.diff-affordance-error {
    font-size: 0.75rem;
    color: var(--red, #b91c1c);
}
.diff-affordance-hint {
    margin: 0.55rem 0 0;
    font-size: 0.74rem;
    color: var(--text-3);
    line-height: 1.4;
}
.diff-affordance-hint code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.7rem;
    background: var(--bg);
    padding: 0.05rem 0.3rem;
    border-radius: var(--radius-sm);
}

/* Old example entries removed. Now using example-topic-btn. */

@media (max-width: 640px) {
    .compare-models { grid-template-columns: 1fr; }
    .disagree-card { flex-direction: column; align-items: flex-start; gap: 0.3rem; }
}

/* (Error page styles live in the .error-card module above.) */

/* ── Dark mode overrides ── */

html.dark .verify-card.checking .verify-card-status { background: var(--blue-bg); }
html.dark .verify-card.verified .verify-card-status { background: var(--green); color: #000; }
html.dark .verify-card.contradicted .verify-card-status { background: var(--red); color: #000; }
html.dark .verify-card.partial .verify-card-status { background: var(--yellow); color: #000; }

html.dark .main-input { background: var(--surface); }
html.dark .source-input { background: var(--blue-bg); }
html.dark .annotated-document { background: var(--surface); }

html.dark .verify-progress { background: rgba(255,255,255,0.06); }
html.dark .verify-evidence-text { background: var(--bg); }

html.dark .btn-primary { background: var(--text); color: var(--bg); }
html.dark .btn-ghost { border-color: var(--border); color: var(--text-3); }

/* Suggestion card dark-mode contrast fix. The definition block
   inside each frame-pattern card used a white overlay background
   that created light-on-light text in dark mode. The name and
   question elements use --text-1 / --blue which are now defined
   in the dark palette above. */
html.dark .suggestion-card {
    background: var(--surface);
    border-color: var(--border);
}
html.dark .suggestion-definition {
    background: rgba(0, 0, 0, 0.25);
    color: var(--text-2);
}
html.dark .suggestion-question {
    color: var(--blue);
}
html.dark .suggestion-link {
    color: var(--blue);
}

/* Reframe card dark-mode contrast. The card uses --surface which
   adapts, but the delta gain/loss colors (#16a34a/#dc2626) can be
   hard to read on dark surfaces. Lighten them slightly. */
html.dark .reframe-delta-gain { color: var(--green); }
html.dark .reframe-delta-loss { color: var(--red); }
html.dark .reframe-disclaimer { color: var(--text-3); }

html.dark .model-text { background: var(--bg); }
html.dark .disagree-card { background: var(--red-bg); }

/* Dark mode: bright backgrounds need dark text for WCAG AA contrast.
   Light-mode colors are dark enough for white text; dark-mode
   colors (#34d399, #fbbf24, #f87171) are not. */
html.dark .gfp-grounded  { color: #000; }
html.dark .gfp-framed    { color: #000; }
html.dark .gfp-projected { color: #000; }

/* ── Print ── */

/* ════════════════════════════════════════════
   PRINT STYLES: professional report output
   ════════════════════════════════════════════ */

@media print {
    /* Page setup: A4 with comfortable margins and a footer */
    @page {
        size: A4;
        margin: 18mm 14mm 18mm 14mm;
    }

    html { font-size: 11pt; }
    body {
        background: #fff;
        color: #000;
        line-height: 1.4;
        font-family: Georgia, "Times New Roman", serif;
    }

    /* Result-layout grid collapses to a single column when the
     * TOC is hidden so the .results content section takes the full
     * print width. Without this rule the grid keeps its 240px
     * sidebar column reserved (now empty) and the content prints
     * narrower. */
    .result-layout {
        display: block;
    }

    /* Hide all chrome and interactive elements */
    header, footer, nav,
    .result-actions, .form-actions,
    /* Result-page TOC sidebar: pure navigation, no information
       value on paper. Print stylesheet flattens the page back to
       a linear analytical document. */
    .toc-sidebar,
    /* Reframe overlay action row carries the same intent as
       result-actions (interactive next-step buttons, no information
       value on paper). Hidden alongside the rest of the action chrome. */
    .reframe-actions,
    /* MCP install upgrade card: pure call-to-action, no information
       value on paper. Hidden alongside the result-actions row above. */
    .mcp-upgrade-card,
    .doc-interact-hint,
    .verify-progress, .verify-progress-status,
    .compare-progress-wrap,
    .ai-skeleton, .ai-skeleton-spinner, .ai-skeleton-text,
    .model-skeleton,
    #ai-skeleton, #compare-ai-skeleton, #stability-skeleton,
    #consensus-skeleton,
    .sn-popover, .sn-popover-backdrop,
    #live-feed,
    .verify-card.checking,
    .demo-preview,
    /* Compare/diff interactive elements: forms and inputs that have
       no meaning on paper. The diff-affordance is a navigation form
       (paste a hash, click to diff); the compare-save-bar is the
       save button shown after the live SSE finishes; the
       doc-example forms run a comparison when clicked. None of
       these belong on a printed copy of a saved analysis or
       comparison. */
    .diff-affordance,
    .compare-save-bar,
    .doc-example-form,
    .compare-examples,
    .example-doc-fade,
    .instruments-panel,
    /* Save flow widgets are interactive (rewired buttons + URL
       boxes) and have no meaning on paper. The Saved analysis
       footer (.saved-meta) above already prints the canonical
       hash + date, which is the print-paper equivalent of a
       share link. */
    .save-status,
    .compare-save-url,
    /* Theme toggle: dark mode is irrelevant in print (always
       white background) and the moon/sun icon serves no purpose
       on paper. */
    .theme-toggle,
    .gfp-copy-btn {
        display: none !important;
    }

    /* Open all collapsible sections so the print includes them */
    details { display: block; }
    details > summary { list-style: none; }
    details > summary::before { display: none; }
    details[open] > *,
    details > * { display: revert; }

    /* Section cards: keep together on a page when possible */
    .section-card,
    .trust-banner,
    .findings-card,
    .gfp-card,
    .framing-grid,
    .framing-card,
    .framing-portrait,
    .sn-card,
    .verify-card,
    .consistency-finding,
    .annotated-document {
        break-inside: avoid;
        page-break-inside: avoid;
        border: 1px solid #999;
    }

    /* Trust banner: keep colored left border but remove tinted bg */
    .trust-banner {
        background: #fff !important;
        border-left: 4px solid #000;
        padding: 0.75rem 1rem;
    }
    .trust-banner.trust-high       { border-left-color: #16713a; }
    .trust-banner.trust-moderate   { border-left-color: #92620a; }
    .trust-banner.trust-low        { border-left-color: #b84515; }
    .trust-banner.trust-very-low   { border-left-color: #b91c1c; }

    /* Trust meter: keep visible but flatter ink */
    .trust-meter { background: #ddd; }
    .meter-verified { background: #16713a; }
    .meter-contradicted { background: #b91c1c; }
    .meter-unchecked { background: #888; }

    /* GFP bar: print-safe ink */
    .gfp-card { background: #fff !important; }
    .gfp-grounded  { background: #16713a; }
    .gfp-framed    { background: #92620a; color: #fff; }
    .gfp-projected { background: #b91c1c; }
    .gfp-projection-alert { background: #fff !important; border-color: #b91c1c; }
    .gfp-prohibition { background: #f5f5f5 !important; }

    /* Group headers: bold and slightly larger */
    .group-header h2,
    .section-group h2 {
        font-size: 13pt;
        font-weight: 700;
        margin-top: 0.6rem;
        page-break-after: avoid;
    }

    /* Annotated document: make it readable on paper */
    .annotated-document {
        font-family: Georgia, "Times New Roman", serif;
        font-size: 11pt;
        line-height: 1.55;
        padding: 0.75rem 1rem;
        background: #fff !important;
    }

    /* Highlight marks: keep colored backgrounds for verification status
       so verified vs contradicted is visible in print, but tone them
       down for less ink. */
    mark.hl-verified {
        background: #e8f5e9 !important;
        border-bottom: 1.5pt solid #16713a !important;
    }
    mark.hl-contradicted {
        background: #fef2f2 !important;
        border-bottom: 1.5pt solid #b91c1c !important;
    }
    mark.hl-fact {
        background: #fff7ed !important;
        border-bottom: 1.5pt solid #b84515 !important;
    }
    mark.hl-hedged, mark.hl-prediction {
        background: #f5f5f5 !important;
        border-bottom: 1.5pt solid #888 !important;
    }

    /* Framing portrait: keep the prominent treatment */
    .framing-portrait {
        background: #fff !important;
        border-left: 4px solid #1d4ed8 !important;
        padding: 0.6rem 0.9rem;
    }
    .framing-portrait-label {
        color: #1d4ed8 !important;
        font-weight: 700;
    }
    .framing-portrait-text { font-size: 11pt; }

    /* Comparison portrait under print. White bg pinned !important
     * so any future colored-bg regression in the screen rule does
     * not leak into print and waste ink on grayscale printers. */
    .compare-takeaway-portrait {
        background: #fff !important;
        border-left: 3px solid #1d4ed8 !important;
        padding: 0.5rem 0 0.5rem 0.8rem;
        font-size: 11pt;
    }

    /* Findings card: bordered list */
    .findings-card {
        background: #fff !important;
        padding: 0.6rem 0.9rem;
    }

    /* Results caveat: closing-note disclosure. On print, force open
       so the bullets are visible regardless of the user's expand
       state on screen, and drop the disclosure chevron since the
       affordance is irrelevant in print. The summary becomes a plain
       heading-like label; the list reads beneath it. */
    details.results-caveat[open] > *,
    details.results-caveat > * {
        display: revert !important;
    }
    .results-caveat {
        background: #fff !important;
        border: 1px solid #ccc !important;
        padding: 0.6rem 0.9rem;
        page-break-inside: avoid;
        break-inside: avoid;
    }
    .results-caveat-summary {
        list-style: none;
        cursor: default;
        font-weight: 700;
        color: #333 !important;
        font-size: 9pt;
        padding-left: 0 !important;
    }
    .results-caveat-summary::before { display: none !important; }
    .results-caveat-list li::before {
        color: #555 !important;
    }
    .results-caveat-footnote {
        border-top: 1px dotted #999 !important;
        color: #555 !important;
    }

    /* Structural-profile intro: keep visible on print so the user
       reads what the deeper sections are before encountering them. */
    .structural-profile-intro {
        border-top: 1px solid #999 !important;
        padding-top: var(--space-sm);
    }
    .structural-profile-intro-label {
        background: transparent !important;
        color: #333 !important;
        font-size: 9pt;
    }

    /* Claims-purpose paragraph: keep visible. */
    .claims-purpose {
        color: #444 !important;
    }

    /* Verification cards: only show those with a real verdict
       (the .checking ones are already hidden above) */
    .verify-card {
        background: #fff !important;
        margin-bottom: 0.4rem;
    }

    /* Hide tablist of trust counts on print but keep the meter */
    .trust-counts > span > .tc-num { font-weight: 700; }

    /* Compare page: hide skeletons, show populated content only */
    .compare-section { break-inside: avoid; }
    .model-card {
        break-inside: avoid;
        background: #fff !important;
        border: 1px solid #999;
    }

    /* Avoid orphan headings */
    h1, h2, h3 {
        page-break-after: avoid;
        break-after: avoid;
    }

    /* Print-only footer: doc URL. Color at 4.7:1 on paper white for
     * readable-but-unobtrusive brand line at 8pt (small print needs
     * the extra contrast margin even though it is secondary). Matches
     * the --text-4 post-WCAG value used elsewhere. */
    body::after {
        content: "frame.clarethium.com";
        display: block;
        margin-top: var(--space-lg);
        font-size: 8pt;
        color: #707070;
        text-align: center;
    }

    /* Disable transitions and animations */
    *, *::before, *::after {
        animation: none !important;
        transition: none !important;
    }
}

/* ── Framing Profile ── */

#group-framing {
    margin-top: var(--space-lg);
}

/* Analytical Coverage + Document Structure stack.
 *
 * Previously rendered as a 2-column responsive grid (`repeat(auto-fit,
 * minmax(360px, 1fr))`), which at typical laptop widths gave each card
 * ~45% of the content column. That constrained width squeezed dense
 * data (especially the 5-dimension Analytical Coverage list with its
 * bar track, count, and density stats per row) into visually cramped
 * wrapping. The operator flagged the layout as "not expert level" on
 * info-dense documents.
 *
 * Changed to vertical stack so each card gets the full content column.
 * The Analytical Coverage bar track can now occupy a comfortable
 * 180-320px range; the Document Structure row grid can let its detail
 * text column stretch without competing with a neighbor. Information
 * density per horizontal pixel drops, legibility per row rises. The
 * vertical cost is ~200px of additional height per document, which is
 * acceptable given both cards are below the hero V4.2 panel and the
 * trust verdict. the reader has already made it past the load-bearing
 * sections by the time they reach these depth views.
 */
.framing-grid {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    margin-bottom: 0.6rem;
}

.framing-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.85rem 1.05rem;
}

.framing-card-title {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    margin-bottom: var(--space-sm);
}

.framing-coverage-summary {
    font-size: 0.85rem;
    color: var(--text-2);
    margin-bottom: var(--space-sm);
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
}

.framing-coverage-count {
    font-weight: 700;
    font-size: 1.35rem;
    color: var(--text);
    line-height: 1;
    letter-spacing: -0.01em;
}

.framing-coverage-balance {
    font-size: 0.78rem;
    margin-top: 0.25rem;
    margin-bottom: 0.6rem;
    padding: 0.35rem 0 0.35rem 0.6rem;
    border-left: 2px solid var(--text-3);
    line-height: 1.5;
    color: var(--text-2);
}
.balance-indicator { color: var(--text-2); }
.balance-heavy { border-left-color: var(--orange); color: var(--text-2); }
.balance-heavy::before { content: ""; }
.balance-moderate { border-left-color: var(--yellow); }
.balance-even { border-left-color: var(--green); }
html.dark .framing-coverage-balance { color: var(--text-2); }

/* Analytical Coverage category list.
 *
 * Redesigned for info density + clean visual hierarchy. Each row now
 * renders as a distinct tile with subtle separation (border + gap) so
 * the five dimensions read as a scannable list rather than a flowing
 * paragraph. Bar track widened from 60px to a flexible 120-160px range
 * so the presence/density signal is actually readable at a glance.
 * Covered vs missing distinguished by left-border color in addition to
 * text dimming. a construct-honest visual cue that "no markers" is as
 * load-bearing a finding as "markers detected."
 */
.framing-categories {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin-top: 0.4rem;
}

/* framing-cat is now a 2-column grid with the dimension header on the
 * left and the bar-row (or "no markers" absent span) on the right:
 *
 *   [name + desc]  │  [bar track ────────]  [count (density)]
 *
 * The header column is fixed-width so all 5 dimensions align visually
 * on the left edge; the bar column stretches to fill the card width
 * that the new full-width framing-grid gives us. Collapses to two
 * stacked rows on narrow viewports via the @media breakpoint below.
 */
.framing-cat {
    font-size: 0.84rem;
    padding: 0.35rem 0 0.35rem 0.7rem;
    border-left: 2px solid transparent;
    scroll-margin-top: 80px;
    display: grid;
    grid-template-columns: minmax(160px, 220px) 1fr;
    grid-column-gap: 1rem;
    align-items: center;
}

/* Candidate-miss details block, when rendered inside a framing-cat,
 * spans both grid columns so the expander reads as a dimension-wide
 * annotation rather than a column-2 afterthought. */
.framing-cat > .framing-candidates {
    grid-column: 1 / -1;
    margin-top: 0.4rem;
}

/* Narrow viewport fallback: the two-column grid collapses to stacked
 * single-column rows below the 640px breakpoint (phone territory).
 * Breakpoint aligned with .framing-structure-grid below and the
 * .fai-lead-grid so both Framing Profile cards stack at the same
 * width, avoiding an awkward intermediate state where one card is
 * stacked and the other is still two-column. */
@media (max-width: 640px) {
    .framing-cat {
        grid-template-columns: 1fr;
        grid-row-gap: 0.4rem;
    }
}

.framing-cat-covered {
    border-left-color: var(--blue, #4a90d9);
}

.framing-cat-missing {
    border-left-color: var(--text-4);
}

.framing-cat-header {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    flex-wrap: wrap;
    /* No bottom margin: single-line layout puts bar+stat in adjacent
     * grid columns, not below. The header occupies the first grid
     * column and shares a row with the bar track and count. */
}

.framing-cat-name {
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: capitalize;
}

.framing-cat-desc {
    font-size: 0.72rem;
    color: var(--text-4);
    font-style: italic;
}

.framing-cat-covered .framing-cat-name { color: var(--text); }
.framing-cat-missing .framing-cat-name { color: var(--text-3); }
.framing-cat-missing .framing-cat-desc { color: var(--text-4); }

/* Bar-row now wraps only the bar+stat, as the name+desc sit in a
 * separate grid column of the parent framing-cat. Kept as a grid so
 * the count column aligns across all 5 dimension rows regardless of
 * bar length. */
.framing-cat-bar-row {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 0.7rem;
}

.framing-cat-bar-track {
    height: 7px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

html.dark .framing-cat-bar-track { background: rgba(255, 255, 255, 0.06); }

.framing-cat-bar {
    height: 100%;
    border-radius: var(--radius-sm);
    min-width: 2px;
}

.framing-cat-bar-covered { background: var(--blue); }
.framing-cat-bar-weak { background: var(--text-4); opacity: 0.5; }

.framing-cat-stat {
    font-size: 0.72rem;
    color: var(--text-3);
}

.framing-cat-density {
    font-family: var(--mono);
    font-size: 0.68rem;
    color: var(--text-4);
}

.framing-cat-absent {
    font-size: 0.72rem;
    color: var(--text-4);
    font-style: italic;
}

/* Temporal bars */

.framing-temporal-bars {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    margin-top: var(--space-xs);
}

.framing-bar-row {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
}

.framing-bar-label {
    min-width: 55px;
    font-size: 0.8rem;
    color: var(--text-3);
}

.framing-bar-dominant {
    font-weight: 700;
    color: var(--text);
}

.framing-bar-track {
    flex: 1;
    height: 8px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 4px;
    overflow: hidden;
}

html.dark .framing-bar-track { background: rgba(255, 255, 255, 0.06); }

.framing-bar {
    height: 100%;
    border-radius: 4px;
    transition: width 0.3s ease;
}

.framing-temporal-note {
    font-size: 0.68rem;
    color: var(--text-4);
    margin-top: 0.35rem;
}

.framing-bar-past    { background: var(--blue); }
.framing-bar-present { background: var(--text-4); }
.framing-bar-future  { background: var(--green); }

.framing-bar-pct {
    min-width: 32px;
    text-align: right;
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--text-2);
}

/* Document Structure rows.
   Each row is a distinct measurement (Voice, Evidence, Confidence,
   Temporal). They are visually adjacent but conceptually separate;
   the prior 0.3rem margin let the small detail-text of one row read
   as belonging to the next. Bumped to 0.6rem and the
   --separator modifier adds a hairline between conceptual groups
   (Sourcing -> Stance -> Time) so Evidence and Confidence stop
   visually overlapping. */
/* Document Structure internal 2-column layout.
 *
 * The Document Structure card now renders Voice + Evidence in the left
 * column and Confidence + Temporal in the right, splitting the card's
 * full width in half per round-4 feedback. Semantic grouping: left =
 * "how the document speaks and what backs it" (voice + evidence);
 * right = "what stance it asserts and when it lives" (confidence +
 * temporal). The two columns are independent vertical flows so each
 * column's candidate-miss expanders stay under their parent row.
 *
 * At narrow widths (≤720px, below typical tablet) the grid collapses
 * to a single column so the reader doesn't get squeezed into two
 * cramped half-columns on a phone.
 */
.framing-structure-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    align-items: start;
}

.framing-structure-col {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* Breakpoint aligned with .framing-cat above and .fai-lead-grid below
 * so the Framing Profile cards (Analytical Coverage + Document
 * Structure) both transition to their stacked forms at the same
 * viewport width. Avoids an intermediate state where one is stacked
 * and the other is still two-column. */
@media (max-width: 640px) {
    .framing-structure-grid {
        grid-template-columns: 1fr;
        gap: 0.6rem;
    }
}

/* Document Structure row layout.
 *
 * Each row (Voice / Evidence / Confidence / Temporal) uses CSS Grid
 * with an explicit label column so labels align across rows even when
 * the value or detail text varies wildly in length. Prior `display: flex`
 * layout let the detail wrap mid-row, and the visual alignment broke
 * down on info-dense documents. The grid pins label + value in fixed
 * positions and lets detail text wrap beneath when it's too long to fit
 * on the value's row. clean hierarchy under any content length.
 */
.framing-structure-row {
    display: grid;
    grid-template-columns: 68px auto 1fr;
    grid-column-gap: 0.6rem;
    grid-row-gap: 0.15rem;
    align-items: baseline;
    margin-bottom: 0.55rem;
    font-size: 0.82rem;
}

.framing-structure-row--separator {
    margin-top: 0.55rem;
    padding-top: 0.55rem;
    border-top: 1px solid var(--border-light);
}

.framing-structure-row--candidates {
    grid-template-columns: 1fr;
}

.framing-struct-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-4);
    white-space: nowrap;
}

.framing-struct-value {
    font-weight: 600;
    color: var(--text);
    font-size: 0.85rem;
    white-space: nowrap;
}

.framing-struct-detail {
    font-size: 0.74rem;
    color: var(--text-3);
    line-height: 1.45;
    grid-column: 2 / -1;
}

.framing-struct-confidence {
    font-size: 0.7rem;
    color: var(--text-3);
    grid-column: 2 / -1;
    font-style: italic;
}

/* Voice type badges */
.framing-voice-prescriptive { color: var(--orange); }
.framing-voice-promotional { color: var(--yellow); }
.framing-voice-advisory { color: var(--blue); }
.framing-voice-analytical { color: var(--green); }
.framing-voice-descriptive { color: var(--blue); }

/* Evidence basis badges */
.framing-evidence-sourced { color: var(--green); }
.framing-evidence-mixed { color: var(--yellow); }
.framing-evidence-unsourced { color: var(--orange); }

/* Portrait: the synthesis. This is the unique value of the product -
   the English-language summary of what the document does to the
   reader's perception. Promoted to lead the framing section so it
   reads as the centerpiece, not as a footnote.

   Visual treatment: distinct background, larger text, label above the
   body so users immediately understand what they are looking at. */
.framing-portrait {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--blue);
    border-radius: var(--radius-lg);
    padding: 1rem 1.25rem;
    margin-bottom: var(--space-lg);
}

html.dark .framing-portrait {
    background: var(--bg);
    border-left-color: var(--blue);
}

.framing-portrait-label {
    display: block;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--blue);
    margin-bottom: 0.4rem;
}

.framing-portrait-text {
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--text);
    margin: 0;
}

/* Portrait head: label on the left, register toggle on the right.
 * Replaces the earlier "Show methodology detail" collapsed expander
 * (which operator round-5 feedback flagged as undiscoverable) with
 * a visible two-pill toggle. Default register is Natural; clicking
 * Precise swaps the paragraph in place via CSS sibling selectors
 * (no JavaScript). Both registers are immediately presented as
 * available options so a researcher sees the Precise option at
 * page load instead of having to hunt for a subtle expander. */
.framing-portrait-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 0.4rem;
}

.framing-portrait-toggle {
    display: inline-flex;
    position: relative;
    background: var(--bg-2, rgba(0, 0, 0, 0.04));
    border-radius: 999px;
    padding: 2px;
    border: 1px solid var(--border-light);
}

/* Radios are hoisted above the head div so sibling selectors can
 * reach the paragraphs below. Visually hidden without removing from
 * the accessibility tree. Labels drive clicks via for= attribute. */
.framing-portrait > input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
}

.framing-portrait-toggle-pill {
    display: inline-block;
    padding: 0.2rem 0.75rem;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-3);
    border-radius: 999px;
    cursor: pointer;
    user-select: none;
    transition: background 0.12s ease, color 0.12s ease;
}

.framing-portrait-toggle-pill:hover {
    color: var(--text-1);
}

/* Active-pill styling uses :has() to project the radio's checked
 * state onto the scoped labels inside the toggle div. This avoids
 * needing the labels to be direct siblings of the radios (the labels
 * live inside .framing-portrait-toggle for layout; the radios live
 * at the top of .framing-portrait for sibling-selector access to
 * the paragraphs below). */
.framing-portrait:has(#portrait-reg-natural:checked) .framing-portrait-toggle-natural {
    background: var(--blue, #4a90d9);
    color: white;
}

.framing-portrait:has(#portrait-reg-precise:checked) .framing-portrait-toggle-precise {
    background: var(--blue, #4a90d9);
    color: white;
}

/* Project the radio's focus state onto its visible label pill.
 * Uses outline (not box-shadow) so the focus indicator matches the
 * rest of the page's focus-visible convention: solid 2px blue with
 * 2px offset. Previous box-shadow was transparent-alpha and read as
 * a soft halo; outline is sharper and more accessible for low-vision
 * users. */
.framing-portrait:has(#portrait-reg-natural:focus-visible) .framing-portrait-toggle-natural,
.framing-portrait:has(#portrait-reg-precise:focus-visible) .framing-portrait-toggle-precise {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* In-place paragraph swap driven by the Precise radio. Scoped to
 * .framing-portrait so the rule does not match any other radio pair
 * that happens to share ids on the page. */
.framing-portrait .framing-portrait-text-precise,
.framing-portrait .framing-portrait-precise-note {
    display: none;
}

.framing-portrait #portrait-reg-precise:checked ~ .framing-portrait-text-precise,
.framing-portrait #portrait-reg-precise:checked ~ .framing-portrait-precise-note {
    display: block;
}

.framing-portrait #portrait-reg-precise:checked ~ .framing-portrait-text-natural {
    display: none;
}

.framing-portrait-precise-note {
    margin: 0.6rem 0 0 0;
    font-size: 0.72rem;
    line-height: 1.5;
    color: var(--text-3);
    font-style: italic;
    padding: 0.5rem 0.7rem;
    background: var(--bg-2, rgba(0, 0, 0, 0.03));
    border-radius: var(--radius-sm);
}

.framing-portrait-precise-link {
    color: var(--blue);
    text-decoration: none;
}

.framing-portrait-precise-link:hover {
    text-decoration: underline;
}

html.dark .framing-portrait-toggle {
    background: rgba(255, 255, 255, 0.05);
}

html.dark .framing-portrait-precise-note {
    background: rgba(255, 255, 255, 0.04);
}

/* ── Source Network verification ── */

.sn-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--border);
    border-radius: var(--radius);
    padding: 0.6rem 0.85rem;
    margin-bottom: 0.4rem;
}
.sn-verified { border-left-color: var(--green); }
.sn-close { border-left-color: var(--green); opacity: 0.85; }
.sn-contradicted { border-left-color: var(--red); }
.sn-disputed { border-left-color: var(--yellow); }
.sn-unverifiable { border-left-color: var(--text-4); }
.sn-projection { border-left-color: var(--blue); }

.sn-header {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-bottom: var(--space-xs);
}
.sn-numbers {
    font-family: var(--mono);
    font-weight: 700;
    font-size: 0.9rem;
    color: var(--text);
}
.sn-verdict-badge {
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-sm);
}
.sn-badge-verified { background: var(--green-bg); color: var(--green); }
.sn-badge-close { background: var(--green-bg); color: var(--green); }
.sn-badge-contradicted { background: var(--red-bg); color: var(--red); }
.sn-badge-disputed { background: var(--yellow-bg); color: var(--yellow); }
.sn-badge-unverifiable { background: var(--gray-bg); color: var(--gray); }
.sn-badge-projection { background: var(--blue-bg); color: var(--blue); }

.sn-via {
    font-size: 0.7rem;
    color: var(--text-4);
}

.sn-context-badge {
    display: inline-block;
    font-size: 0.6rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    background: var(--surface-2, rgba(0, 0, 0, 0.04));
    border: 1px solid var(--border, #e0e0e0);
    border-radius: 3px;
    padding: 0.1em 0.4em;
    margin-left: 0.3rem;
    vertical-align: middle;
    line-height: 1.3;
}
html.dark .sn-context-badge {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.12);
}

.sn-tier-strong       { color: var(--green); border-color: var(--green); }
.sn-tier-moderate     { color: var(--yellow); border-color: var(--yellow); }
.sn-tier-weak         { color: var(--red); border-color: var(--red); }
.sn-tier-uncalibrated { color: var(--text-3); border-color: var(--border); font-style: italic; }

/* The reliability tier badge is rendered as an anchor so clicking
   it lands on the calibration corpus. Keep the visual treatment of
   the non-link version (same pill shape, no underline, tier-coloured
   border), but give it a subtle hover state so the click affordance
   is discoverable. */
a.sn-ctx-reliability {
    text-decoration: none;
    cursor: pointer;
    transition: background 0.1s ease;
}
a.sn-ctx-reliability:hover {
    background: rgba(0, 0, 0, 0.04);
    text-decoration: none;
}
html.dark a.sn-ctx-reliability:hover {
    background: rgba(255, 255, 255, 0.08);
}

.sn-sentence {
    font-size: 0.78rem;
    color: var(--text-2);
    margin-bottom: 0.3rem;
    line-height: 1.4;
}
.sn-sources { margin-top: 0.2rem; }
.sn-source-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.75rem;
    margin-bottom: 0.15rem;
}
.sn-source-name {
    font-weight: 600;
    color: var(--text-3);
    min-width: 90px;
}
.sn-source-value {
    font-family: var(--mono);
    font-weight: 600;
    color: var(--text);
}
.sn-match-badge {
    font-size: 0.6rem;
    font-weight: 600;
    padding: 0.05rem 0.3rem;
    border-radius: 2px;
}
.sn-match-exact { background: var(--green-bg); color: var(--green); }
.sn-match-close { background: var(--green-bg); color: var(--green); }
.sn-match-contradicted { background: var(--red-bg); color: var(--red); }
.sn-diff { font-size: 0.65rem; color: var(--text-4); }
.sn-source-link {
    font-size: 0.65rem;
    color: var(--blue);
    text-decoration: none;
}
.sn-source-link:hover { text-decoration: underline; }
.sn-source-context {
    font-size: 0.7rem;
    color: var(--text-4);
    font-style: italic;
    margin: 0.1rem 0 0.2rem 90px;
    line-height: 1.3;
}

/* Context-relevance chip prefix. Inline with the source-context
 * paragraph, flags the "numbers match, topics don't" false-positive
 * class. tier="none" is a stronger warning than tier="weak"; both
 * carry tooltip detail on hover.
 */
.sn-relevance-chip {
    display: inline-block;
    margin-right: 0.35rem;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    cursor: help;
    font-style: normal;
    vertical-align: middle;
}
.sn-relevance-none {
    background: rgba(249, 115, 22, 0.08);
    color: var(--orange);
    border: 1px solid rgba(249, 115, 22, 0.35);
}
.sn-relevance-weak {
    background: rgba(245, 158, 11, 0.08);
    color: var(--yellow);
    border: 1px solid rgba(245, 158, 11, 0.35);
}
html.dark .sn-relevance-none {
    background: rgba(249, 115, 22, 0.14);
    border-color: rgba(249, 115, 22, 0.4);
}
html.dark .sn-relevance-weak {
    background: rgba(245, 158, 11, 0.14);
    border-color: rgba(245, 158, 11, 0.4);
}

/* AI-assisted framing interpretation */
.framing-ai-interpretation {
    margin-top: var(--space-md);
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--blue-border);
    border-radius: var(--radius);
    background: var(--blue-bg);
}

/* AI skeleton (loading state for async Phase 2).
 *
 * Shaped as a content-placeholder: status row (spinner + text) at top
 * followed by gray skeleton rows that approximate the final rendered
 * height. Reduces CLS when Grok returns and the real content replaces
 * the skeleton.
 */
.ai-skeleton {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    padding: 0.5rem 0 1rem;
    min-height: 260px;
    transition: opacity 0.4s ease;
}

.ai-skeleton.fade-out {
    opacity: 0;
}

.ai-skeleton-status {
    display: flex;
    align-items: center;
    gap: var(--space-md);
}

/* Individual placeholder rows. Shimmer animation signals active
 * loading; subtler than a solid gray bar. */
.ai-skeleton-rows {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    margin-top: 0.4rem;
}

.ai-skeleton-row {
    height: 0.9rem;
    border-radius: var(--radius-sm);
    background: linear-gradient(
        90deg,
        var(--border) 0%,
        rgba(0, 0, 0, 0.04) 50%,
        var(--border) 100%
    );
    background-size: 200% 100%;
    animation: ai-skeleton-shimmer 1.8s ease-in-out infinite;
}

.ai-skeleton-row--lead { width: 85%; height: 1.1rem; }
.ai-skeleton-row--short { width: 60%; }

html.dark .ai-skeleton-row {
    background: linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.06) 0%,
        rgba(255, 255, 255, 0.12) 50%,
        rgba(255, 255, 255, 0.06) 100%
    );
    background-size: 200% 100%;
}

@keyframes ai-skeleton-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* Respect prefers-reduced-motion: shimmer is decorative. */
@media (prefers-reduced-motion: reduce) {
    .ai-skeleton-row { animation: none; }
}

/* AI skeleton spinner uses the same flowing-border loader pattern as
 * .loading-spinner (see comment block above). Fixed square; bright
 * segment sweeps around the border via animating --loading-angle on
 * the ::before pseudo's conic-gradient. */
.ai-skeleton-spinner {
    width: 20px;
    height: 20px;
    border: 1.5px solid var(--border);
    border-radius: 3px;
    position: relative;
    flex-shrink: 0;
}

/* ::before rule handled by the shared flowing-border block below. */

.ai-skeleton-text {
    font-size: 0.82rem;
    color: var(--text-3);
}

/* AI-unavailable surface: two-paragraph fallback that replaces the
 * skeleton when /api/ai-interpret fails. The head line names the
 * specific failure mode (rate-limited vs generic unavailable); the
 * body line communicates what is STILL intact (the structural read
 * + takeaway questions). Without the body line a user reads "AI
 * unavailable" and thinks the whole analysis failed; with it, they
 * understand most of the page worked and the AI layer is the only
 * gap. */
.ai-unavailable-head {
    color: var(--text-2);
    font-weight: 600;
    margin: 0 0 0.4rem 0;
}

.ai-unavailable-body {
    color: var(--text-3);
    font-size: 0.82rem;
    line-height: 1.55;
    margin: 0;
    max-width: 56ch;
}

#group-ai-interpretation {
    min-height: 100px;
}

#ai-content {
    animation: fadeIn 0.5s ease;
}

/* Promoted AI interpretation (lead section) */
.fai-lead-topic {
    font-size: 0.92rem;
    font-weight: 500;
    color: var(--text);
    line-height: 1.5;
    margin-bottom: 0.6rem;
}

/*
   Takeaway block: the two construct-honest elements at the top of the
   Analysis section (honest headline and question to ask). Visual goal
   is pull-quote density: scannable, higher weight than surrounding
   body copy, distinct from the lead-grid cards below. Left accent
   border carries the color encoding (blue for reframe, amber for
   probe) so the semantics survive at a glance.
*/
.fai-takeaway {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    margin-bottom: 1.15rem;
    padding: 1rem 1.15rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    /* Subtle elevation so the takeaway reads as the page's primary
     * actionable surface rather than another flat analysis card. */
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

html.dark .fai-takeaway {
    background: var(--surface);
    border-color: var(--border);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}

.fai-takeaway-row {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.45rem 0.75rem;
    border-left: 4px solid var(--blue);
    /* Per-row background tint matches the row's left-accent. The page's
     * --blue-bg / --yellow-bg variables resolve to solid hex in light
     * mode and 0.10-opacity rgba in dark mode, so using the tokens
     * would produce inconsistent strength across themes (and 0.10 is
     * stronger than this row needs, body text would compete with
     * the tint). The ad-hoc rgba here matches the convention used
     * for similar "subtler than the standard accent-bg" surfaces
     * elsewhere in this file (search ".consistency-warning"). */
    background: rgba(59, 130, 246, 0.05);
    border-radius: 0 6px 6px 0;
}

html.dark .fai-takeaway-row {
    background: rgba(59, 130, 246, 0.08);
}

.fai-takeaway-label {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--blue);
}

/* Label row: legacy /check structural pattern, retained for cross-
 * surface visual consistency on the Honest headline label. */
.fai-takeaway-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.2rem;
}

/* Copy-to-clipboard button for the Question to ask row.
 * Sized up from the original 0.65rem ghost pill so the action reads
 * as a clear affordance, but takes its color tokens from the page's
 * .btn-secondary system (used by Save & share on this same view) so
 * the visual register matches the page's other secondary CTAs. The
 * hover lifts the border + label toward the question row's yellow
 * accent to tie the action to its context without filling it; the
 * codebase's --yellow is a deep amber in light mode (#92620a) where
 * dark text on solid yellow fails WCAG contrast, so the filled
 * variant from a prior pass was wrong. */
/* Polish (2026-05-05): dropped uppercase + letter-spacing (which
 * read as shouty billboard CTAs), bumped font-size 0.72rem ->
 * 0.85rem so the label is comfortably readable, lighter font-
 * weight 700 -> 600 so the buttons stop competing with body
 * text for visual weight. The hover state gains a subtle elevation
 * shadow + slight lift for tactile feedback consistent with the
 * .btn variants on the same page. */
.fai-takeaway-copy-btn {
    font-size: 0.85rem;
    font-weight: 600;
    padding: 0.5rem 1.05rem;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    cursor: pointer;
    line-height: 1.3;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease, transform 0.12s ease, box-shadow 0.15s ease;
    flex-shrink: 0;
}

.fai-takeaway-copy-btn:hover,
.fai-takeaway-copy-btn:focus-visible {
    border-color: var(--yellow);
    color: var(--yellow);
    background: rgba(245, 158, 11, 0.06);
    outline: none;
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
}

@media (prefers-reduced-motion: reduce) {
    .fai-takeaway-copy-btn:hover,
    .fai-takeaway-copy-btn:focus-visible {
        transform: none;
    }
}

.fai-takeaway-copy-btn--copied {
    border-color: var(--green) !important;
    color: var(--green) !important;
    background: rgba(16, 185, 129, 0.08) !important;
}

html.dark .fai-takeaway-copy-btn {
    background: var(--surface);
    border-color: var(--border);
    color: var(--text);
}

/* Usage hint below the question text: verb-first CTA so the reader
 * knows the next step after copying, not a passive tip. The arrow
 * draws the eye and signals continuation rather than an aside. */
.fai-takeaway-hint {
    font-size: 0.78rem;
    color: var(--text-2);
    font-style: normal;
    margin: 0.45rem 0 0 0;
    line-height: 1.4;
}

.fai-takeaway-text {
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--text);
    margin: 0;
    font-weight: 500;
    /* Cap text width at ~70 characters for readability on wide
     * viewports; narrow viewports are unaffected (content column
     * is already narrower than 70ch there). */
    max-width: 70ch;
}

/* Narrow viewports: button drops to its own row beneath the label so
 * it gets a full-width, easy-to-tap target. 540px aligns with the
 * existing narrow-viewport breakpoint elsewhere in this stylesheet
 * (search "max-width: 540px"); 520px would have introduced a unique
 * cutoff and an extra responsive boundary for callers to remember. */
@media (max-width: 540px) {
    .fai-takeaway-label-row {
        flex-wrap: wrap;
    }
    .fai-takeaway-copy-btn {
        width: 100%;
        margin-top: 0.4rem;
        padding: 0.55rem 0.95rem;
    }
}

/* Deep-link button row: lives under the question text in the
 * takeaway block. Three peer actions (Open in Claude, Open in
 * ChatGPT, Copy prompt). Layout flex-wraps so narrow viewports
 * stack the buttons full-width. The deep-link buttons share the
 * .fai-takeaway-copy-btn baseline shape so the action group reads
 * as one cohesive set; the brand accent only emerges on hover/
 * focus, not at rest, so the row does not look like a billboard. */
.fai-takeaway-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.55rem;
    align-items: center;
}

/* Polish (2026-05-05): same treatment as .fai-takeaway-copy-btn
 * above. Dropped uppercase + letter-spacing, bumped font-size,
 * lighter font-weight, added hover lift + soft shadow so the
 * three Take-to-LLM buttons read as proper interactive elements
 * rather than billboard CTAs. The brand-color hover states
 * (Claude orange, ChatGPT green) stay unchanged below; the
 * polish is to the at-rest baseline so the brand differentiation
 * has a calmer canvas to land on. */
.fai-takeaway-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.85rem;
    font-weight: 600;
    padding: 0.5rem 1.05rem;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    cursor: pointer;
    line-height: 1.3;
    text-decoration: none;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease, transform 0.12s ease, box-shadow 0.15s ease;
    flex-shrink: 0;
}

.fai-takeaway-action-btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
}

@media (prefers-reduced-motion: reduce) {
    .fai-takeaway-action-btn:hover {
        transform: none;
    }
}

.fai-takeaway-action-btn:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* Brand-accented hover state. Claude orange (#d97757) and OpenAI
 * green (#10a37f) are canonical brand colors. The codebase has no
 * --claude-* / --openai-* variables, so these are hardcoded; same
 * convention as .gfp-grounded uses var(--green) for "verified"
 * semantic meaning. They are scoped to this single component so
 * the brand coupling stays local. If a brand changes its color,
 * the change is one place. */
.fai-takeaway-action-btn--claude:hover,
.fai-takeaway-action-btn--claude:focus-visible {
    border-color: #d97757;
    color: #d97757;
    background: rgba(217, 119, 87, 0.06);
    outline: none;
}

.fai-takeaway-action-btn--chatgpt:hover,
.fai-takeaway-action-btn--chatgpt:focus-visible {
    border-color: #10a37f;
    color: #10a37f;
    background: rgba(16, 163, 127, 0.06);
    outline: none;
}

html.dark .fai-takeaway-action-btn {
    background: var(--surface);
    border-color: var(--border);
    color: var(--text);
}

@media (max-width: 540px) {
    .fai-takeaway-action-btn {
        flex: 1 1 100%;
        justify-content: center;
        padding: 0.55rem 0.95rem;
    }
}

/* Frame Action Panel (Option B, 2026-05-04). Replaces the old
   single-headline-and-question takeaway block with one card per
   detected frame. Each card carries Open in Claude / ChatGPT /
   Copy prompt buttons that load a frame-specific prompt. The user
   chooses which frame to think with; the LLM (after the click)
   produces the document-aware brainstorm. */
.frame-action-panel {
    margin: 0 auto var(--space-lg);
    max-width: var(--max-w);
    padding: var(--space-md) var(--space-lg);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.frame-action-header {
    margin-bottom: var(--space-md);
}
.frame-action-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 0.25rem 0;
    color: var(--text-1);
}
.frame-action-subtitle {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-3);
    margin: 0;
}
.frame-action-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--space-md);
}
.frame-action-card {
    padding: var(--space-md);
    background: var(--bg);
    border: 1px solid var(--border-light);
    border-left: 3px solid var(--blue);
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.frame-action-card-head {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.frame-action-card-name {
    font-weight: 600;
    color: var(--text-1);
    font-size: 0.95rem;
}
.frame-action-card-fvs {
    font-size: 0.7rem;
    font-family: var(--mono);
    color: var(--text-4);
    background: var(--bg-muted);
    padding: 0.1rem 0.35rem;
    border-radius: 3px;
}
.frame-action-card-signal {
    font-size: 0.78rem;
    color: var(--text-3);
    margin: 0;
    line-height: 1.4;
}
.frame-action-card-question {
    font-size: 0.86rem;
    color: var(--text-2);
    margin: 0;
    line-height: 1.5;
    font-style: italic;
}
.frame-action-card-buttons {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-top: auto;
    padding-top: 0.4rem;
}
.frame-action-card-buttons .fai-takeaway-action-btn,
.frame-action-card-buttons .fai-takeaway-copy-btn {
    flex: 0 0 auto;
    font-size: 0.78rem;
    padding: 0.4rem 0.7rem;
}
@media (max-width: 540px) {
    .frame-action-cards { grid-template-columns: 1fr; }
    .frame-action-card-buttons .fai-takeaway-action-btn,
    .frame-action-card-buttons .fai-takeaway-copy-btn {
        flex: 1 1 100%;
        justify-content: center;
    }
}

/* Takeaway questions panel (2026-05-04). The primary takeaway
 * surface, composed deterministically from analysis substrate
 * (V4.2 frames, coverage gaps, AI-interpret blind_spots, source-
 * network unverified claims) plus a single Take-to-LLM button
 * group. Visual language tracks the existing FVS suggestion-card
 * pattern: frame items use the suggestion-card treatment (compact,
 * surface bg, blue left-accent), section headers use the
 * suggestions-label eyebrow shape, and the lighter list items
 * (absent dimensions, concerns, unverified claims) render as
 * plain bulleted text without per-item bordered boxes. The first
 * cut boxed every list item; readability collapsed under the
 * visual cacophony. The redesign keeps a card treatment only
 * where the content carries enough weight to warrant it. */
/* Takeaway-questions panel is a SECONDARY container, not a
 * hero-class card. The 5px colored left-stripe convention in
 * this product is reserved for hero cards (Frame Check hero
 * uses it for brand-blue, trust banner uses it for trust-tier
 * color); the takeaway panel deliberately does not have one.
 *
 * The previous treatment used a 5px orange left-stripe to
 * "differentiate" from the hero, but that put the panel in
 * visual competition with the hero AND clashed with the inner
 * frame entries (which carry their own 3px blue left-accent
 * signalling "this is a Frame Check structural finding"). The
 * orange-outer + blue-inner combo read as a card fighting
 * itself. Orange also collided semantically with the trust-low
 * tier (which uses orange-bg in the trust-banner palette), so
 * the panel risked being misread as "low confidence container"
 * which is the opposite of its actual job.
 *
 * The current treatment differentiates via STRUCTURE, not color:
 * 1px border all around, soft bg-muted background, no colored
 * accent. The hero stays the loud blue-stripe primary; the
 * takeaway panel reads as a quiet container of Frame Check
 * findings (the inner blue accents on each frame entry carry
 * the brand identity at the right scope, which is per-finding
 * not per-container). Other secondary panels added later
 * (decision tracking, reading patterns, etc.) should follow
 * the same "no colored stripe" rule so the visual taxonomy
 * stays clean: stripe = hero card, no-stripe = secondary
 * container.
 *
 * Width: no max-width, no margin auto. The panel fills the
 * .results column directly, matching the .frame-check-hero
 * treatment directly above. */
.takeaway-questions-panel {
    margin: 0 0 var(--space-lg) 0;
    padding: var(--space-md) var(--space-lg);
    background: var(--bg-muted, var(--bg));
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
}

/* ── Return-visit nudge (unresolved decisions) ──────────────────
   Surfaces the user's old unresolved decisions inline above the
   takeaway when they return to /check. Visual register matches
   the takeaway-questions-panel (border-left accent, muted bg) so
   it reads as "Frame Check intentionally surfacing this", but
   the accent color is blue (not orange) to signal "your past
   work" rather than "this analysis's takeaway". Collapsible via
   <details>; default open at 1-2 entries (low real-estate cost),
   default closed at 3+ (less friction for the new analysis the
   user came here to read).
*/
.return-visit-nudge {
    margin: 0 0 var(--space-lg) 0;
    padding: 0;
    background: var(--bg-muted, var(--bg));
    border: 1px solid var(--border-light);
    border-left: 5px solid var(--blue);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

.return-visit-nudge-summary {
    cursor: pointer;
    list-style: none;
    padding: var(--space-md) var(--space-lg) var(--space-md) calc(var(--space-lg) - 4px);
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.return-visit-nudge-summary::-webkit-details-marker { display: none; }

.return-visit-nudge-summary::before {
    content: "▸ ";
    color: var(--blue);
    font-size: 0.85rem;
}

.return-visit-nudge[open] .return-visit-nudge-summary::before {
    content: "▾ ";
}

.return-visit-nudge-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-1);
    display: inline;
}

.return-visit-nudge-subtitle {
    font-size: 0.85rem;
    color: var(--text-2);
}

.return-visit-nudge-body {
    padding: 0 var(--space-lg) var(--space-md) calc(var(--space-lg) - 4px);
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    border-top: 1px solid var(--border-light);
    padding-top: var(--space-md);
}

.return-visit-decision-card {
    padding: var(--space-sm) var(--space-md);
    background: var(--bg-1, #ffffff);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-sm);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.return-visit-decision-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 0.78rem;
    color: var(--text-3);
}

.return-visit-decision-date {
    font-weight: 600;
}

.return-visit-decision-age {
    font-style: italic;
}

.return-visit-decision-section {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.return-visit-decision-section-h {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-3);
}

.return-visit-decision-text {
    margin: 0;
    font-size: 0.92rem;
    line-height: 1.5;
    color: var(--text-1);
}

.return-visit-outcome-form {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.3rem;
}

.return-visit-outcome-textarea {
    font-family: inherit;
    font-size: 0.88rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--border, #e5e7eb);
    border-radius: var(--radius-sm);
    background: var(--bg-1, #ffffff);
    color: var(--text-1);
    resize: vertical;
    min-height: 2.4rem;
}

.return-visit-outcome-actions {
    display: flex;
    gap: 0.6rem;
    align-items: center;
    flex-wrap: wrap;
}

.return-visit-outcome-submit {
    padding: 0.35rem 0.85rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: white;
    background: var(--blue);
    border: 1px solid var(--blue);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.return-visit-outcome-submit:hover:not(:disabled) {
    filter: brightness(1.05);
}

.return-visit-outcome-submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.return-visit-outcome-status {
    font-size: 0.78rem;
    color: var(--text-3);
}

.return-visit-outcome-status--ok {
    color: var(--green, #10b981);
}

.return-visit-outcome-status--error {
    color: var(--red, #ef4444);
}

html.dark .return-visit-decision-card {
    background: var(--bg-2);
    border-color: rgba(255, 255, 255, 0.08);
}

html.dark .return-visit-outcome-textarea {
    background: var(--bg-2);
    border-color: rgba(255, 255, 255, 0.1);
    color: var(--text-1);
}

html.dark .return-visit-outcome-submit {
    /* Light-mode --blue is #1d4ed8 which holds AA against white
     * text; dark-mode --blue is #60a5fa (lighter, intended for
     * accents and links, not solid-button backgrounds). White
     * text on #60a5fa fails WCAG AA contrast at ~2.5:1. Override
     * to the same #2563eb that .btn-mcp uses on dark mode (~4.7:1
     * with white). Hardcoded hex rather than a token because no
     * existing token carries this specific darker-blue role. */
    background: #2563eb;
    color: #fff;
    border-color: #2563eb;
}
.takeaway-questions-header {
    margin-bottom: var(--space-md);
}
.takeaway-questions-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 0.25rem 0;
    color: var(--text-1);
    line-height: 1.3;
}
.takeaway-questions-subtitle {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-3);
    margin: 0;
}
/* Comparison portrait. Deterministic 2-3 sentence structural
 * summary at the top of the compare takeaway panel. A quiet inset
 * (single blue left rule) rather than a callout box, since the
 * surrounding panel already provides the visual frame; nesting a
 * second bordered surface inside it reads as a heavier emphasis
 * than the portrait warrants. */
.compare-takeaway-portrait {
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--text-1);
    margin: 0;
    padding: 0.5rem 0 0.5rem 0.8rem;
    border-left: 3px solid var(--blue);
}
.takeaway-questions-section {
    margin-top: var(--space-md);
}
.takeaway-questions-section:first-of-type {
    margin-top: var(--space-sm);
}
/* Verification section header (per-claim block). Promotes section
 * identity above the description sentence that previously acted as
 * an ambiguous header. Styled to match the takeaway/reading-pattern
 * section-h eyebrow shape so the verification block reads as a peer
 * section in the page's information architecture. */
.verification-section-h {
    display: block;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-1);
    margin: 0 0 0.4rem 0;
    line-height: 1.3;
}
.takeaway-questions-section-h {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-3);
    margin: 0 0 var(--space-sm) 0;
}
.takeaway-questions-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

/* Frame items: same compact card treatment as the FVS suggestion
 * cards rendered in the Frame Check hero, so the takeaway frames
 * read as a continuation of the same visual language. */
/* Inner frame items: read as quiet entries inside the panel,
 * not as nested cards with their own surface. The previous
 * treatment had each frame on its own var(--surface) background
 * with full border, producing a "card on card" stack that read
 * as visually heavy (especially in dark mode where surface is
 * near-black on a near-black page). The simplified version drops
 * the surface fill and the right/top/bottom borders, leaving only
 * the blue left-accent that signals "this is a frame entry."
 * The result is airier and lets the panel's own background
 * provide the visual frame for the entries inside. */
.takeaway-questions-frame {
    padding: 0.5rem 0.8rem;
    background: transparent;
    border: none;
    border-left: 3px solid var(--blue, #4a90d9);
    border-radius: 0;
    margin-bottom: var(--space-sm);
}
.takeaway-questions-frame:last-child {
    margin-bottom: 0;
}
.takeaway-questions-frame-head {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.3rem;
}
.takeaway-questions-frame-name {
    font-weight: 600;
    color: var(--text-1);
    font-size: 0.9rem;
}
.takeaway-questions-frame-tier {
    font-size: 0.7rem;
    color: var(--text-4);
    background: var(--bg-muted);
    padding: 0.05rem 0.4rem;
    border-radius: 3px;
}
.takeaway-questions-frame-id {
    font-size: 0.7rem;
    font-family: var(--mono);
    color: var(--text-4);
    margin-left: auto;
}
/* When the FVS-NNN badge is an <a>, it links to the library entry
 * for that frame. The user's call (2026-05-04): drop the dedicated
 * "See the frame -->" line that repeated under every frame card,
 * make the FVS-NNN itself the clickable element. Subtle link
 * affordance: the muted-gray default reads as identifier; hover /
 * focus reveals it as a link. */
a.takeaway-questions-frame-id {
    text-decoration: none;
    transition: color 0.15s;
}
a.takeaway-questions-frame-id:hover,
a.takeaway-questions-frame-id:focus-visible {
    color: var(--blue);
    text-decoration: underline;
}
.takeaway-questions-frame-reasoning {
    font-size: 0.82rem;
    color: var(--text-2);
    margin: 0 0 0.25rem 0;
    line-height: 1.5;
}
/* Library-paragraph definition under the signal line. Same
 * visual register as the reasoning row above it (size, colour,
 * leading) so the two paragraphs read as a continuous explanation
 * rather than as separate metadata strips. Bottom margin matches
 * the reasoning row's so the question line below sits at the same
 * vertical rhythm. */
.takeaway-questions-frame-definition {
    font-size: 0.82rem;
    color: var(--text-2);
    margin: 0 0 0.35rem 0;
    line-height: 1.5;
}
.takeaway-questions-frame-question {
    font-size: 0.82rem;
    color: var(--text-2);
    margin: 0;
    line-height: 1.5;
    font-style: italic;
}

/* Plain bulleted lines for absent dimensions, concerns, unverified
 * claims. Custom dot bullet keeps the indent shallow and the
 * visual weight light: each item is one line of text, not a card.
 * The lift in readability over per-item bordered boxes is the
 * whole reason for this redesign. */
.takeaway-questions-absent-item,
.takeaway-questions-concern,
.takeaway-questions-unverified-item {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.55;
    padding: 0.2rem 0 0.2rem 1rem;
    position: relative;
}
.takeaway-questions-absent-item::before,
.takeaway-questions-concern::before,
.takeaway-questions-unverified-item::before {
    content: "•";
    position: absolute;
    left: 0;
    top: 0.2rem;
    color: var(--text-4);
}
.takeaway-questions-absent-item strong,
.takeaway-questions-unverified-item strong {
    color: var(--text-1);
    font-weight: 600;
}
.takeaway-questions-unverified-item em {
    color: var(--text-3);
    font-style: italic;
}

.takeaway-questions-actions {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-top: var(--space-md);
    padding-top: var(--space-md);
    border-top: 1px solid var(--border-light);
}
.takeaway-questions-actions .takeaway-llm-btn {
    flex: 0 0 auto;
    font-size: 0.82rem;
    padding: 0.45rem 0.85rem;
}
@media (max-width: 540px) {
    .takeaway-questions-actions .takeaway-llm-btn {
        flex: 1 1 100%;
        justify-content: center;
    }
}

/* Reading pattern panel (Frame Mirror cross-doc context, 2026-05-04).
 * Inline on /check for opt-in users with prior analyses on record.
 * Tracks the takeaway-questions visual language: panel container,
 * eyebrow section headers, plain bulleted items with subtle matching
 * highlight when a current-doc frame or missing dimension also
 * appears in the user's pattern. The match badge sits to the right
 * of the count, indicating overlap without shouting; the highlight
 * is a soft tint, not a strong color, so the reading-pattern panel
 * complements the takeaway-questions panel above it rather than
 * competing for attention. */
/* Decision tracking panel (validation-seed UX, 2026-05-04). Inline
 * below the takeaway-questions panel for users who have opted into
 * Frame Mirror. Two render states: log-decision form (when no
 * decision is on record for this analysis) or display + outcome-
 * input form (when a decision has been logged previously). The
 * data layer (mirror_decisions table + log_decision/log_outcome
 * helpers) is the longitudinal seed for the validation program;
 * the UX is deliberately low-friction (single textarea, optional)
 * to maximize the chance any data accumulates at all.
 *
 * Visual language tracks the takeaway/reading-pattern panel
 * pattern: panel container with surface bg, eyebrow section
 * headers, neutral form treatment so the affordance reads as
 * journaling rather than a structured intake form. */
.decision-panel {
    margin: 0 auto var(--space-lg);
    max-width: var(--max-w);
    padding: var(--space-md) var(--space-lg);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.decision-panel-header {
    margin-bottom: var(--space-md);
}
.decision-panel-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 0.25rem 0;
    color: var(--text-1);
    line-height: 1.3;
}
.decision-panel-subtitle {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-3);
    margin: 0;
}
.decision-panel-section {
    margin-top: var(--space-md);
}
.decision-panel-section:first-of-type {
    margin-top: var(--space-sm);
}
.decision-panel-section-h {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-3);
    margin: 0 0 var(--space-sm) 0;
}
.decision-panel-decision,
.decision-panel-outcome {
    font-size: 0.9rem;
    color: var(--text-1);
    line-height: 1.55;
    margin: 0;
    padding: 0.5rem 0.8rem;
    background: var(--bg);
    border: 1px solid var(--border-light);
    border-left: 3px solid var(--blue, #4a90d9);
    border-radius: var(--radius-sm);
}
.decision-panel-outcome {
    border-left-color: var(--accent-warn, #f59e0b);
}
.decision-panel-form {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.decision-panel-textarea {
    width: 100%;
    min-height: 4.5rem;
    padding: 0.55rem 0.7rem;
    font-family: inherit;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--text-1);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    resize: vertical;
    box-sizing: border-box;
}
.decision-panel-textarea:focus {
    outline: none;
    border-color: var(--blue, #4a90d9);
}
.decision-panel-form-actions {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.decision-panel-submit {
    flex: 0 0 auto;
    font-size: 0.82rem;
    padding: 0.45rem 0.85rem;
    background: var(--blue, #4a90d9);
    color: white;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-weight: 500;
}
.decision-panel-submit:hover,
.decision-panel-submit:focus-visible {
    filter: brightness(1.05);
}
.decision-panel-submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.decision-panel-status {
    font-size: 0.82rem;
    color: var(--text-3);
}
.decision-panel-status--success {
    color: var(--accent-success, #16a34a);
}
.decision-panel-status--error {
    color: var(--accent-error, #dc2626);
}

.reading-pattern-panel {
    margin: 0 auto var(--space-lg);
    max-width: var(--max-w);
    padding: var(--space-md) var(--space-lg);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.reading-pattern-header {
    margin-bottom: var(--space-md);
}
.reading-pattern-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 0.25rem 0;
    color: var(--text-1);
    line-height: 1.3;
}
.reading-pattern-subtitle {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-3);
    margin: 0;
}
.reading-pattern-section {
    margin-top: var(--space-md);
}
.reading-pattern-section:first-of-type {
    margin-top: var(--space-sm);
}
.reading-pattern-section-h {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-3);
    margin: 0 0 var(--space-sm) 0;
}
.reading-pattern-list {
    list-style: none;
    padding: 0;
    margin: 0;
}
.reading-pattern-item {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.55;
    padding: 0.3rem 0 0.3rem 1rem;
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
}
.reading-pattern-item::before {
    content: "•";
    position: absolute;
    left: 0;
    top: 0.3rem;
    color: var(--text-4);
}
.reading-pattern-item-label {
    color: var(--text-1);
    font-weight: 600;
}
.reading-pattern-count {
    font-size: 0.78rem;
    color: var(--text-4);
    font-variant-numeric: tabular-nums;
}
.reading-pattern-item--match::before {
    color: var(--blue);
}
.reading-pattern-match-tag {
    font-size: 0.72rem;
    color: var(--blue);
    background: rgba(74, 144, 217, 0.08);
    padding: 0.05rem 0.45rem;
    border-radius: 3px;
    margin-left: auto;
}
.reading-pattern-trend {
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.55;
    margin: 0;
}
.reading-pattern-footer {
    margin-top: var(--space-md);
    padding-top: var(--space-md);
    border-top: 1px solid var(--border-light);
}
.reading-pattern-link {
    font-size: 0.82rem;
    color: var(--blue);
    text-decoration: none;
}
.reading-pattern-link:hover,
.reading-pattern-link:focus-visible {
    text-decoration: underline;
}

.fai-lead-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
    margin-bottom: var(--space-sm);
}

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

.fai-header {
    display: flex;
    align-items: baseline;
    gap: var(--space-sm);
    margin-bottom: 0.4rem;
}

.fai-doc-type {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--blue);
    padding: 0.1rem 0.4rem;
    background: rgba(29, 78, 216, 0.06);
    border-radius: var(--radius-sm);
    text-transform: lowercase;
}

html.dark .fai-doc-type {
    background: rgba(96, 165, 250, 0.1);
}

.fai-section {
    margin-bottom: var(--space-sm);
}

.fai-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--blue);
    margin-bottom: 0.15rem;
}

.fai-text {
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text);
}

.fai-list {
    margin: 0.15rem 0 0 1rem;
    padding: 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text);
}

.fai-list li {
    margin-bottom: var(--space-xs);
}

.fai-disclaimer {
    font-size: 0.65rem;
    color: var(--text-4);
    margin-top: 0.4rem;
    font-style: italic;
}

/* Context note above annotated document */
.framing-context-note {
    font-size: 0.78rem;
    color: var(--text-2);
    font-style: italic;
    padding: 0.4rem 0 0.4rem 0.7rem;
    border-left: 2px solid var(--yellow);
    margin-bottom: var(--space-sm);
}

/* Interactivity hint for the annotated document. Tells users the marks
   are clickable so the popover system is discoverable. */
.doc-interact-hint {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.1rem 0.4rem;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--blue);
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-radius: var(--radius-sm);
    letter-spacing: 0;
}

/* Legacy summary (API backward compat, hidden in template) */
.framing-summary {
    border-left: 3px solid var(--border);
    border-radius: var(--radius);
    padding: 0.6rem 0.85rem;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-2);
}

/* Print-specific framing tweaks merged into the main @media print
   block above. Kept here only for the bar tracks which need a
   visible color when ink is grayscale. */
@media print {
    .framing-bar-track, .framing-cat-bar-track {
        background: #eee !important;
    }
    .framing-bar, .framing-cat-bar {
        background: #555 !important;
    }
}

/* ── Confidence dimensions ── */

.conf-dims, .sn-pop-dims {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.25rem 0.75rem;
    margin: 0.35rem 0;
}

.conf-dim {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.68rem;
}

.conf-dim-name {
    min-width: 28px;
    color: var(--text-4);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.conf-dim-bar-track {
    width: 40px;
    height: 4px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 2px;
    overflow: hidden;
    flex-shrink: 0;
}

html.dark .conf-dim-bar-track { background: rgba(255, 255, 255, 0.06); }

.conf-dim-bar {
    height: 100%;
    background: var(--green);
    border-radius: 2px;
    min-width: 1px;
}

.conf-dim-label {
    color: var(--text-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 80px;
}

/* ── Cross-model consensus ── */

.consensus-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--border);
    border-radius: var(--radius);
    padding: 0.6rem 0.85rem;
    margin-bottom: 0.4rem;
}

.consensus-consensus_confirmed, .consensus-confirmed { border-left-color: var(--green); }
.consensus-consensus_different, .consensus-different { border-left-color: var(--yellow); }
.consensus-models_disagree, .consensus-disagree { border-left-color: var(--orange); }
.consensus-inconclusive { border-left-color: var(--text-4); }
.consensus-partial { border-left-color: var(--blue); }

.consensus-header {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-bottom: 0.3rem;
    flex-wrap: wrap;
}

.consensus-value {
    font-family: var(--mono);
    font-weight: 700;
    font-size: 0.9rem;
    color: var(--text);
}

.consensus-subject {
    font-size: 0.78rem;
    color: var(--text-3);
}

.consensus-verdict-badge {
    font-size: 0.6rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-sm);
    margin-left: auto;
}

.cvb-consensus_confirmed, .cvb-consensus-confirmed { background: var(--green-bg); color: var(--green); }
.cvb-consensus_different, .cvb-consensus-different { background: var(--yellow-bg); color: var(--yellow); }
.cvb-models_disagree, .cvb-consensus-disagree { background: var(--orange-bg); color: var(--orange); }
.cvb-inconclusive { background: var(--gray-bg); color: var(--gray); }
.cvb-consensus-partial { background: var(--blue-bg); color: var(--blue); }

.consensus-models {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-sm);
}

.consensus-model {
    font-size: 0.78rem;
}

.consensus-model-name {
    font-weight: 600;
    color: var(--text-3);
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-bottom: 0.1rem;
}

.consensus-model-answer {
    color: var(--text-2);
    font-size: 0.78rem;
}

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

/* ── Source verification popover (click-to-see-sources) ── */

.sn-popover {
    z-index: 100;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    padding: 0.75rem 1rem;
    max-width: 360px;
    min-width: 240px;
    font-size: 0.8rem;
    animation: popIn 0.15s ease;
}

@keyframes popIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }

html.dark .sn-popover { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); }

/* ── Diff page: side-by-side comparison of two saved analyses ── */
.diff-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
}

.diff-header h1 {
    font-size: 1.5rem;
    margin-bottom: 0.3rem;
}

.diff-note {
    color: var(--text-2);
    font-size: 0.85rem;
}

.diff-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 1.5rem;
    padding: 0.85rem 1.1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.diff-summary-item {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 8rem;
}

.diff-summary-label {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-3);
}

.diff-summary-value {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--text);
    flex-wrap: wrap;
}

.diff-side-a, .diff-side-b {
    font-weight: 600;
}

.diff-arrow {
    color: var(--text-3);
    font-size: 0.8rem;
}

.diff-delta {
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.05rem 0.35rem;
    border-radius: var(--radius-sm);
    margin-left: 0.2rem;
}

.diff-delta-up {
    background: var(--green-bg);
    color: var(--green);
}

.diff-delta-down {
    background: var(--red-bg);
    color: var(--red);
}

.diff-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-lg);
}

.diff-pane {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.diff-pane-header {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    padding: 0.5rem 0.85rem;
    background: var(--gray-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 0.78rem;
}

.diff-pane-label {
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-2);
    font-size: 0.65rem;
}

.diff-pane-date {
    color: var(--text-3);
    margin-left: auto;
}

.diff-pane-link {
    color: var(--blue);
    text-decoration: none;
    font-weight: 500;
    font-size: 0.75rem;
}

.diff-pane-link:hover {
    text-decoration: underline;
}

/* Diff helper classes: replace inline styles in diff.html and
   compare_diff.html so the templates do not carry layout in
   `style="..."` attributes. The naming follows the diff- prefix
   used by the surrounding components. */

.diff-summary-models {
    margin-top: var(--space-md);
}

.diff-summary-item-wide {
    min-width: 12rem;
}

.diff-pane-summary {
    margin-top: var(--space-sm);
}

.diff-pane-portrait {
    margin-top: var(--space-sm);
}

.diff-pane-findings {
    margin-top: var(--space-md);
}

.diff-pane-subject {
    margin: 0.3rem 0;
    font-size: 0.85rem;
    color: var(--text-2);
    line-height: 1.5;
}

.diff-pane-vs {
    color: var(--text-3);
    margin: 0 var(--space-xs);
}

/* Compact verdict headline used inside diff panes (the side-by-side
   trust banners need a smaller headline than the full results page
   so two banners fit comfortably side by side). Replaces an inline
   `style="font-size: 1rem;"`. */
.verdict-headline-compact {
    font-size: 1rem;
}

/* Compact trust banner padding for diff panes. The full results
   page banner has 1.5rem 1.75rem padding; in a side-by-side layout
   that is too much vertical space. */
.trust-banner-compact {
    padding: 1rem 1.25rem;
}

@media (max-width: 900px) {
    .diff-grid {
        grid-template-columns: 1fr;
    }
}

/* Recipient-orientation strip: a small line above the hero on
 * TRUE recipient views (saved_hash AND not save_token). Tells a
 * cold visitor what they are looking at and how recent it is in
 * one glance, before they read any analysis content. The detailed
 * .saved-meta block below answers a different question (is the
 * link expiring?); this strip answers "what am I looking at?".
 *
 * Visual register intentionally quiet: small type, soft border,
 * subtle background tint via blue-bg so the strip reads as
 * supporting context (where context belongs) rather than
 * competing for the eye with the hero content right below it.
 * The link icon (chain glyph) signals "this came from
 * elsewhere", reinforcing the recipient framing. */
.saved-recipient-strip {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin-bottom: var(--space-md);
    padding: 0.45rem 0.85rem;
    background: var(--blue-bg);
    border: 1px solid var(--blue-border);
    border-radius: var(--radius-sm);
    font-size: 0.8rem;
    color: var(--text-2);
    line-height: 1.4;
}

.saved-recipient-strip-icon {
    font-size: 0.95rem;
    line-height: 1;
    flex-shrink: 0;
    color: var(--blue);
}

.saved-recipient-strip-text {
    flex: 1;
    min-width: 0;
}

.saved-recipient-strip time {
    font-weight: 600;
    color: var(--text);
    border-bottom: 1px dotted var(--text-3);
    cursor: help;
}

/* Saved analysis metadata footer: appears on /saved/{hash} pages
   to tell the user when the analysis was saved and how many days
   remain before the link expires. The warn variant signals when
   the link is about to expire so the user can re-save if needed. */
.saved-meta {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-top: var(--space-lg);
    margin-bottom: 0.4rem;
    padding: 0.55rem 0.85rem;
    background: var(--gray-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 0.78rem;
    color: var(--text-2);
}

.saved-meta-icon {
    font-size: 0.9rem;
    color: var(--green);
    flex-shrink: 0;
}

.saved-meta time {
    font-weight: 600;
    color: var(--text);
    border-bottom: 1px dotted var(--text-3);
    cursor: help;
}

.saved-meta-warn {
    background: var(--yellow-bg);
    border-color: var(--yellow-border);
}

.saved-meta-warn .saved-meta-icon {
    color: var(--yellow);
}

/* Save status: shown after the user clicks Save or Copy share link.
   Contains either the saved URL or an error message. role="status"
   and aria-live="polite" so screen readers announce the outcome.

   The card has two visual states:
     - .save-status-success: green border, code-style URL box
     - .save-status-error:   red border, plain text
   The default (no modifier) is gray so the card never looks
   "complete" when it carries a transient state. */
.save-status {
    margin-top: 0.6rem;
    padding: 0.7rem 0.9rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 0.82rem;
    color: var(--text);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.save-status-success {
    background: var(--green-bg);
    border-color: var(--green-border);
}

.save-status-error {
    background: var(--red-bg);
    border-color: var(--red-border);
    color: var(--red);
}

.save-status-label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--green);
}

.save-status-url {
    display: block;
    font-family: var(--mono);
    font-size: 0.78rem;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--green-border);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.55rem;
    user-select: all;
    word-break: break-all;
    line-height: 1.4;
}

html.dark .save-status-url {
    background: var(--bg);
    border-color: var(--green-border);
}

/* Post-save MCP mini-CTA: appended to .save-status after a
 * successful save. Inherits .btn .btn-mcp styling (brand-blue
 * background, white text) so it visually matches the standing
 * MCP upgrade card's CTA below; the eye reads the two surfaces
 * as the same action class. Sized smaller and aligned to start
 * inside the .save-status flex column so it sits below the
 * "Saved. Shareable link:" label and the URL chip without
 * dominating the success state. The inline-flex with align-self
 * gives it a content-width pill rather than a row-spanning
 * button. */
.save-status-mcp-cta {
    align-self: flex-start;
    margin-top: 0.3rem;
    padding: 0.45rem 0.85rem;
    font-size: 0.82rem;
    font-weight: 600;
    line-height: 1.3;
    text-decoration: none;
    border-radius: var(--radius-sm);
}

/* Legacy <a> support for any save-status that still renders as
   plain anchor (e.g., older cached pages). The new flow uses
   .save-status-url + a rewired button. */
.save-status a {
    color: var(--green);
    text-decoration: none;
    font-weight: 600;
    word-break: break-all;
}

.save-status a:hover {
    text-decoration: underline;
}

/* Mobile modal-style popover for narrow viewports.
   The JS positioning algorithm switches to fixed-position centered
   layout below 480px wide because anchored popovers cannot fit on
   small phones without overflowing the viewport. */
.sn-popover-mobile {
    z-index: 1001;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.24);
    /* The JS sets max-width / max-height to viewport-aware values */
}

html.dark .sn-popover-mobile {
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}

/* Backdrop sits behind the mobile popover and gives the user a
   tappable area outside the popover for dismissal. The existing
   document.click handler closes the popover; the backdrop just
   provides a visible target. */
.sn-popover-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 1000;
    animation: fadeIn 0.15s ease;
}

html.dark .sn-popover-backdrop {
    background: rgba(0, 0, 0, 0.65);
}

.sn-pop-header {
    font-weight: 700;
    font-size: 0.85rem;
    margin-bottom: var(--space-sm);
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--border-light);
}

.pop-verified { color: var(--green); }
.pop-contradicted { color: var(--red); }
.pop-unverified { color: var(--text-3); }

.sn-pop-source {
    margin-bottom: 0.35rem;
    font-size: 0.78rem;
}

.sn-pop-source strong {
    color: var(--text);
}

.sn-pop-diff {
    font-size: 0.7rem;
    color: var(--text-4);
}

.sn-pop-link {
    color: var(--blue);
    text-decoration: none;
    font-size: 0.75rem;
}

.sn-pop-link:hover { text-decoration: underline; }

.sn-pop-context {
    font-size: 0.72rem;
    color: var(--text-3);
    font-style: italic;
    margin: 0.15rem 0 0;
    line-height: 1.3;
}

.sn-pop-none {
    color: var(--text-4);
    font-style: italic;
    font-size: 0.78rem;
}

/* ── Loading overlay ── */

.loading-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    animation: fadeIn 0.3s ease;
}

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

html.dark .loading-overlay { background: rgba(0, 0, 0, 0.7); }

.loading-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    padding: 2rem 2.5rem;
    text-align: center;
    max-width: 380px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

/*
   Frame-shaped loader: a FIXED rounded square whose border is traced
   by a bright segment that moves around the perimeter, like a chase-
   light along a picture frame. Echoes the Frame Check logo (rounded
   square around a checkmark). The square geometry does not rotate;
   only the angular position of the bright segment moves, so the
   frame reads as stable while the motion communicates "working."

   Technique:
   - The visible element carries a dim, always-on border (always
     shows the frame outline).
   - ::before pseudo carries a conic-gradient with a bright segment.
     mask-composite: exclude hollows out the center so only a
     "border ring" of the gradient renders.
   - @property --loading-angle registers the conic-gradient's starting
     angle as an animatable <angle> value. Without @property, browsers
     animate unregistered custom properties as strings, producing
     discrete jumps instead of smooth sweeps.
   - animation: loader-sweep moves --loading-angle from 0deg to 360deg
     in a linear loop, sweeping the bright segment around the border.
   - prefers-reduced-motion: animation disabled; the dim border still
     tells the user the element is a loader.

   Applied uniformly across all four spinners on the page:
   .loading-spinner, .ai-skeleton-spinner,
   .verify-card.checking .verify-icon, .btn-reframe-spinner. Each
   has its own size but the same conic-mask + sweep pattern.
*/

@property --loading-angle {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}

@keyframes loader-sweep { to { --loading-angle: 360deg; } }

.loading-spinner {
    width: 36px;
    height: 36px;
    border: 2px solid var(--border);
    border-radius: 6px;
    position: relative;
    margin: 0 auto 1.25rem;
}

/* Flowing-border loader: shared ::before rule.
 *
 * The ::before pseudo-element is what carries the rotating conic
 * gradient that produces the chase-light effect on all four spinners
 * (loading-spinner, ai-skeleton-spinner, verify-icon, btn-reframe-
 * spinner). Consolidating here avoids 80 lines of duplication.
 *
 * Per-spinner tweaks (border-radius, inset/padding thickness) are
 * picked up via `border-radius: inherit` and CSS custom properties
 * (--loader-inset, --loader-thickness) with sensible defaults that
 * match the 1.5px border width of the smaller spinners. The 36px
 * .loading-spinner overrides --loader-inset: -2px and
 * --loader-thickness: 2px to match its 2px border. */
.loading-spinner::before,
.ai-skeleton-spinner::before,
.verify-card.checking .verify-icon::before,
.btn-reframe-spinner::before {
    content: '';
    position: absolute;
    inset: var(--loader-inset, -1.5px);
    padding: var(--loader-thickness, 1.5px);
    border-radius: inherit;
    background: conic-gradient(
        from var(--loading-angle, 0deg),
        transparent 0deg 240deg,
        var(--blue) 310deg,
        transparent 360deg
    );
    -webkit-mask: linear-gradient(#fff 0 0) content-box,
                  linear-gradient(#fff 0 0);
    mask: linear-gradient(#fff 0 0) content-box,
          linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    animation: loader-sweep 1.4s linear infinite;
    pointer-events: none;
}

.loading-spinner {
    --loader-inset: -2px;
    --loader-thickness: 2px;
}

.loading-stages {
    text-align: left;
    margin: 0 0 var(--space-lg);
    padding: 0;
    list-style: none;
}

.loading-stage {
    font-size: 0.82rem;
    color: var(--text-4);
    padding: 0.3rem 0 0.3rem 1.5rem;
    position: relative;
    transition: color 0.3s;
}

.loading-stage::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 2px solid var(--border);
    background: transparent;
    transition: all 0.3s;
}

.loading-stage.loading-active {
    color: var(--text);
    font-weight: 600;
}

.loading-stage.loading-active::before {
    border-color: var(--blue);
    background: var(--blue);
    box-shadow: 0 0 8px rgba(29, 78, 216, 0.3);
}

.loading-stage.loading-done {
    color: var(--text-3);
}

.loading-stage.loading-done::before {
    border-color: var(--green);
    background: var(--green);
}

.loading-note {
    font-size: 0.72rem;
    color: var(--text-4);
    margin-top: var(--space-sm);
}

/* ── Responsive ── */

@media (max-width: 640px) {
    html { font-size: 14px; }
    main { padding: 1.5rem 1rem; }
    .hero h1 { font-size: 1.3rem; }
    .instruments-grid { grid-template-columns: 1fr; gap: 1.5rem; }
    .instruments-title { font-size: 1rem; }
    .error-card { padding: 1.25rem 1.25rem; }
    .error-card-headline { font-size: 1.05rem; }
    .error-card-footer { flex-direction: column; align-items: flex-start; gap: 0.3rem; }
    .error-card-footer-value { word-break: break-all; }
    .section-summary { flex-direction: column; align-items: flex-start; gap: 0.3rem; }
    .section-one-liner { white-space: normal; }
    .metrics-row { flex-direction: column; }
    .flagged-item { font-size: 0.78rem; }
    .chain-step { flex-direction: column; align-items: flex-start; gap: 0.2rem; }
    .chain-stage { min-width: auto; }
    .chain-detail { min-width: auto; }
    .evidence-header { flex-direction: column; align-items: flex-start; }
    .pipeline-stats { flex-direction: column; gap: 0.3rem; }
    .verify-card { flex-direction: column; }
    .verify-card-status { width: 100%; min-height: 36px; flex-direction: row; gap: 0.5rem; padding: 0.5rem; }
    .framing-grid { grid-template-columns: 1fr; }
    .about-methods { gap: 0.5rem; }
    .about-cta { flex-direction: column; }
    /* .verify-summary removed (was: gap: 0.3rem at <=640px) */
    .conf-dims { gap: 0.3rem; }
    .sn-header { flex-direction: column; align-items: flex-start; gap: 0.25rem; }
    .gfp-legend { flex-wrap: wrap; gap: 0.5rem; }
    .model-metrics { flex-wrap: wrap; }
    /* Document Structure rows: collapse the 3-column grid to a single
     * stacked column on phones so the detail text has room to breathe.
     * Previous rule used flex-direction: column which had no effect on
     * the grid element (stale from before the grid refactor). */
    .framing-structure-row {
        grid-template-columns: 1fr;
        grid-row-gap: 0.2rem;
    }
    .example-framing-row { gap: 0.3rem; }
    .nav-links { gap: 0.6rem; }
    .nav-link { font-size: 0.75rem; }
    .nav-active::after { bottom: -0.35rem; }

    /* Larger touch targets and looser line-height for the annotated
       document so finger taps reliably hit the highlighted marks.
       Inline padding cannot grow without breaking flow, so we expand
       vertical breathing room via line-height instead. */
    .annotated-document { line-height: 1.85; padding: 1rem 1.1rem; }
    .annotated-document mark { padding: 0.15rem 0.35rem; }

    /* Mobile-specific tightening. The caveat is now a collapsible
       disclosure without the prior yellow-callout padding; mobile
       just tightens the inner list and footnote text. */
    .results-caveat-list li { font-size: 0.74rem; padding-left: 1rem; }
    .results-caveat-footnote { font-size: 0.7rem; }

    /* Diff summary items: drop the wide-min on mobile so the per-
       model deltas stack one-per-row instead of overflowing. */
    .diff-summary-item-wide { min-width: auto; }

    /* Footer top row: stack the brand and the link nav vertically
       on narrow viewports so neither overflows. flex-wrap already
       handles this when there is enough room; explicit
       flex-direction: column makes the stacking deterministic. */
    .footer-top { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
    .footer-links { gap: 0.85rem; }

    /* Example document fade: keep the fade visible but slightly
       shallower since the wrap height is also shallower at mobile. */
    .example-doc-wrap .annotated-document { max-height: 320px; }
    .example-doc-fade { height: 50px; }
}

/* ── Tablet breakpoint (641-900px) ──
   The jump from 640px mobile to 860px desktop left tablets in a
   no-mans-land: desktop layout squeezed into iPad-sized viewports
   with cramped cards and two-column grids that barely fit. This
   block adjusts the specific elements that suffer most at
   641-900px while keeping the mobile rules at <=640px untouched. */
@media (min-width: 641px) and (max-width: 900px) {
    html { font-size: 14.5px; }
    main { padding: 2rem 1.25rem; }

    /* Compare page: stack the two model cards vertically instead of
       squeezing them side-by-side at <~450px each. Each card gets
       full width for its metrics and framing panels. */
    .compare-models {
        grid-template-columns: 1fr;
        gap: var(--space-lg);
    }

    /* Framing grid: keep two columns but tighten the gap */
    .framing-grid {
        gap: var(--space-sm);
    }

    /* Consensus model cards: stack at tablet width too */
    .consensus-models {
        grid-template-columns: 1fr;
        gap: var(--space-sm);
    }

    /* Instruments panel: keep three columns at tablet width but
       tighten the gap so each column has more room. */
    .instruments-grid {
        gap: var(--space-lg);
    }

    /* Section cards: slightly tighter padding */
    .section-card {
        padding: 0.75rem 1rem;
    }

    /* Framing temporal bars: slightly tighter label column */
    .framing-bar-row .framing-bar-label {
        min-width: 3rem;
    }

    /* Compare banner: reduce padding */
    .compare-banner {
        padding: 1rem 1.25rem;
    }
}

/* Stability number tags */
.stability-numbers {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.4rem;
}
.stab-tag {
    font-size: 0.75rem;
    font-family: 'JetBrains Mono', monospace;
    padding: 0.15rem 0.4rem;
    border-radius: var(--radius-sm);
    font-weight: 500;
}
.stab-stable {
    background: var(--bg-verified, #e8f5e9);
    color: var(--text-verified, #2e7d32);
}
.stab-changed {
    background: var(--bg-warning, #fff3e0);
    color: var(--text-warning, #e65100);
}
.stability-model {
    margin-bottom: 0.6rem;
}
.stability-stable {
    color: var(--text-verified, #2e7d32);
    font-weight: 600;
}
.stability-changed {
    color: var(--text-warning, #e65100);
    font-weight: 600;
}

/* Verified claims collapsible group */
.sn-verified-group {
    margin-top: 0.3rem;
}

.sn-verified-toggle {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.4rem 0.6rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--green);
    cursor: pointer;
    border-radius: var(--radius);
    background: var(--green-bg);
    margin-bottom: 0.4rem;
    list-style: none;
}

.sn-verified-toggle::-webkit-details-marker { display: none; }

/* Unresolved + projection groups. Same shape as the verified-group
 * toggle but with palette matching the bucket's semantic color
 * (gray for "not found", blue for "projection"). Keeps the reader's
 * eye parsing the Verification details as a sequence of uniform
 * drill-downs, one per verdict bucket. */
.sn-unresolved-group,
.sn-projection-group {
    margin-top: 0.3rem;
}

.sn-unresolved-toggle,
.sn-projection-toggle {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.4rem 0.6rem;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    border-radius: var(--radius);
    margin-bottom: 0.4rem;
    list-style: none;
}

.sn-unresolved-toggle {
    color: var(--text-3);
    background: var(--gray-bg);
}

.sn-projection-toggle {
    color: var(--blue);
    background: var(--blue-bg);
}

.sn-unresolved-toggle::-webkit-details-marker,
.sn-projection-toggle::-webkit-details-marker { display: none; }

/* Per-claim reason under an unresolved card. Italicized, dimmer, and
 * indented so the reason reads as metadata about the verdict, not as
 * another claim sentence. */
.sn-unresolved-reason {
    font-size: 0.75rem;
    color: var(--text-3);
    font-style: italic;
    margin: 0.35rem 0 0 0;
    line-height: 1.5;
    padding-left: 0.7rem;
    border-left: 2px solid var(--border-light);
}

.sn-projection-caveat {
    font-size: 0.78rem;
    color: var(--text-2);
    font-style: italic;
    margin: 0 0 0.5rem 0;
    padding: 0.4rem 0 0.4rem 0.7rem;
    border-left: 2px solid var(--blue);
    line-height: 1.5;
}

html.dark .sn-projection-caveat { color: var(--text-2); border-left-color: var(--blue); }

.sn-badge-unresolved { background: var(--gray-bg); color: var(--text-3); }

.sn-toggle-hint {
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--text-4);
}

/* (verify-summary / .vs-* pill rules removed: the trust banner
   at the top of results.html already shows the same counts as
   a three-segment meter with verified / contradicted / unchecked
   labels. The pills duplicated that information one screen below
   and competed with the actual unique value of the section
   below, per-claim source data.) */

/* Verification sub-headers (within unified section) */
.verification-sub-header {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-2);
    margin: 1.2rem 0 0.3rem;
    padding-top: 0.8rem;
    border-top: 1px solid var(--border);
}

/* ── Frame Mirror ── */

/* Mirror container widths match the main app content width (860px
   via the <main> container) so the Mirror reads as part of the
   same application, not a narrower secondary panel. The opt-in
   box keeps its own card chrome (border, padding) but takes the
   full content width; long-form prose inside is constrained by
   .mirror-prose-column, which targets an ~640px reading measure
   so the explanatory copy still reads comfortably inside the
   wider card. */
.mirror-optin {
    margin: 2rem auto;
    padding: 1.75rem 2rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.mirror-prose-column {
    max-width: 640px;
}
/* Prose inside the opt-in card stays at a comfortable reading
   measure even though the card itself spans the full content
   width. Headings, paragraphs, and the disclosure microcopy all
   share the measure so the opt-in reads as a considered page,
   not a stretched one. The preview panel and the enable button
   are intentionally exempt so they occupy the full width of the
   card and feel like genuine CTAs. */
.mirror-optin > h2,
.mirror-optin > p,
.mirror-optin > .mirror-disclosure {
    max-width: 640px;
}

.mirror-disclosure {
    font-size: 0.85rem;
    color: var(--text-3);
    font-style: italic;
    margin: 1rem 0;
}

.mirror-summary {
    margin: 0 auto;
}

.mirror-stats {
    display: flex;
    gap: 2rem;
    margin: 1.5rem 0;
}

.mirror-stat {
    display: flex;
    flex-direction: column;
}

.mirror-stat-value {
    font-size: 2rem;
    font-weight: 700;
    color: var(--text-1);
    line-height: 1.2;
}

.mirror-stat-label {
    font-size: 0.85rem;
    color: var(--text-3);
}

.mirror-note {
    font-size: 0.85rem;
    color: var(--text-3);
    margin-bottom: 0.75rem;
}

.mirror-frames, .mirror-coverage {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-bottom: 2rem;
}

.mirror-frame-row, .mirror-coverage-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.85rem;
}

.mirror-frame-id, .mirror-coverage-cat {
    min-width: 80px;
    font-weight: 600;
    color: var(--text-1);
}

.mirror-frame-bar, .mirror-coverage-bar {
    height: 8px;
    background: var(--blue, #4a90d9);
    border-radius: 4px;
    min-width: 4px;
}

.mirror-frame-count, .mirror-coverage-pct {
    font-size: 0.78rem;
    color: var(--text-3);
    white-space: nowrap;
}

.mirror-voice {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 2rem;
}

.mirror-voice-row {
    display: flex;
    gap: 0.5rem;
    font-size: 0.85rem;
}

.mirror-voice-type {
    font-weight: 600;
    color: var(--text-1);
    text-transform: capitalize;
}

.mirror-voice-pct {
    color: var(--text-3);
}

.mirror-sessions {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1rem;
}

.mirror-session-card {
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.75rem 1rem;
}

.mirror-session-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 0.25rem;
}

.mirror-session-date {
    font-weight: 600;
    font-size: 0.9rem;
}

.mirror-session-meta {
    font-size: 0.8rem;
    color: var(--text-3);
}

.mirror-session-body {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--text-2);
}

.mirror-session-coverage {
    font-weight: 500;
}

.mirror-session-missing,
.mirror-session-frames {
    color: var(--text-3);
}

/* ── Decisions and outcomes section ── */
/* Phase 3 of decision-outcome tracking. The user-facing reflection
 * surface for the longitudinal data the takeaway-panel decision
 * input collects. Visual treatment matches the mirror-session-card
 * pattern above so /mirror reads as a unified personal record:
 * sessions describe what was analyzed, decisions describe what the
 * user chose to do about it. Outcome rows get a subtle amber accent
 * to signal the closed loop (decision -> outcome -> reflection). */
.mirror-decisions {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1rem;
}
.mirror-decisions-empty {
    margin-top: 1rem;
    padding: 0.85rem 1rem;
    background: var(--bg);
    border: 1px dashed var(--border);
    border-radius: 6px;
    font-size: 0.88rem;
    color: var(--text-3);
}
.mirror-decisions-empty p {
    margin: 0;
    line-height: 1.55;
}
.mirror-decision-card {
    border: 1px solid var(--border);
    border-left: 3px solid var(--blue, #4a90d9);
    border-radius: 6px;
    padding: 0.75rem 1rem;
    background: var(--surface);
}
.mirror-decision-card--has-outcome {
    border-left-color: var(--accent-warn, #f59e0b);
}
.mirror-decision-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 0.5rem;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.mirror-decision-date {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--text-2);
    font-variant-numeric: tabular-nums;
}
.mirror-decision-header-actions {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.mirror-decision-link {
    font-size: 0.78rem;
    color: var(--blue);
    text-decoration: none;
}
.mirror-decision-link:hover,
.mirror-decision-link:focus-visible {
    text-decoration: underline;
}
/* Per-decision delete button. Visually quiet by default (muted
 * text-4 color, transparent border) so it does not compete with
 * the date or saved-link affordances; on hover it reveals as a
 * destructive action via the error accent color. Confirm prompt
 * in JS guards against accidental clicks. */
.mirror-decision-delete {
    font-size: 0.75rem;
    background: none;
    border: 1px solid transparent;
    color: var(--text-4);
    padding: 0.1rem 0.4rem;
    border-radius: 3px;
    cursor: pointer;
    font-family: inherit;
    transition: color 0.15s, border-color 0.15s;
}
.mirror-decision-delete:hover,
.mirror-decision-delete:focus-visible {
    color: var(--accent-error, #dc2626);
    border-color: currentColor;
}
.mirror-decision-delete:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.mirror-decision-body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.mirror-decision-section-h {
    display: block;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-3);
    margin-bottom: 0.2rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.mirror-decision-text,
.mirror-decision-outcome {
    font-size: 0.9rem;
    color: var(--text-1);
    line-height: 1.55;
    margin: 0;
}
.mirror-decision-outcome {
    color: var(--text-2);
}

/* Outcome-log form on /mirror (parity with /check return-visit
 * nudge): renders inside a decision card whose outcome_text is
 * empty. Same JSON body as the return-visit form posts to the same
 * /api/mirror/outcome endpoint. Visual register is quieter than
 * the nudge (no separate panel chrome since it lives inside the
 * decision card) but uses the same submit-button styling so the
 * affordance reads consistently across surfaces. */
.mirror-outcome-form {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.4rem;
    padding-top: 0.5rem;
    border-top: 1px dashed var(--border-light);
}
.mirror-outcome-textarea {
    font-family: inherit;
    font-size: 0.88rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--border, #e5e7eb);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-1);
    resize: vertical;
    min-height: 2.4rem;
}
.mirror-outcome-actions {
    display: flex;
    gap: 0.6rem;
    align-items: center;
    flex-wrap: wrap;
}
.mirror-outcome-submit {
    padding: 0.35rem 0.85rem;
    font-size: 0.82rem;
    font-weight: 600;
    color: white;
    background: var(--blue);
    border: 1px solid var(--blue);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: inherit;
}
.mirror-outcome-submit:hover:not(:disabled) {
    filter: brightness(1.05);
}
.mirror-outcome-submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.mirror-outcome-status {
    font-size: 0.78rem;
    color: var(--text-3);
}
.mirror-outcome-status--ok {
    color: var(--green, #10b981);
}
.mirror-outcome-status--error {
    color: var(--red, #ef4444);
}
html.dark .mirror-outcome-submit {
    /* Same dark-mode WCAG fix as .return-visit-outcome-submit and
     * .btn-mcp: dark-mode --blue is too light for white text at AA. */
    background: #2563eb;
    color: #fff;
    border-color: #2563eb;
}

/* ── Mirror falsifications list ─────────────────────────────────
   Surfaces the user's flagged frame disagreements with per-row
   delete. Visual register matches the decision cards (same outer
   container shape + delete affordance pattern) but uses a different
   border-left accent (gray, neutral) since flags are methodology
   feedback rather than reflection. Flag rows are dense: date +
   FVS id + flag_type in one header line, frame name + why_text in
   the body. Why_text is at most 500 chars (schema cap), so always
   one short paragraph. */
.mirror-falsifications {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.mirror-falsification-card {
    border: 1px solid var(--border);
    border-left: 3px solid var(--text-4, #6b7280);
    border-radius: 6px;
    padding: 0.65rem 1rem;
    background: var(--surface);
}
.mirror-falsification-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
}
.mirror-falsification-date {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--text-2);
    font-variant-numeric: tabular-nums;
}
.mirror-falsification-meta {
    font-size: 0.78rem;
    color: var(--text-3);
    display: inline-flex;
    gap: 0.4rem;
    align-items: baseline;
}
.mirror-falsification-fvs {
    font-weight: 600;
    color: var(--blue);
    font-variant-numeric: tabular-nums;
}
.mirror-falsification-divider {
    color: var(--text-4);
}
.mirror-falsification-type {
    font-style: italic;
}
.mirror-falsification-delete {
    font-size: 0.75rem;
    background: none;
    border: 1px solid transparent;
    color: var(--text-4);
    padding: 0.1rem 0.4rem;
    border-radius: 3px;
    cursor: pointer;
    font-family: inherit;
    transition: color 0.15s, border-color 0.15s;
}
.mirror-falsification-delete:hover,
.mirror-falsification-delete:focus-visible {
    color: var(--accent-error, #dc2626);
    border-color: currentColor;
}
.mirror-falsification-delete:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.mirror-falsification-body {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.mirror-falsification-frame-name {
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-2);
    font-style: italic;
}
.mirror-falsification-why {
    margin: 0;
    font-size: 0.9rem;
    color: var(--text-1);
    line-height: 1.55;
}
.mirror-falsifications-empty {
    color: var(--text-3);
    font-size: 0.88rem;
    line-height: 1.55;
}

/* ── L2 Reframe ── */

.btn-reframe {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.6rem;
    padding: 0.4rem 0.9rem;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    background: var(--surface);
    border: 1px solid var(--blue);
    color: var(--blue);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

.btn-reframe::after {
    content: "\2192";  /* rightward arrow */
    font-size: 0.85em;
    font-weight: 400;
    transition: transform 0.15s ease;
}

.btn-reframe:hover,
.btn-reframe:focus-visible {
    background: var(--blue);
    color: #ffffff;
    box-shadow: 0 2px 6px rgba(74, 144, 217, 0.25);
    outline: none;
}

.btn-reframe:hover::after,
.btn-reframe:focus-visible::after {
    transform: translateX(2px);
}

.btn-reframe:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}


.btn-reframe:disabled {
    opacity: 0.6;
    cursor: wait;
}

.btn-reframe-loading {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/* btn-reframe loading spinner uses the flowing-border loader pattern.
 * At 12px the sweep is subtle but preserves the brand metaphor. */
.btn-reframe-spinner {
    width: 12px;
    height: 12px;
    border: 1.5px solid var(--border);
    border-radius: 2px;
    display: inline-block;
    position: relative;
}

/* ::before rule handled by the shared flowing-border block below. */


.reframe-card {
    background: var(--surface);
    border: 2px solid var(--blue);
    border-radius: 8px;
    padding: 1rem 1.25rem;
    margin-bottom: 1.5rem;
}

.reframe-header {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.reframe-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--blue);
}

.reframe-source {
    font-size: 0.85rem;
    color: var(--text-3);
}

.reframe-narrative {
    font-size: 0.95rem;
    line-height: 1.5;
    color: var(--text-1);
    margin-bottom: 0.75rem;
}

.reframe-delta {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin-bottom: 0.75rem;
}

.reframe-delta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.85rem;
    align-items: baseline;
}

.reframe-delta-label {
    font-weight: 600;
    min-width: 6rem;
    color: var(--text-2);
}

.reframe-delta-value {
    color: var(--text-2);
}

.reframe-delta-gain {
    color: #16a34a;
    font-weight: 500;
}

.reframe-delta-loss {
    color: #dc2626;
    font-weight: 500;
}

.reframe-text-toggle {
    margin-top: 0.5rem;
    font-size: 0.85rem;
}

.reframe-text-toggle summary {
    cursor: pointer;
    color: var(--blue);
}

.reframe-text {
    margin-top: 0.5rem;
    padding: 0.75rem;
    background: var(--bg);
    border-radius: 4px;
    font-size: 0.85rem;
    line-height: 1.5;
    white-space: pre-wrap;
}

.reframe-disclaimer {
    margin-top: 0.5rem;
    font-size: 0.75rem;
    color: var(--text-4);
    font-style: italic;
}

/* Action row at the bottom of the reframe card. Closes the prior
 * dead-end where the overlay populated and stopped. Buttons are
 * standard .btn variants so they pick up the global button styles
 * and dark-mode rules without per-element overrides. The top
 * border separates the actions from the disclaimer above so the
 * disclaimer stays the visual end of the data and the actions
 * read as the next-step row. */
.reframe-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--border-light);
}

/* ── Compare Preview ── */

.compare-preview {
    margin-bottom: 1.5rem;
}

/* Inline hint shown between the preview and the form when the form
 * is empty and example pairs are available. Anchors to the example
 * pairs section so a user without docs finds them without having to
 * scroll past the empty form first. Styled as a subtle secondary
 * message, not a CTA. */
.compare-examples-hint {
    font-size: 0.85rem;
    color: var(--text-3);
    margin: 0 0 1rem 0;
}

.compare-examples-hint a {
    color: var(--blue);
    text-decoration: none;
    border-bottom: 1px dotted var(--blue);
    padding-bottom: 1px;
}

.compare-examples-hint a:hover,
.compare-examples-hint a:focus-visible {
    border-bottom-style: solid;
    outline: none;
}

.compare-examples-hint a:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}

.compare-preview-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.75rem 1rem;
}

.compare-preview-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
}

.compare-preview-items {
    margin-top: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.compare-preview-item {
    font-size: 0.85rem;
    color: var(--text-2);
    padding-left: 1rem;
    position: relative;
}

.compare-preview-item::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.45em;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--blue);
}

.mirror-session-link {
    display: inline-block;
    margin-top: 0.4rem;
    font-size: 0.8rem;
    color: var(--blue);
}

.mirror-preview {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin: 1rem 0;
    opacity: 0.75;
}

.mirror-preview-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.mirror-preview-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0.5rem 0;
}

.mirror-preview-metric {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--text-2);
}

.mirror-preview-session {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.8rem;
    color: var(--text-3);
    padding-top: 0.5rem;
    border-top: 1px solid var(--border);
    margin-top: 0.5rem;
}

.mirror-preview-date {
    font-weight: 600;
}

.mirror-actions {
    display: flex;
    gap: 1rem;
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

.mirror-inline-error {
    margin-top: 0.75rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
    color: var(--red);
    background: rgba(185, 28, 28, 0.08);
    border-left: 3px solid var(--red);
    border-radius: var(--radius-sm);
}

.btn-danger {
    background: var(--red);
    color: #ffffff;
    border: none;
    padding: 0.5rem 1rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 600;
    transition: background 0.15s ease, box-shadow 0.15s ease;
}

.btn-danger:hover {
    filter: brightness(1.05);
    box-shadow: 0 2px 6px rgba(185, 28, 28, 0.25);
}

.btn-danger:focus-visible {
    outline: 2px solid var(--red);
    outline-offset: 2px;
}

.mirror-empty {
    margin: 2rem auto;
    text-align: center;
    padding: 1.75rem 2rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.mirror-prompt {
    margin: 2rem auto;
    padding: 1rem 1.25rem;
    background: var(--blue-bg, #f0f4ff);
    border: 1px solid var(--blue, #4a90d9);
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
}

.mirror-prompt-text {
    margin: 0;
    color: var(--text-2);
    line-height: 1.5;
}

.mirror-trends {
    margin-bottom: 2rem;
}

.mirror-trend-period {
    display: flex;
    justify-content: space-between;
    padding: 0.3rem 0;
    font-size: 0.85rem;
}

.mirror-trend-label {
    font-weight: 600;
    color: var(--text-1);
}

.mirror-trend-count {
    color: var(--text-3);
}

.mirror-trend-comparison {
    margin-top: 0.75rem;
}

.mirror-trend-sublabel {
    display: block;
    font-size: 0.78rem;
    color: var(--text-3);
    margin-bottom: 0.4rem;
}

.mirror-trend-item {
    display: block;
    font-size: 0.85rem;
    padding: 0.2rem 0;
}

.mirror-trend-arrow {
    font-size: 0.75rem;
    font-weight: 600;
    margin-left: 0.5rem;
}

.mirror-trend-up {
    color: #dc3545;
}

.mirror-trend-down {
    color: #28a745;
}

/* Reduced-motion override. Respects the OS-level user preference for
   reduced motion (vestibular-disorder accommodation). Applied as a
   tail override so individual components keep their animations for
   the default case; when the preference is set, animations and
   transitions collapse to near-zero duration so the visual result is
   instantaneous without breaking layout or :hover states.
   scroll-behavior is also reset so programmatic scrollIntoView jumps
   instead of smooth-scrolls. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    /* Spinners are functional feedback (the user needs to know
       processing is happening), not decorative motion. Exempt
       them from the blanket kill. A slower sweep (2.8s vs 1.4s)
       is less jarring while still communicating "working."
       The new flowing-border loaders carry animation on ::before
       pseudo-elements, so the override targets those explicitly. */
    .loading-spinner::before,
    .ai-skeleton-spinner::before,
    .verify-card.checking .verify-icon::before,
    .btn-reframe-spinner::before {
        animation: loader-sweep 2.8s linear infinite !important;
    }
}

/* ================================================================
 * V4.2 frame-verdict panel (Phase 4 item 5)
 * ================================================================
 * The V4.2 panel renders between Layer A structural suggestions and
 * the numerical-claim trust verdict. Four possible states:
 *
 *   1. Success: fired-frame cards with tier badges + honest_limit,
 *      collapsed not-fired summary, engine provenance footer.
 *   2. Fallback: Layer A placeholder when orchestrator degraded
 *      gracefully (budget, circuit, API unavailable).
 *   3. Blocked: sanitized user-facing error from engine exceptions
 *      (prompt injection, document too complex, etc.).
 *   4. Absent: v4_2_result is None; the partial is not rendered.
 *
 * Tier colors reuse the existing trust-verdict palette so the
 * semantic meaning matches across the page: green = strong,
 * amber = moderate, red = weak/construct-honesty caveat,
 * gray = unmeasured.
 */

.v4_2-panel {
    margin: var(--space-lg) 0;
    padding: var(--space-md);
    border-radius: var(--radius-lg);
    background: var(--bg-1, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
}

.v4_2-panel-fallback {
    background: var(--blue-bg, #f0f4ff);
    border-color: var(--blue, #4a90d9);
}

.v4_2-panel-blocked {
    background: var(--yellow-bg, #fef3c7);
    border-color: var(--yellow, #f59e0b);
}

.v4_2-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-sm);
    margin-bottom: var(--space-md);
}

.v4_2-heading {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text-1);
}

.v4_2-heading-sub {
    display: block;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--text-3);
    margin-top: 0.15rem;
    letter-spacing: 0.02em;
}

.v4_2-fired-count {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-2);
    background: var(--bg-2, #f9fafb);
    padding: 0.25rem 0.6rem;
    border-radius: var(--radius-sm);
}

.v4_2-cache-notice {
    margin: 0 0 var(--space-md);
    padding: 0.5rem 0.75rem;
    background: var(--bg-2, #f9fafb);
    border-left: 3px solid var(--text-3);
    border-radius: var(--radius-sm);
    font-size: 0.78rem;
    color: var(--text-2);
    line-height: 1.4;
}

.v4_2-no-fires {
    margin: 0 0 var(--space-md);
    font-size: 0.85rem;
    color: var(--text-2);
    font-style: italic;
}

/* ── Fired-frame cards ────────────────────────────────────────── */

.v4_2-fired-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    margin-bottom: var(--space-md);
}

.v4_2-frame-card {
    padding: 0.7rem 0.9rem;
    border-radius: var(--radius-sm);
    background: var(--bg-1, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
    border-left-width: 3px;
    font-size: 0.85rem;
    line-height: 1.5;
}

/* Tier-specific left border communicates reliability at a glance.
   Reuses the trust-verdict palette for semantic consistency.        */
.v4_2-frame-card.v4_2-tier-strong     { border-left-color: var(--green, #10b981); }
.v4_2-frame-card.v4_2-tier-moderate   { border-left-color: var(--yellow, #f59e0b); }
.v4_2-frame-card.v4_2-tier-weak       { border-left-color: var(--red, #ef4444); }
.v4_2-frame-card.v4_2-tier-unmeasured { border-left-color: var(--gray, #9ca3af); }

.v4_2-frame-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
    flex-wrap: wrap;
}

.v4_2-frame-id {
    font-family: var(--mono);
    font-weight: 600;
    font-size: 0.78rem;
    color: var(--text-2);
}

/* Frame name (FVS_NAMES lookup) rendered alongside the FVS-ID
   header. The ID is the canonical reference, the name is the
   human-readable label. Visual hierarchy: ID muted-mono, name
   regular weight at the same color so the eye reads them as a
   pair without one dominating. Round-3 audit F27. */
.v4_2-frame-name {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--text-1);
}

.v4_2-frame-name-compact {
    font-size: 0.78rem;
    font-weight: 400;
    color: var(--text-2);
}

/* Cross-family AC1 number rendered inline next to the tier badge.
   The tier word ("strong", "moderate") summarizes the AC1 score;
   surfacing the number itself is the construct-honesty discipline.
   Pre-fix the AC1 was tooltip-only (invisible on touch devices).
   Smaller, lighter weight so it reads as a parenthetical. */
.v4_2-tier-ac1 {
    font-size: 0.72rem;
    font-weight: 500;
    opacity: 0.85;
    margin-left: 0.15rem;
    text-transform: none;
    letter-spacing: normal;
}

.v4_2-frame-verdict {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
}

.v4_2-tier-badge {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    cursor: help;
}

.v4_2-tier-badge-compact {
    font-size: 0.62rem;
    padding: 0.1rem 0.4rem;
}

.v4_2-tier-badge-strong     { background: var(--green-bg);  color: var(--green); }
.v4_2-tier-badge-moderate   { background: var(--yellow-bg); color: var(--yellow); }
.v4_2-tier-badge-weak       { background: var(--red-bg);    color: var(--red); }
.v4_2-tier-badge-unmeasured { background: var(--gray-bg);   color: var(--gray); }

.v4_2-frame-reasoning {
    margin: 0;
    color: var(--text-1);
    font-size: 0.85rem;
    line-height: 1.5;
}

/* ── Honest-limit disclosure (collapsible) ────────────────────── */
/* Collapsed by default: a one-line summary keeps the caveat visible
 * without the full ~90-word disclosure dominating the frame card.
 * Expanding reveals the full construct-honesty text. Design choice
 * is per the manual-test feedback that honest_limit was too
 * vertical-heavy when multiple weak-tier frames fired. */

.v4_2-honest-limit {
    margin-top: 0.5rem;
    padding: 0;
    border-radius: var(--radius-sm);
    background: rgba(245, 158, 11, 0.06);
    border-left: 2px solid var(--yellow, #f59e0b);
}

.v4_2-honest-limit-summary {
    cursor: pointer;
    list-style: none;
    padding: 0.45rem 0.65rem;
    display: flex;
    gap: 0.5rem;
    align-items: baseline;
    font-size: 0.78rem;
    line-height: 1.4;
    color: var(--text-2);
}

.v4_2-honest-limit-summary::-webkit-details-marker { display: none; }

.v4_2-honest-limit-summary::before {
    content: "▸";
    color: var(--yellow, #f59e0b);
    font-size: 0.72rem;
    flex-shrink: 0;
    transition: transform 0.15s ease;
}

.v4_2-honest-limit[open] .v4_2-honest-limit-summary::before {
    content: "▾";
}

.v4_2-honest-limit-summary:hover {
    background: rgba(245, 158, 11, 0.08);
    border-top-left-radius: var(--radius-sm);
    border-top-right-radius: var(--radius-sm);
}

.v4_2-honest-limit-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--yellow, #f59e0b);
    flex-shrink: 0;
}

.v4_2-honest-limit-teaser {
    color: var(--text-2);
    font-weight: 400;
}

.v4_2-honest-limit-text {
    margin: 0;
    padding: 0 0.7rem 0.65rem 1.45rem;
    font-size: 0.78rem;
    line-height: 1.55;
    color: var(--text-2);
    border-top: 1px solid rgba(245, 158, 11, 0.25);
    padding-top: 0.55rem;
}

/* ── Falsification flag (per-frame) ───────────────────────────── */
/*
   Per-detection-card affordance: opt-in users can flag a frame
   verdict they think is wrong, with a flag_type + brief reason.
   Visually quieter than honest-limit (no accent border) so it
   doesn't compete with the verdict copy; expands on click into
   a compact form. State 'submitted' disables the form so a single
   user can't spam-flag the same frame on the same page load.
*/
.v4_2-falsification {
    margin-top: 0.4rem;
    padding: 0;
    border-radius: var(--radius-sm);
    background: transparent;
}

.v4_2-falsification-summary {
    cursor: pointer;
    list-style: none;
    padding: 0.3rem 0;
    display: inline-flex;
    gap: 0.35rem;
    align-items: baseline;
    font-size: 0.72rem;
    line-height: 1.4;
    color: var(--text-3, #6b7280);
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}

.v4_2-falsification-summary::-webkit-details-marker { display: none; }

.v4_2-falsification-summary:hover {
    color: var(--text-2);
}

.v4_2-falsification[open] .v4_2-falsification-summary {
    color: var(--text-2);
}

.v4_2-falsification-form {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 0.4rem;
    padding: 0.5rem 0.65rem;
    background: var(--bg-2, #f9fafb);
    border-radius: var(--radius-sm);
}

.v4_2-falsification-label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.74rem;
    color: var(--text-2);
}

.v4_2-falsification-select,
.v4_2-falsification-textarea {
    font-family: inherit;
    font-size: 0.78rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border, #e5e7eb);
    border-radius: var(--radius-sm);
    background: var(--bg-1, #ffffff);
    color: var(--text-1);
}

.v4_2-falsification-textarea {
    resize: vertical;
    min-height: 2.4rem;
}

.v4_2-falsification-actions {
    display: flex;
    gap: 0.6rem;
    align-items: center;
    flex-wrap: wrap;
}

.v4_2-falsification-submit {
    padding: 0.3rem 0.7rem;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--text-1);
    background: var(--bg-1, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.v4_2-falsification-submit:hover:not(:disabled) {
    background: var(--bg-3, #f3f4f6);
}

.v4_2-falsification-submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.v4_2-falsification-status {
    font-size: 0.72rem;
    color: var(--text-3);
}

.v4_2-falsification-status--ok {
    color: var(--green, #10b981);
}

.v4_2-falsification-status--error {
    color: var(--red, #ef4444);
}

html.dark .v4_2-falsification-form {
    background: rgba(255, 255, 255, 0.03);
}

html.dark .v4_2-falsification-select,
html.dark .v4_2-falsification-textarea,
html.dark .v4_2-falsification-submit {
    background: var(--bg-2);
    border-color: rgba(255, 255, 255, 0.1);
    color: var(--text-1);
}

/* ── Not-fired expander ───────────────────────────────────────── */

.v4_2-not-fired {
    margin-bottom: var(--space-md);
    padding: 0.5rem 0.75rem;
    background: var(--bg-2, #f9fafb);
    border-radius: var(--radius-sm);
    font-size: 0.82rem;
}

.v4_2-not-fired-summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--text-2);
    list-style: none;
    padding: 0.2rem 0;
}

.v4_2-not-fired-summary::-webkit-details-marker { display: none; }

.v4_2-not-fired-summary::before {
    content: "▸ ";
    display: inline-block;
    margin-right: 0.3rem;
    color: var(--text-3);
    transition: transform 0.2s;
}

.v4_2-not-fired[open] .v4_2-not-fired-summary::before {
    content: "▾ ";
}

.v4_2-not-fired-hint {
    font-weight: 400;
    color: var(--text-3);
    font-size: 0.75rem;
}

.v4_2-not-fired-list {
    list-style: none;
    padding: 0.5rem 0 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.v4_2-not-fired-item {
    display: grid;
    grid-template-columns: auto auto 1fr;
    gap: 0.5rem;
    align-items: center;
    font-size: 0.78rem;
    color: var(--text-2);
}

.v4_2-frame-reasoning-inline {
    color: var(--text-3);
    font-style: italic;
    font-size: 0.75rem;
    line-height: 1.4;
}

/* ── Engine provenance footer ─────────────────────────────────── */

.v4_2-provenance {
    border-top: 1px solid var(--border, #e5e7eb);
    padding-top: var(--space-sm);
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    font-size: 0.72rem;
    color: var(--text-3);
}

.v4_2-provenance-row {
    display: flex;
    gap: 0.5rem;
}

.v4_2-prov-label {
    font-weight: 600;
    color: var(--text-2);
    min-width: 5rem;
}

.v4_2-prov-value {
    font-family: var(--mono);
    color: var(--text-3);
}

.v4_2-reliability-note-wrapper {
    margin-top: 0.4rem;
}

.v4_2-reliability-note-summary {
    cursor: pointer;
    font-size: 0.72rem;
    color: var(--text-3);
    font-weight: 500;
}

.v4_2-reliability-note-summary::-webkit-details-marker { display: none; }

.v4_2-reliability-note-summary::before {
    content: "▸ ";
    color: var(--text-3);
}

.v4_2-reliability-note-wrapper[open] .v4_2-reliability-note-summary::before {
    content: "▾ ";
}

.v4_2-reliability-note-text {
    margin: 0.4rem 0 0;
    padding: 0.5rem 0.7rem;
    background: var(--bg-2, #f9fafb);
    border-radius: var(--radius-sm);
    font-size: 0.72rem;
    line-height: 1.5;
    color: var(--text-2);
}

/* ── Fallback and blocked states ──────────────────────────────── */

.v4_2-fallback-body,
.v4_2-blocked-body {
    font-size: 0.88rem;
    line-height: 1.5;
}

.v4_2-fallback-message,
.v4_2-blocked-message {
    margin: 0 0 0.6rem;
    color: var(--text-1);
    font-weight: 500;
}

.v4_2-fallback-hint,
.v4_2-blocked-hint {
    margin: 0;
    font-size: 0.78rem;
    color: var(--text-2);
}

/* ── Dark-mode palette ────────────────────────────────────────── */

html.dark .v4_2-panel {
    background: var(--bg-1);
    border-color: var(--border);
}

html.dark .v4_2-frame-card {
    background: var(--bg-1);
    border-color: var(--border);
}

html.dark .v4_2-fired-count,
html.dark .v4_2-cache-notice,
html.dark .v4_2-not-fired,
html.dark .v4_2-reliability-note-text {
    background: var(--bg-2);
}

/* Dark-mode palette for post-manual-test polish round: the collapsed
 * honest_limit details element and the redesigned framing-cat tiles.
 * The yellow accent is dimmed for dark mode so it signals caveat
 * without glaring. */
html.dark .v4_2-honest-limit {
    background: rgba(245, 158, 11, 0.12);
    border-left-color: rgba(245, 158, 11, 0.7);
}

html.dark .v4_2-honest-limit-summary:hover {
    background: rgba(245, 158, 11, 0.18);
}

html.dark .v4_2-honest-limit-text {
    border-top-color: rgba(245, 158, 11, 0.3);
}

html.dark .v4_2-honest-limit-label {
    color: rgba(245, 158, 11, 0.95);
}

/* Cost-origin subtext: dimmer-still variant of the provenance text
 * in dark mode so the parenthetical "original call" line doesn't
 * compete with the "no additional spend" primary message. */
.v4_2-prov-cost-origin {
    display: block;
    font-size: 0.68rem;
    color: var(--text-4);
    margin-top: 0.1rem;
    font-family: var(--mono);
}

html.dark .v4_2-prov-cost-origin {
    color: var(--text-4);
}


/* ─── ANALYSIS MANIFEST ─── */
/* Provenance receipt collapsed at the bottom of every result page.
 * Closed by default so the analytical findings stay visually
 * primary; an open state surfaces the layered substrate, calibration
 * backing per provider, and exact LLM call metadata. Border + muted
 * background mark it as auxiliary / receipt-shaped, not a finding. */
.analysis-manifest {
    margin-top: 1.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    padding: 0;
}

.analysis-manifest-summary {
    cursor: pointer;
    padding: 0.85rem 1rem;
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.65rem;
    user-select: none;
}

.analysis-manifest-summary::-webkit-details-marker { display: none; }

.analysis-manifest-summary::before {
    content: "▸";
    color: var(--text-3);
    font-size: 0.85rem;
    transition: transform 0.15s ease;
    display: inline-block;
}

.analysis-manifest[open] > .analysis-manifest-summary::before {
    transform: rotate(90deg);
}

.analysis-manifest-title {
    font-weight: 600;
    color: var(--text);
}

.analysis-manifest-hint {
    color: var(--text-3);
    font-size: 0.85rem;
}

.analysis-manifest-body {
    padding: 0 1rem 1.25rem 1rem;
    border-top: 1px solid var(--border);
}

.analysis-manifest-lede {
    color: var(--text-2);
    font-size: 0.9rem;
    line-height: 1.55;
    margin: 0.85rem 0 1rem 0;
}

.manifest-section {
    margin-top: 1.1rem;
}

.manifest-section-title {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-3);
    margin: 0 0 0.5rem 0;
}

.manifest-section-note {
    color: var(--text-3);
    font-size: 0.82rem;
    margin: 0 0 0.55rem 0;
    line-height: 1.5;
}

/* Substrate identity table: dl with two columns. dt label / dd value. */
.manifest-id-list {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.3rem 0.85rem;
    margin: 0;
}

.manifest-id-list dt {
    color: var(--text-3);
    font-size: 0.85rem;
}

.manifest-id-list dd {
    margin: 0;
    color: var(--text);
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.manifest-id-link {
    font-size: 0.75rem;
    color: var(--blue);
    text-decoration: none;
}

.manifest-id-link:hover { text-decoration: underline; }

/* Layer lists: vertical stack with left-accent matching ran/skipped state. */
.manifest-layer-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.manifest-layer {
    padding: 0.5rem 0.7rem;
    border-radius: 4px;
    border-left: 3px solid var(--border);
    background: var(--bg);
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.85rem;
}

.manifest-layer-ran { border-left-color: var(--green); }

.manifest-layer-skipped { border-left-color: var(--text-4); }

.manifest-layer-name {
    color: var(--text);
    font-weight: 600;
}

.manifest-layer-detail,
.manifest-layer-reason {
    color: var(--text-2);
    font-size: 0.82rem;
    line-height: 1.45;
}

.manifest-layer-link {
    align-self: flex-start;
    font-size: 0.75rem;
    color: var(--blue);
    text-decoration: none;
}

.manifest-layer-link:hover { text-decoration: underline; }

/* LLM-call table: compact, monospace stage/model so a reader can
 * scan the substrate keys without confusion with body prose. */
.manifest-llm-table,
.manifest-provider-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
    margin: 0;
}

.manifest-llm-table th,
.manifest-provider-table th {
    text-align: left;
    color: var(--text-3);
    font-weight: 600;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.45rem 0.55rem;
    border-bottom: 1px solid var(--border);
}

.manifest-llm-table td,
.manifest-provider-table td {
    padding: 0.45rem 0.55rem;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    vertical-align: top;
}

.manifest-llm-table tr:last-child td,
.manifest-provider-table tr:last-child td { border-bottom: none; }

.manifest-llm-skipped td { color: var(--text-3); }

.manifest-llm-reason {
    color: var(--text-3);
    cursor: help;
}

.manifest-llm-reason-row td {
    background: var(--bg);
    color: var(--text-3);
    font-style: italic;
    font-size: 0.78rem;
    padding-left: 1.5rem;
    border-bottom: 1px solid var(--border);
}

.manifest-na {
    color: var(--text-4);
    font-size: 0.78rem;
}

/* Provider tier pills: reuse the trust-tier color family used
 * elsewhere on the page so a reader scanning the manifest sees the
 * same green/amber/red signal they saw on the trust banner above. */
.manifest-tier {
    display: inline-block;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.manifest-tier-strong { background: rgba(16, 185, 129, 0.12); color: var(--green); }
.manifest-tier-moderate { background: rgba(245, 158, 11, 0.12); color: var(--yellow); }
.manifest-tier-weak { background: rgba(239, 68, 68, 0.12); color: var(--red); }
.manifest-tier-uncalibrated { background: rgba(148, 163, 184, 0.18); color: var(--text-3); }

.manifest-footer {
    color: var(--text-3);
    font-size: 0.82rem;
    line-height: 1.5;
    margin: 1.1rem 0 0 0;
}

/* Mobile: stack the LLM table cells so wide rows remain legible at
 * narrow widths. 540px breakpoint matches the .fai-takeaway-copy-btn
 * mobile breakpoint elsewhere in this file (search "max-width: 540px"). */
@media (max-width: 540px) {
    .manifest-llm-table,
    .manifest-provider-table {
        font-size: 0.8rem;
    }
    .manifest-llm-table th,
    .manifest-llm-table td,
    .manifest-provider-table th,
    .manifest-provider-table td {
        padding: 0.4rem 0.4rem;
    }
    .manifest-id-list {
        grid-template-columns: 1fr;
        gap: 0.15rem 0;
    }
    .manifest-id-list dt {
        margin-top: 0.4rem;
    }
}

/* Cross-link prefill acknowledgement: surfaced above the /compare
 * form when the user arrived via the "Compare with another document"
 * CTA on /check results. Muted blue so it reads as a status note,
 * not a primary callout. Mirrors the .pii-intake-notice spacing
 * convention so multiple in-flow notes align consistently. */
.compare-prefill-note {
    margin: 0 0 0.85rem 0;
    padding: 0.6rem 0.85rem;
    border-left: 3px solid var(--blue);
    background: rgba(59, 130, 246, 0.06);
    color: var(--text-2);
    font-size: 0.88rem;
    line-height: 1.45;
    border-radius: 0 4px 4px 0;
}

html.dark .compare-prefill-note {
    background: rgba(59, 130, 246, 0.10);
}

/* ─── WORKED EXAMPLES ONBOARDING ─── */
/* First-time visitors see real documents Frame Check has analyzed
 * below the instruments panel. Card grid layout: each card is an
 * anchor with date + source-type + title + hook + cta. Visual
 * register sits BETWEEN the muted instruments-panel above and the
 * footer; bordered cards with hover-lift to read as discoverable
 * but not as the primary CTA. */
.worked-examples-onboarding {
    margin: 3rem 0 2rem 0;
    padding: 0;
}

.worked-examples-header {
    margin-bottom: 1.25rem;
    text-align: center;
}

.worked-examples-eyebrow {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    margin-bottom: 0.5rem;
}

.worked-examples-title {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text);
    margin: 0 0 0.65rem 0;
    line-height: 1.25;
}

.worked-examples-lede {
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--text-2);
    margin: 0 auto;
    max-width: 60ch;
}

/* Card grid: auto-fit so the count adapts to viewport width.
 * minmax keeps cards from getting too narrow on mid-size displays. */
.worked-examples-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 0.85rem;
    margin-bottom: 1rem;
}

.worked-example-card {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.95rem 1.05rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    text-decoration: none;
    transition: border-color 0.15s ease, transform 0.15s ease,
                box-shadow 0.15s ease;
}

.worked-example-card:hover,
.worked-example-card:focus-visible {
    border-color: var(--blue);
    transform: translateY(-1px);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    outline: none;
}

html.dark .worked-example-card:hover,
html.dark .worked-example-card:focus-visible {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

.worked-example-meta {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    flex-wrap: wrap;
    font-size: 0.72rem;
    color: var(--text-3);
}

.worked-example-source-type {
    padding: 0.08rem 0.4rem;
    border-radius: 999px;
    background: var(--bg);
    border: 1px solid var(--border);
    font-size: 0.68rem;
    text-transform: lowercase;
    letter-spacing: 0.02em;
}

.worked-example-title {
    font-size: 1.0rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--text);
}

.worked-example-hook {
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--text-2);
}

.worked-example-cta {
    margin-top: 0.25rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--blue);
    align-self: flex-start;
}

.worked-examples-archive-link {
    text-align: center;
    margin: 0;
    font-size: 0.85rem;
}

.worked-examples-archive-link a {
    color: var(--blue);
    text-decoration: none;
    font-weight: 600;
}

.worked-examples-archive-link a:hover { text-decoration: underline; }

/* Mobile: stack to single column at narrow widths. 540px aligns
 * with the existing breakpoint convention elsewhere in this file. */
@media (max-width: 540px) {
    .worked-examples-grid {
        grid-template-columns: 1fr;
    }
    .worked-examples-title {
        font-size: 1.35rem;
    }
}

/* .input-footer modifier: when there is no left-side child (e.g.
 * char-count) to balance against, push the actions to the right
 * via flex-end. Used on /compare's submit row where the form has
 * no per-textarea char-count. The default .input-footer (space-
 * between) reads as left-aligned with one child; flex-end is the
 * intended visual when the row holds only the actions. */
.input-footer--end {
    justify-content: flex-end;
}
