/* ==========================================================================
   preside-by-side.css — Styles for /preside-by-side
   frontendneeded.com

   Editorial-poster aesthetic: high-contrast serif display, flag-forward
   gradients, restrained typography against bold color fields.

   This page intentionally does NOT use the shared gr-shell layout. It
   loads /assets/css/styles.css only for the FEN navbar — a few overrides
   below undo the shared body's full-viewport lock so this page can scroll.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Overrides for shared styles.css

   The shared body sets:
     height: 100vh; overflow: hidden;
     display: flex; flex-direction: column;
     font-family: 'DM Sans', sans-serif;

   This page needs to scroll vertically and uses its own body font, so we
   undo those rules here. The navbar's font is then locked back to DM Sans
   so it stays visually consistent with the rest of the site.
   -------------------------------------------------------------------------- */

@font-face {
    font-family: 'Fraunces';
    src: url('/assets/fonts/Fraunces-VariableFont_SOFT,WONK,opsz,wght.ttf') format('truetype');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Fraunces';
    src: url('/assets/fonts/Fraunces-Italic-VariableFont_SOFT,WONK,opsz,wght.ttf') format('truetype');
    font-weight: 100 900;
    font-style: italic;
    font-display: swap;
}

@font-face {
    font-family: 'IBM Plex Sans';
    src: url('/assets/fonts/IBMPlexSans-VariableFont_wdth,wght.ttf') format('truetype');
    font-weight: 100 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'IBM Plex Sans';
    src: url('/assets/fonts/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf') format('truetype');
    font-weight: 100 700;
    font-style: italic;
    font-display: swap;
}

body {
    height: auto;
    overflow-x: hidden;
    overflow-y: visible;
    display: block;
}

/* Stick the FEN navbar to the top on this page (since the body actually
   scrolls here, unlike the home page). The mobile figure-container sticks
   at top: 40px expecting this 40px-tall navbar to be pinned above it.
   z-index sits above the figure-container (z-index 10) and centerline. */
.site-nav {
    position: sticky;
    top: 0;
    z-index: 50;
}

.site-nav,
.site-nav a,
.nav-dropdown-toggle,
.nav-dropdown-menu a {
    font-family: 'DM Sans', sans-serif;
}


/* --------------------------------------------------------------------------
     Variables
     -------------------------------------------------------------------------- */
:root {
    /* Allow transitions to/from intrinsic sizes (max-content, auto). Used
       by the bar-detail expand animation; falls back to instant in browsers
       that don't support it yet. */
    interpolate-size: allow-keywords;

    /* Palette — democrat (blue) */
    --blue-deep: #0a1845;
    --blue-mid: #1e3a8a;
    --blue-bright: #2952d4;
    --blue-glow: #4d7fff;
    --blue-pale: #6e9eff;

    /* Palette — republican (red) */
    --red-deep: #4a0810;
    --red-mid: #8c0e1a;
    --red-bright: #c81e2a;
    --red-glow: #e74154;
    --red-pale: #ff6b78;

    /* Neutrals */
    --ink: #f8f5ee;
    --ink-dim: rgba(248, 245, 238, 0.72);
    --ink-faint: rgba(248, 245, 238, 0.45);
    --paper: #f5f1e8;
    --shadow-strong: 0 12px 40px rgba(0, 0, 0, 0.45);
    --shadow-soft: 0 4px 18px rgba(0, 0, 0, 0.25);

    /* Type */
    --font-display: 'Fraunces', 'Times New Roman', Georgia, serif;
    --font-body: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;

    /* Layout */
    --bar-height: 62px;
    --bar-gap: 18px;
    --side-pad-y: 80px;
    --header-h: auto;

    /* Motion */
    --ease-out: cubic-bezier(0.19, 1, 0.22, 1);
    --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}

/* --------------------------------------------------------------------------
     Party theming

     Color follows the president; side only governs layout/orientation.
     Whichever ancestor element carries `data-party` defines the --party-*
     variables for everything inside. JS sets data-party on:
       - each .side          (drives the side background + bar fills)
       - each .picker-cell   (drives the picker border tint)
       - the modal           (drives modal severity + sheet border colors)

     Adding a third party ("independent" etc.) is one more block here plus
     a `party` value in the president data — no other code changes.

     bg-1..bg-4 are the four stops of each side's vertical background
     gradient, in order. The two end stops are nearly identical (a slightly
     deeper, slightly different deep at top vs bottom) — kept distinct
     because the original had subtle variance there.

     --party-rgb is an unwrapped triplet so the same channel can power
     multiple alphas via rgba() (radial accent at 0.25, picker border at
     0.45, picker hover at 0.85) without exploding into one variable per
     opacity.

     --party-pattern is the SVG texture (stars for democrat, stripes for
     republican) that overlays the side background. Lives with the party
     so it follows the president across sides — picking a republican on
     the left correctly shows red stripes, not red stars.
     -------------------------------------------------------------------------- */

[data-party="democrat"] {
    --party-bg-1: var(--blue-deep);
    /* gradient 0% */
    --party-bg-2: var(--blue-mid);
    /* gradient 35% */
    --party-bg-3: #1a2e6f;
    /* gradient 65% */
    --party-bg-4: #0a1438;
    /* gradient 100% */
    --party-bright: var(--blue-bright);
    --party-glow: var(--blue-glow);
    --party-pale: var(--blue-pale);
    --party-rgb: 77, 127, 255;
    --party-pattern: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'><polygon points='40,12 47.5,33 70,33 51.5,46.5 58.5,68 40,55 21.5,68 28.5,46.5 10,33 32.5,33' fill='white' fill-opacity='0.07'/></svg>");
}

[data-party="republican"] {
    --party-bg-1: var(--red-deep);
    --party-bg-2: var(--red-mid);
    --party-bg-3: #6f1018;
    --party-bg-4: #3a0810;
    --party-bright: var(--red-bright);
    --party-glow: var(--red-glow);
    --party-pale: var(--red-pale);
    --party-rgb: 231, 65, 84;
    --party-pattern: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='40' viewBox='0 0 100 40'><rect x='0' y='0' width='100' height='20' fill='white' fill-opacity='0.06'/></svg>");
}

/* --------------------------------------------------------------------------
     Base reset + body
     -------------------------------------------------------------------------- */
button {
    font: inherit;
    color: inherit;
    cursor: pointer;
}

ol,
ul {
    margin: 0;
    padding: 0;
    list-style: none;
}

a {
    color: inherit;
}

html {
    scroll-behavior: smooth;
    background: #0a0a14;
}

body {
    font-family: var(--font-body);
    font-size: 16px;
    line-height: 1.55;
    color: var(--ink);
    background: #0a0a14;
    -webkit-font-smoothing: antialiased;
}

/* --------------------------------------------------------------------------
     Site header — editorial masthead
     -------------------------------------------------------------------------- */
.site-header {
    position: relative;
    text-align: center;
    padding: 70px 24px 50px;
    background:
        radial-gradient(ellipse at center top, rgba(255, 255, 255, 0.04) 0%, transparent 60%),
        linear-gradient(180deg, #0a0a14 0%, #14141f 100%);
    z-index: 2;
}

.site-eyebrow {
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0 0 18px;
}

.site-title {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: clamp(2.25rem, 6.5vw, 5rem);
    line-height: 0.95;
    letter-spacing: -0.025em;
    margin: 0;
    color: var(--ink);
    font-variation-settings: 'opsz' 144;
}

.site-title em {
    font-style: italic;
    font-weight: 500;
    font-size: 0.7em;
    color: var(--ink-dim);
    font-variation-settings: 'opsz' 100;
    margin: 0 0.1em;
    display: inline-block;
    transform: translateY(-0.15em);
}

.site-tagline {
    font-family: var(--font-display);
    font-style: italic;
    font-size: clamp(0.95rem, 1.4vw, 1.15rem);
    color: var(--ink-dim);
    margin: 22px auto 0;
    max-width: 540px;
}

.header-rule {
    width: 60px;
    height: 2px;
    margin: 36px auto 0;
    background: linear-gradient(90deg, transparent, var(--ink-faint), transparent);
}

/* --------------------------------------------------------------------------
     President pickers — straddle the boundary between header and comparison.

     The .picker-row container is zero-height and uses absolute positioning
     to place each picker over the boundary line. Each .picker-cell is
     centered on its half of the page (25% / 75%) and vertically centered
     on the boundary itself via translate(-50%, -50%) — so half the picker
     sits in the header's bottom padding, half pokes down into the
     comparison's top padding. Both have enough padding (50px / 80px) to
     comfortably absorb a ~50px-tall picker.

     Mobile: the row turns into a normal-flow flex row (see media query
     near the bottom of this file).
     -------------------------------------------------------------------------- */
.picker-row {
    position: relative;
    height: 0;
    /* Sit above the comparison's content (z:1–2) and the centerline (z:5),
       but below the FEN navbar (z:50) so scroll-sticky nav still wins. */
    z-index: 25;
}

.picker-cell {
    position: absolute;
    top: 0;
    transform: translate(-50%, -50%);
}

.picker-cell[data-side="left"] {
    left: 25%;
}

.picker-cell[data-side="right"] {
    left: 75%;
}

.president-picker {
    /* Reset native styling so we can paint our own. The dropdown that
       opens on click is still browser-native — only the closed display
       is styleable cross-browser, which is fine for "basic picker UI". */
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;

    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1rem;
    font-variation-settings: 'opsz' 36;
    letter-spacing: -0.005em;

    color: var(--ink);
    background-color: rgba(0, 0, 0, 0.55);
    /* Border tint follows the picker cell's data-party (set by JS). The
       fallback (white) only applies if a cell somehow loses its data-party
       — the cascade from .picker-cell[data-party="..."] supplies the rgb
       triplet otherwise. */
    border: 1px solid rgba(var(--party-rgb, 255, 255, 255), 0.45);
    border-radius: 2px;
    padding: 12px 36px 12px 18px;
    min-width: 160px;

    /* Centers the currently-selected option's text horizontally. The
       second declaration is what actually does the work in <select>;
       the first keeps the value inheritable for any future styled
       option lists. */
    text-align: center;
    text-align-last: center;

    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.5),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);

    cursor: pointer;
    transition: border-color 0.2s, background-color 0.2s, transform 0.2s;

    /* Custom dropdown chevron — inline SVG so no extra HTTP request and
       it inherits theme cleanly. Replace the stroke colour to recolour. */
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 14px 14px;
}

.president-picker:hover {
    background-color: rgba(0, 0, 0, 0.7);
    border-color: rgba(var(--party-rgb, 255, 255, 255), 0.85);
    transform: translateY(-1px);
}

.president-picker:focus-visible {
    outline: 2px solid var(--ink);
    outline-offset: 3px;
}

/* Style the opened option list. Most browsers render the open dropdown
   with OS chrome, but Chrome/Edge let us at least pick the surface and
   text colors so the list isn't a stark white rectangle next to a dark
   page. Safari/Firefox ignore most of this; that's fine. */
.president-picker option {
    background: #1a1a26;
    color: var(--ink);
    font-family: var(--font-display);
}

/* --------------------------------------------------------------------------
     Comparison main — two-column split
     -------------------------------------------------------------------------- */
.comparison {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    min-height: 100vh;
    isolation: isolate;
}

/* The visible vertical centerline */
.centerline {
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    transform: translateX(-50%);
    background: linear-gradient(180deg,
            rgba(255, 255, 255, 0.55) 0%,
            rgba(255, 255, 255, 0.15) 60%,
            rgba(255, 255, 255, 0) 100%);
    box-shadow: 0 0 24px rgba(255, 255, 255, 0.2);
    z-index: 5;
    pointer-events: none;
}

/* --------------------------------------------------------------------------
     Sides — shared layout
     -------------------------------------------------------------------------- */
.side {
    position: relative;
    display: grid;
    padding: var(--side-pad-y) 0;
    min-height: 100vh;
    overflow: hidden;
}

.side-left {
    grid-template-columns: minmax(220px, 26%) 1fr;
}

.side-right {
    grid-template-columns: 1fr minmax(220px, 26%);
}

/* Background layers — gradient + pattern overlay.

   Pattern (stars vs stripes) is driven by --party-pattern, which travels
   with the president via data-party. The radial-accent anchor stays bound
   to the side (12% on the left, 88% on the right) because the silhouette
   never moves — the glow always belongs at the outer edge. */
.side-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

.side-left .side-bg {
    background:
        /* pattern follows the side's current party — stars for democrat,
           stripes for republican */
        var(--party-pattern) repeat,
        /* main gradient — colors follow the side's current party */
        linear-gradient(180deg,
            var(--party-bg-1) 0%,
            var(--party-bg-2) 35%,
            var(--party-bg-3) 65%,
            var(--party-bg-4) 100%),
        /* outer-edge glow behind silhouette — anchored at 12% (left side
           of viewport), color follows party */
        radial-gradient(ellipse at 12% 50%, rgba(var(--party-rgb), 0.25) 0%, transparent 55%);
}

.side-right .side-bg {
    background:
        var(--party-pattern) repeat,
        linear-gradient(180deg,
            var(--party-bg-1) 0%,
            var(--party-bg-2) 35%,
            var(--party-bg-3) 65%,
            var(--party-bg-4) 100%),
        /* radial anchored at 88% (right side of viewport) */
        radial-gradient(ellipse at 88% 50%, rgba(var(--party-rgb), 0.25) 0%, transparent 55%);
}

/* Subtle grain layer for texture */
.side::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 1;
    background:
        radial-gradient(circle at 50% 0%, rgba(0, 0, 0, 0.4) 0%, transparent 40%),
        radial-gradient(circle at 50% 100%, rgba(0, 0, 0, 0.55) 0%, transparent 50%);
}

/* --------------------------------------------------------------------------
     Figure (silhouette + name badge) — sticky on desktop

     NOTE: grid-row: 1 is set explicitly below because in side-right the
     figure-wrap appears BEFORE bars-wrap in the DOM but is placed in
     grid-column: 2. CSS grid's default "sparse" auto-flow advances the
     placement cursor past column 2, then puts bars-wrap (which targets
     column 1) onto a NEW row. Pinning both items to row 1 forces them
     to share the same row, so the bars on each side line up vertically.
     -------------------------------------------------------------------------- */
.figure-wrap {
    position: relative;
    z-index: 2;
    height: 100%;
    grid-row: 1;
}

.side-left .figure-wrap {
    grid-column: 1;
}

.side-right .figure-wrap {
    grid-column: 2;
}

.figure-container {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    padding: 0 0 4vh;
}

.silhouette {
    width: 100%;
    max-width: 360px;
    height: auto;
    flex: 1;
    min-height: 0;
    filter: drop-shadow(0 20px 32px rgba(0, 0, 0, 0.4));
}

.name-badge {
    position: absolute;
    bottom: 6vh;
    text-align: center;
    z-index: 3;
    padding: 14px 24px;
    background: rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 2px;
    box-shadow: var(--shadow-strong);
    /* The badge ships empty in the HTML (JS fills it in). The styled
       background + padding + blur would otherwise paint a small empty
       rounded rectangle for a frame before JS runs. Hide until the
       `hydrated` class is added below — see the init block in
       preside-by-side.js. Reduced-motion users get a snap-in (the
       global prefers-reduced-motion rule wipes the transition); that's
       appropriate. */
    opacity: 0;
    transition: opacity 0.25s ease;
}

body.hydrated .name-badge {
    opacity: 1;
}

.name-first {
    display: block;
    font-family: var(--font-body);
    font-size: 0.7rem;
    font-weight: 500;
    letter-spacing: 0.28em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: 4px;
}

.last-name {
    display: block;
    font-family: var(--font-display);
    font-weight: 900;
    font-size: clamp(1.6rem, 3.2vw, 2.5rem);
    line-height: 1;
    letter-spacing: -0.02em;
    font-variation-settings: 'opsz' 144;
    color: var(--ink);
}

.name-detail {
    display: block;
    font-family: var(--font-display);
    font-style: italic;
    font-size: 0.78rem;
    color: var(--ink-dim);
    margin-top: 6px;
    font-variation-settings: 'opsz' 9;
}

.name-detail sup {
    font-size: 0.65em;
    margin-left: 1px;
}

/* --------------------------------------------------------------------------
     Bars — the diverging chart

     justify-content: flex-start so the bars start at the TOP of the side
     instead of being vertically centered (which pushed them below the fold).
     grid-row: 1 — see note on .figure-wrap above.
     -------------------------------------------------------------------------- */
.bars-wrap {
    position: relative;
    z-index: 2;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding: 40px 0;
    min-height: 100%;
    grid-row: 1;
}

.side-left .bars-wrap {
    grid-column: 2;
}

.side-right .bars-wrap {
    grid-column: 1;
}

.bars-container {
    display: flex;
    flex-direction: column;
    gap: var(--bar-gap);
    width: 100%;
}

/* Each .bar is a column: row + (hidden) detail */
.bar {
    display: flex;
    flex-direction: column;
    width: 100%;
}

/* The bar row — the visible bar + outer label */
.bar-row {
    display: flex;
    align-items: center;
    gap: 18px;
}

.side-left .bar-row {
    flex-direction: row;
    /* [label] [bar →] center */
    justify-content: flex-end;
    /* anchor at right (centerline) */
    padding-right: 0;
}

.side-right .bar-row {
    flex-direction: row;
    /* center [← bar] [label] */
    justify-content: flex-start;
    /* anchor at left (centerline) */
    padding-left: 0;
}

/* Outer label — desktop default */
.bar-label-outer {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: -0.005em;
    color: var(--ink);
    white-space: nowrap;
    font-variation-settings: 'opsz' 36;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
    flex-shrink: 0;
}

/* The bar fill itself — width driven by --severity */
.bar-fill {
    --bar-width: calc(15% + (var(--severity) * 7.2%));
    width: var(--bar-width);
    height: var(--bar-height);
    border: none;
    position: relative;
    display: flex;
    align-items: center;
    cursor: pointer;
    flex-shrink: 0;

    transition:
        transform 0.35s var(--ease-soft),
        filter 0.25s var(--ease-soft),
        box-shadow 0.35s var(--ease-soft);

    /* Page-load entrance animation */
    transform-origin: var(--origin, right center);
    animation: barGrow 0.95s var(--ease-out) backwards;
}

.bar-fill:focus-visible {
    outline: 2px solid var(--ink);
    outline-offset: 4px;
}

.side-left .bar-fill {
    --origin: right center;
    background:
        linear-gradient(90deg,
            var(--party-pale) 0%,
            var(--party-glow) 35%,
            var(--party-bright) 100%);
    border-radius: 2px 0 0 2px;
    justify-content: flex-start;
    padding: 0 18px;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.25),
        inset 0 -1px 0 rgba(0, 0, 0, 0.2),
        -2px 4px 12px rgba(0, 0, 0, 0.3);
}

.side-right .bar-fill {
    --origin: left center;
    background:
        linear-gradient(270deg,
            var(--party-pale) 0%,
            var(--party-glow) 35%,
            var(--party-bright) 100%);
    border-radius: 0 2px 2px 0;
    justify-content: flex-end;
    padding: 0 18px;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.25),
        inset 0 -1px 0 rgba(0, 0, 0, 0.2),
        2px 4px 12px rgba(0, 0, 0, 0.3);
}

.bar-fill:hover {
    filter: brightness(1.1) saturate(1.1);
    transform: scaleY(1.04);
}

.bar.expanded .bar-fill {
    filter: brightness(1.15) saturate(1.15);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.35),
        inset 0 -1px 0 rgba(0, 0, 0, 0.25),
        0 0 24px rgba(255, 255, 255, 0.1);
}

/* Severity number — at the outer tip of the bar */
.severity-number {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--ink);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
    font-variation-settings: 'opsz' 144;
    letter-spacing: -0.02em;
    opacity: 0;
    animation: fadeIn 0.5s var(--ease-soft) 0.7s forwards;
}

/* Severity hint — only on the first (largest) bar of each side, to clarify
   what the big standalone number is. Low-opacity, points outward toward
   the number, fades in after the number has settled. Hidden on mobile. */
.bar:first-child .bar-fill::after {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-family: var(--font-body);
    font-size: 0.65rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink);
    opacity: 0;
    white-space: nowrap;
    pointer-events: none;
    animation: severityHintFadeIn 0.6s var(--ease-soft) 1.4s forwards;
}

.side-left .bar:first-child .bar-fill::after {
    content: '\2190 \00a0severity level';
    /* ← severity level */
    left: 56px;
    /* past the severity number */
}

.side-right .bar:first-child .bar-fill::after {
    content: 'severity level\00a0 \2192';
    /* severity level → */
    right: 64px;
    /* before the (potentially 2-digit) number */
}

/* Inner label — hidden on desktop, shown on mobile */
.bar-label-inner {
    display: none;
}

/* --------------------------------------------------------------------------
     Bar detail (expanded panel) — desktop inline, mobile modal
     -------------------------------------------------------------------------- */
.bar-detail {
    width: 100%;
    max-height: 0;
    overflow: hidden;
    transition:
        max-height 0.5s var(--ease-soft),
        margin-top 0.5s var(--ease-soft),
        opacity 0.4s var(--ease-soft);
    opacity: 0;
}

.bar-detail[hidden] {
    display: block;
    /* override [hidden] so we can animate */
}

.bar.expanded .bar-detail {
    max-height: max-content;
    margin-top: 14px;
    opacity: 1;
}

.bar-detail-inner {
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 4px;
    padding: 28px 32px;
    box-shadow: var(--shadow-soft);
}

.side-left .bar-detail-inner {
    border-left: 3px solid var(--party-glow);
}

.side-right .bar-detail-inner {
    border-right: 3px solid var(--party-glow);
}

.detail-title {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 1.5rem;
    font-variation-settings: 'opsz' 144;
    letter-spacing: -0.015em;
    margin: 0 0 12px;
    color: var(--ink);
}

.detail-description {
    font-family: var(--font-body);
    font-size: 0.98rem;
    line-height: 1.65;
    color: var(--ink-dim);
    margin: 0 0 22px;
}

.detail-sources {
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    padding-top: 18px;
}

.sources-heading {
    font-family: var(--font-body);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0 0 10px;
}

.sources-list {
    counter-reset: source;
}

.sources-list li {
    counter-increment: source;
    font-family: var(--font-body);
    font-size: 0.88rem;
    padding: 6px 0 6px 28px;
    position: relative;
    color: var(--ink-dim);
}

.sources-list li::before {
    content: counter(source);
    position: absolute;
    left: 0;
    top: 6px;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.85rem;
    color: var(--ink-faint);
    width: 20px;
    text-align: right;
}

.sources-list a {
    text-decoration: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.25);
    transition: border-color 0.2s, color 0.2s;
}

.sources-list a:hover {
    color: var(--ink);
    border-bottom-color: var(--ink);
}

/* --------------------------------------------------------------------------
     Footer
     -------------------------------------------------------------------------- */
.site-footer {
    position: relative;
    z-index: 2;
    padding: 50px 24px 60px;
    text-align: center;
    background: linear-gradient(180deg, #14141f 0%, #0a0a14 100%);
}

.footer-disclaimer {
    font-family: var(--font-display);
    font-style: italic;
    font-size: 0.92rem;
    line-height: 1.65;
    color: var(--ink-dim);
    max-width: 680px;
    margin: 0 auto;
    font-variation-settings: 'opsz' 14;
}

.footer-disclaimer strong {
    font-style: normal;
    font-weight: 700;
    color: var(--ink);
    letter-spacing: 0.02em;
}

/* --------------------------------------------------------------------------
     Modal — uses the native <dialog> element (mobile only)

     <dialog> opened via showModal() handles focus trapping, ESC dismissal,
     top-layer placement, and inerting the background page for free. We
     style on top of that and add an animated open/close.

     The animation uses an .is-open class toggled from JS (on the next frame
     after .showModal(), and removed before .close() with a delay). This is
     more reliable across browsers than animating the open/close transition
     directly, because <dialog> goes display:none ↔ display:block when
     toggled and that breaks pure-CSS transitions.
     -------------------------------------------------------------------------- */

/* Strip the browser's default <dialog> chrome */
.modal {
    border: none;
    background: transparent;
    color: inherit;
    padding: 0;
    margin: 0;
}

/* When open, lay the dialog out as a full-viewport flex container with the
   sheet anchored to the bottom. dvh handles iOS Safari's URL-bar quirks. */
.modal[open] {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
}

/* Native ::backdrop replaces the manual .modal-backdrop div we used to ship */
.modal::backdrop {
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    opacity: 0;
    transition: opacity 0.3s var(--ease-soft);
}

.modal.is-open::backdrop {
    opacity: 1;
}

.modal-sheet {
    position: relative;
    width: 100%;
    max-width: 720px;
    max-height: 85dvh;
    overflow-y: auto;
    background: linear-gradient(180deg, #1a1a26 0%, #0f0f18 100%);
    /* Border tint follows the modal's data-party (set in openModal). The
       fallback only applies if data-party is absent — which shouldn't
       happen in normal flow but keeps the modal looking right if a future
       caller forgets to set it. */
    border-top: 1px solid var(--party-glow, rgba(255, 255, 255, 0.15));
    border-radius: 16px 16px 0 0;
    padding: 36px 28px 36px;
    transform: translateY(40px);
    transition: transform 0.4s var(--ease-out);
    box-shadow: 0 -20px 60px rgba(0, 0, 0, 0.6);
}

.modal.is-open .modal-sheet {
    transform: translateY(0);
}

.modal-close {
    position: absolute;
    top: 16px;
    right: 16px;
    width: 40px;
    height: 40px;
    border: none;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--ink);
    transition: background 0.2s;
}

.modal-close:hover {
    background: rgba(255, 255, 255, 0.15);
}

.modal-severity {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 3.4rem;
    line-height: 1;
    font-variation-settings: 'opsz' 144;
    margin: 0 0 6px;
    /* Color follows the modal's data-party (set in openModal). Fallback
       to --ink so the number remains visible even if data-party is unset. */
    color: var(--party-glow, var(--ink));
}

.modal-severity::before {
    content: 'Severity ';
    display: block;
    font-family: var(--font-body);
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: 4px;
}

/* "/10" suffix after the severity number — smaller and dimmer so the big
   number still reads as the headline. The ::before above ('Severity ') is
   block-level and stays put. */
.modal-severity::after {
    content: '/10';
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.6rem;
    color: var(--ink-faint);
    letter-spacing: -0.01em;
    margin-left: 2px;
}

.modal-title {
    font-family: var(--font-display);
    font-weight: 900;
    font-size: 1.85rem;
    line-height: 1.05;
    letter-spacing: -0.02em;
    font-variation-settings: 'opsz' 144;
    margin: 0 0 18px;
    color: var(--ink);
}

.modal-description {
    font-family: var(--font-body);
    font-size: 0.98rem;
    line-height: 1.65;
    color: var(--ink-dim);
    margin: 0 0 28px;
}

.modal-sources {
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    padding-top: 18px;
}

/* --------------------------------------------------------------------------
     Animations
     -------------------------------------------------------------------------- */
@keyframes barGrow {
    from {
        transform: scaleX(0);
        opacity: 0;
    }

    to {
        transform: scaleX(1);
        opacity: 1;
    }
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(2px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Same idea as fadeIn but caps at 0.45 so the hint stays subtle. */
@keyframes severityHintFadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 0.45;
    }
}

@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-delay: 0ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* --------------------------------------------------------------------------
     Mobile — under 1000px
     -------------------------------------------------------------------------- */
@media (max-width: 1000px) {
    :root {
        --bar-height: 50px;
        --bar-gap: 12px;
        --side-pad-y: 20px;
    }

    .site-header {
        padding: 44px 20px 32px;
    }

    /* Picker row — the 25%/75% absolute positioning doesn't make sense
       once the sides stack, so this becomes a normal-flow flex row that
       sits between the header and the comparison. Each picker takes
       half the width. Note: the row scrolls away with the page header,
       and the figure-container's sticky behavior takes over from there. */
    .picker-row {
        position: static;
        height: auto;
        display: flex;
        gap: 10px;
        padding: 14px 16px;
        background: linear-gradient(180deg, #14141f 0%, #0e0e18 100%);
        border-bottom: 1px solid rgba(255, 255, 255, 0.06);
        z-index: auto;
    }

    .picker-cell {
        position: static;
        transform: none;
        flex: 1;
    }

    /* Override the desktop 25%/75% values so they don't keep applying. */
    .picker-cell[data-side="left"],
    .picker-cell[data-side="right"] {
        left: auto;
    }

    .president-picker {
        width: 100%;
        min-width: 0;
        font-size: 0.92rem;
        padding: 10px 32px 10px 14px;
    }

    /* Each side becomes a single column with figure as a header strip */
    .side-left,
    .side-right {
        grid-template-columns: 1fr;
        grid-template-rows: auto 1fr;
    }

    .figure-wrap {
        grid-column: 1;
        grid-row: 1;
        height: auto;
    }

    .side-left .figure-wrap,
    .side-right .figure-wrap {
        grid-column: 1;
    }

    .bars-wrap {
        grid-column: 1;
        grid-row: 2;
        padding: 24px 0 32px;
    }

    .side-left .bars-wrap,
    .side-right .bars-wrap {
        grid-column: 1;
    }

    /* Figure becomes a small sticky header.
       Note: the FEN navbar is 40px tall on mobile (from styles.css),
       so we offset this sticky header to sit just below it. */
    .figure-container {
        position: sticky;
        top: 40px;
        height: auto;
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
        padding: 12px 16px;
        background: rgba(0, 0, 0, 0.55);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        border-bottom: 1px solid rgba(255, 255, 255, 0.15);
        z-index: 10;
        gap: 12px;
    }

    .side-right .figure-container {
        flex-direction: row-reverse;
    }

    .silhouette {
        width: 44px;
        height: 44px;
        flex: 0 0 44px;
        min-height: 0;
        filter: none;
    }

    .name-badge {
        position: static;
        padding: 0;
        background: none;
        border: none;
        box-shadow: none;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        text-align: left;
    }

    .side-right .name-badge {
        text-align: right;
    }

    .name-first {
        display: none;
    }

    .name-detail {
        display: none;
    }

    .last-name {
        font-size: 1.2rem;
    }

    /* Bars — labels move INSIDE on mobile */
    .bar-label-outer {
        display: none;
    }

    .bar-label-inner {
        display: block;
        font-family: var(--font-display);
        font-weight: 700;
        font-size: 0.85rem;
        color: var(--ink);
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
        font-variation-settings: 'opsz' 36;
        letter-spacing: -0.005em;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Severity numbers are hidden on mobile — the modal still surfaces
       the value when a bar is expanded, which is enough at this size. */
    .severity-number {
        display: none;
    }

    /* The desktop-only severity hint (← severity level) goes too. */
    .bar:first-child .bar-fill::after {
        display: none;
    }

    /* With severity gone, anchor each label at the centerline edge of
       its bar so labels on both sides line up against the centerline.
       (Single flex item now — flex-direction is just for clarity.) */
    .side-left .bar-fill {
        flex-direction: row;
        justify-content: flex-end;
        padding: 0 14px;
        gap: 0;
    }

    .side-right .bar-fill {
        flex-direction: row;
        justify-content: flex-start;
        padding: 0 14px;
        gap: 0;
    }

    /* Tighter bar widths on mobile so everything fits */
    .bar-fill {
        --bar-width: calc(20% + (var(--severity) * 7.5%));
    }

    /* Hide inline detail on mobile — modal takes over */
    .bar.expanded .bar-detail {
        max-height: 0;
        margin-top: 0;
        opacity: 0;
    }

    /* Push centerline above sticky header on mobile so it stays visible */
    .centerline {
        z-index: 11;
    }

    /* Smaller modal severity number on tiny screens */
    .modal-severity {
        font-size: 2.6rem;
    }

    .modal-title {
        font-size: 1.45rem;
    }
}

/* Even narrower phones */
@media (max-width: 380px) {
    .bar-label-inner {
        font-size: 0.75rem;
    }

    .bar-fill {
        --bar-width: calc(22% + (var(--severity) * 7.3%));
    }
}