/* EU Tracker — colours live here so the three map layers, the legend and the
   panel always agree, and so the whole thing follows the reader's theme. 

   SPDX-License-Identifier: AGPL-3.0-or-later
*/

:root {
  /* The site's own default, not the machine's. An explicit dark choice sets
     this back below, so scrollbars, form controls and the space behind the
     page follow the theme actually on screen rather than the one the phone
     would have chosen. */
  color-scheme: light;

  --eu-blue: #0b3a8f;
  --eu-gold: #f6c700;

  --bg: #f4f5f8;
  --surface: #ffffff;
  --surface-sunken: #eceef3;
  --ink: #131a24;
  --ink-soft: #4d5666;
  --ink-faint: #656c7b;   /* 4.54:1 on the page, not 3.88 */
  --line: #d6dae3;
  --line-strong: #b3bac7;
  --focus: #0b57d0;

  --vote-for: #15785f;
  --vote-against: #b3382c;
  --vote-abstain: #c88a12;
  --vote-absent: #9aa2b1;
  --vote-na: #c3c8d2;

  /* The same four answers, as ink.

     A vote colour has two jobs and they pull in opposite directions. Filling
     a country on the map it should be saturated enough to read across a
     continent at a glance. Set as words — a pill, a count, the result line —
     it has to clear 4.5:1 against whatever is behind it, and a pill puts the
     colour on a 16% tint of ITSELF, which is the least forgiving ground it
     will ever have. Abstain was the worst of them at 2.18:1.

     So the fill keeps its saturation and the ink is darkened until it clears
     4.5:1 on all five grounds it actually appears on: surface, sunken, page,
     and its own tint over each. Nothing on the map moves. */
  --ink-for: #136a54;
  --ink-against: #a73429;
  --ink-abstain: #835a0c;
  --ink-absent: #60656f;

  /* The brand blue reads beautifully as text on white and is invisible on the
     dark theme's surface — 1.63:1 — because it was never given a dark value.
     It is a background colour in most places, so the background keeps it and
     the text uses this. */
  --eu-ink: #0b3a8f;

  /* Words set ON a vote's own fill: the bar segments. Which of white or
     near-black wins depends on the fill, and the fills differ between themes,
     so the answer is stated per theme rather than guessed once. */
  --on-for: #ffffff;
  --on-against: #ffffff;
  --on-abstain: #1c1400;
  --on-absent: #14181f;

  --cost-1: #f6d3b8;
  --cost-2: #e5a06a;
  --cost-3: #c26224;
  --gain-1: #c5e3dd;
  --gain-2: #7cbfb2;
  --gain-3: #2f8b78;
  --zero: #e4e7ee;

  --nato: #8459c4;
  --nato-ink: #744eac;   /* the chip sets it on a 14% tint of itself */

  --context: #dcdfe6;
  --context-line: #ccd1da;

  --radius: 10px;
  --shadow: 0 1px 2px rgba(16, 24, 40, .07), 0 8px 24px rgba(16, 24, 40, .06);
  --font: "IBM Plex Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --serif: "Source Serif 4", "Iowan Old Style", Palatino, Georgia, serif;

  --body-parliament: #5b4bd0;
  --body-council: #10428f;
  --body-commission: #0d6b64;
}

/* Three theme states, not two. A viewer on "system" gets no attribute at all,
   so prefers-color-scheme has to carry it; an explicit choice stamps
   data-theme on the root and must win over the operating system either way. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #10141b;
    --surface: #171d26;
    --surface-sunken: #1e2530;
    --ink: #e8ecf3;
    --ink-soft: #b0b9c8;
    --ink-faint: #8c95a5;
    --line: #2b3441;
    --line-strong: #3d4757;
    --focus: #7aa7ff;
    --vote-for: #2f9c7d;
    --vote-against: #d75b4c;
    --vote-abstain: #d8a53a;
    --vote-absent: #6b7385;
    --vote-na: #3a4351;

    /* Lightened rather than darkened, for the same reason and to the same
       threshold. Abstain already cleared it and is left alone. */
    --ink-for: #50ac92;
    --ink-against: #e07e72;
    --ink-abstain: #d8a53a;
    --ink-absent: #9298a5;

    --eu-ink: #7aa7ff;      /* 7.10:1 on the dark surface */

    /* The dark theme's fills are lighter, so the answers invert. */
    --on-for: #0d1117;
    --on-against: #0d1117;
    --on-abstain: #1c1400;
    --on-absent: #ffffff;
    --cost-1: #5c4331;
    --cost-2: #96602f;
    --cost-3: #d1793a;
    --gain-1: #2c4a47;
    --gain-2: #3f8a7c;
    --gain-3: #5fc0aa;
    --zero: #262d38;
    --nato: #a37fe0;
    --nato-ink: #aa89e2;

    --body-parliament: #9b8ff0;
    --body-council: #6f9ae8;
    --body-commission: #4fb3a8;

    --context: #1c2330;
    --context-line: #29313d;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
    --bg: #10141b;
    --surface: #171d26;
    --surface-sunken: #1e2530;
    --ink: #e8ecf3;
    --ink-soft: #b0b9c8;
    --ink-faint: #8c95a5;
    --line: #2b3441;
    --line-strong: #3d4757;
    --focus: #7aa7ff;
    --vote-for: #2f9c7d;
    --vote-against: #d75b4c;
    --vote-abstain: #d8a53a;
    --vote-absent: #6b7385;
    --vote-na: #3a4351;
    --cost-1: #5c4331;
    --cost-2: #96602f;
    --cost-3: #d1793a;
    --gain-1: #2c4a47;
    --gain-2: #3f8a7c;
    --gain-3: #5fc0aa;
    --zero: #262d38;
    --nato: #a37fe0;
    --nato-ink: #aa89e2;

    --body-parliament: #9b8ff0;
    --body-council: #6f9ae8;
    --body-commission: #4fb3a8;

    --context: #1c2330;
    --context-line: #29313d;
}

* { box-sizing: border-box; }

/* Any element that sets its own display — grid, flex — silently outranks the
   browser's rule for [hidden], and then hiding it from JavaScript does nothing.
   This has caught three components already, so: one rule, everywhere. */
[hidden] { display: none !important; }

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

a { color: var(--focus); }
a:hover { text-decoration-thickness: 2px; }

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  background: var(--surface);
  padding: .6rem 1rem;
  z-index: 20;
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ---------------------------------------------------------------- chrome */

.banner {
  background: var(--eu-blue);
  color: #fff;
  padding: .45rem clamp(1rem, 4vw, 2.5rem);
  font-size: .8rem;
  display: flex;
  flex-wrap: wrap;
  gap: .1rem .5rem;
  align-items: baseline;
}
.banner strong { color: var(--eu-gold); }
.banner a { color: #fff; }
/* The full note lives on the decision itself; the strip only has to warn. */
.banner-note { color: rgba(255, 255, 255, .82); }

.site-header {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem clamp(1rem, 4vw, 2.5rem);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

/* The name is a button, and has to keep looking like the masthead: no chrome,
   just the mark and the words, with the hand and the focus ring a control owes
   a reader. */
.brand {
  display: flex;
  align-items: center;
  gap: .8rem;
  font: inherit;
  text-align: left;
  padding: .2rem;
  margin: -.2rem;
  border: 0;
  border-radius: 10px;
  background: none;
  color: inherit;
  cursor: pointer;
}
.brand:hover .brand-name { color: var(--eu-ink); }
.brand:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.brand-mark {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--eu-blue);
  border: 3px dashed var(--eu-gold);
  flex: none;
}
.brand-name { display: block; font-weight: 700; font-size: 1.15rem; letter-spacing: -.01em; }
.brand-tagline { display: block; color: var(--ink-faint); font-size: .82rem; }

/* The right of the header: the plenary dates and the About link stacked and
   right-aligned, then the two controls beside them. They used to sit inside a
   <p>, which the parser closed at the first <div> — so on a phone the Share
   button and the theme toggle escaped into the header itself and took a row
   each. They are their own element now. */
.header-meta {
  margin: 0 0 0 auto;
  text-align: right;
  font-size: .8rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .1rem;
  min-width: 0;
}
.header-tools { display: flex; align-items: center; gap: .4rem; flex: none; }


/* Light or dark, as a switch rather than a cycle.

   It used to be one round button that stepped light -> dark -> follow the
   system. Three states on a control with one position meant you could only
   learn where you were by reading the label, and "follow the system" was
   reachable only by pressing twice past the theme you wanted. A switch has
   two positions and shows both: the sun and the moon are always in view, and
   the knob says which one you are in.

   The knob is placed from the root attribute, not from the button's state, so
   it is already in the right position when the inline head script has set the
   theme and this script has not run yet. */
.theme-toggle {
  --track-w: 3.3rem;
  --track-h: 1.8rem;
  --knob: 1.4rem;
  --pad: .2rem;
  position: relative;
  width: var(--track-w);
  height: var(--track-h);
  margin-left: .5rem;
  padding: 0;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-sunken);
  cursor: pointer;
}
.theme-toggle:hover { border-color: var(--ink-faint); }
.theme-toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* The white disc. It sits above the icons, so whichever one it covers reads
   against it rather than against the track. */
.theme-toggle-knob {
  position: absolute;
  top: 50%;
  left: var(--pad);
  width: var(--knob);
  height: var(--knob);
  margin-top: calc(var(--knob) / -2);
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(19, 26, 36, .22);
  pointer-events: none;
}
/* On a dark track a dark disc reads as a shadow rather than a knob, and the
   drop shadow that separates it in the light theme does nothing against a
   near-black ground. A hairline is what carries the edge instead. */
:root[data-theme="dark"] .theme-toggle-knob {
  box-shadow: 0 0 0 1px var(--line-strong), 0 1px 3px rgba(0, 0, 0, .45);
}
:root[data-theme="dark"] .theme-toggle-knob {
  transform: translateX(calc(var(--track-w) - var(--knob) - var(--pad) * 2 - 2px));
}
.theme-toggle-icon {
  position: relative;
  z-index: 1;
  width: var(--knob);
  height: var(--knob);
  margin: 0 var(--pad);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-faint);
  pointer-events: none;
}
.theme-toggle-icon svg {
  width: .95rem;
  height: .95rem;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
}
/* The icon under the knob is the one you are in, so it is the one that is
   spelled out; the other stays quiet. */
:root:not([data-theme="dark"]) .theme-toggle .is-sun,
:root[data-theme="dark"] .theme-toggle .is-moon { color: var(--ink); }

/* No data-theme at all is a visit that chose "follow the system" back when
   that was a third position. Such a page is painted from the machine's
   setting until theme.js resolves the choice, so the switch reads from the
   same place for those few milliseconds rather than pointing the wrong way. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle-knob {
    transform: translateX(calc(var(--track-w) - var(--knob) - var(--pad) * 2 - 2px));
  }
  :root:not([data-theme]) .theme-toggle .is-sun { color: var(--ink-faint); }
  :root:not([data-theme]) .theme-toggle .is-moon { color: var(--ink); }
}

@media (prefers-reduced-motion: no-preference) {
  .theme-toggle-knob { transition: transform .22s cubic-bezier(.2, .7, .3, 1); }
  .theme-toggle-icon { transition: color .22s ease; }
}

/* No switch on a phone. The site is light there and there is nothing to
   choose, so the control would be a lever attached to nothing. The width is
   the same one at which the header becomes the phone header, and theme.js
   holds the theme to light across exactly this range — the switch is never
   hidden while the theme it controls is still dark. */
@media (max-width: 40rem) {
  .theme-toggle { display: none; }
}

.is-loaded {
  opacity: 1;
}

.loading-thread {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--eu-blue) 0%, var(--eu-gold) 100%);
  background-size: 200% 100%;
  animation: loadingProgress 2s linear infinite;
  z-index: 100;
}

@keyframes loadingProgress {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Skeleton loading for decision cards */
.decision-loading {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem;
  animation: pulse 1.5s ease-in-out infinite;
}

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

/* Error states */
.error-message {
  background: color-mix(in srgb, var(--vote-against) 15%, var(--surface));
  border: 1px solid var(--vote-against);
  color: var(--ink-against);
  padding: 0.75rem 1rem;
  border-radius: var(--radius);
  font-size: 0.875rem;
}

.error-message::before {
  content: "⚠ ";
}

/* Empty states */
.empty-state {
  text-align: center;
  padding: 2rem;
  color: var(--ink-faint);
}

.empty-state svg

/* Country tooltip on map hover */
.country-tooltip {
  position: absolute;
  top: 0;
  left: 0;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ink);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  z-index: 1000;
  transition: opacity 0.2s ease, transform 0.2s ease;
  opacity: 0;
  transform: translateY(-4px);
}

.country-tooltip.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.country-tooltip::after {
  content: '';
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 6px solid var(--line-strong);
}



/* Enhanced mobile responsiveness */
@media (max-width: 62rem) {
  /* Larger touch targets for map countries on mobile */
  .country-small .country-hit {
    r: 14px;
  }
  
  .country-small .country-dot {
    r: 5.5px;
  }
  
  /* Better spacing for touch */
  .map-wrap {
    -webkit-tap-highlight-color: transparent;
  }
  
  /* Search improvements for mobile */
  .search input[type="search"] {
    font-size: 1rem;
    padding: 0.75rem 1rem;
  }
  
  /* Filter buttons on mobile */
  .tracker-filters {
    flex-wrap: wrap;
    gap: 0.5rem;
  }
  
  .tracker-filters button {
    flex: 1 0 auto;
    min-width: calc(50% - 0.5rem);
  }
}

/* Focus visible for keyboard navigation */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Skip link for keyboard users */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: var(--eu-blue);
  color: white;
  padding: 0.5rem 1rem;
  z-index: 100;
  text-decoration: none;
  font-weight: 600;
  border-radius: 0 0 var(--radius) var(--radius);
}

.skip-link:focus {
  top: 0;
}

/* On a phone the header ran to a third of the screen. It is two rows now: the
   brand with the two controls beside it, then the plenary dates and the About
   link along the bottom in small type. */
@media (max-width: 40rem) {
  .site-header {
    padding: .7rem 1rem;
    gap: .4rem .75rem;
    align-items: center;
  }
  .brand { gap: .55rem; flex: 1 1 auto; min-width: 0; }
  .brand-mark { width: 26px; height: 26px; border-width: 2px; }
  .brand-name { font-size: 1.02rem; }
  .brand-tagline { font-size: .74rem; line-height: 1.35; }
  .header-tools { order: 1; margin-left: auto; }
  .header-meta {
    order: 2;
    width: 100%;
    margin: 0;
    text-align: left;
    font-size: .73rem;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: flex-start;
    gap: .1rem .7rem;
  }
}
#header-plenary { color: var(--ink-soft); font-weight: 500; }

main {
  max-width: 1560px;
  margin: 0 auto;
  padding: clamp(1rem, 3vw, 2rem) clamp(1rem, 4vw, 2.5rem) 4rem;
  display: grid;
  gap: clamp(1rem, 2vw, 1.5rem);
  /* The map is the thing people came for, so it takes the larger share of the
     width and the reading column takes what it needs and no more. */
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  align-items: start;
}
.table-section { grid-column: 1 / -1; }

/* The rail is the reading column: what is being tracked, then the decision,
   then the country that was clicked. */
.rail {
  display: grid;
  gap: clamp(1rem, 2vw, 1.5rem);
  min-width: 0;
}

/* The country comes first, always: its record when one is open, and the
   invitation to pick one when none is. Either way the map's answer sits at the
   top of the reading column rather than below the plenary sessions and a
   roll-call of seven hundred names. */
.rail #country-panel { order: -1; }

/* A grid item will not shrink below its content unless told to. Without this a
   five-column table inside the panel makes a 320-pixel phone scroll sideways. */
.rail > * { min-width: 0; }

/* When something is scrolled into view — a country tapped on a phone, the
   roll-call jumped to from the panel — it should land a little below the top
   of the screen rather than flush against it. */
#country-panel, .panel-body, .decision-brief, #decision-section, #member-section, #roll { scroll-margin-top: 1.75rem; }
.map-section { min-width: 0; }

@media (max-width: 62rem) {
  main { grid-template-columns: minmax(0, 1fr); }

  /* One column, and the reading column dissolves into it — its sections become
     items of the page's own grid, so they can be ordered against the map
     instead of only among themselves.

     The order is what the reader came for. A link to a vote, opened from a
     message, has to arrive on that vote: what it was, how it went, who was
     for and against. The same for a link to a member. Then the map, then the
     country if one is open, then the sessions. With nothing open the first two
     are hidden and it reads as before: map, country, sessions. */
  .rail { display: contents; }

  /* The vote splits in two here: what it was and how it went above the map,
     the roll-call of seven hundred names below it. :not([hidden]) matters —
     display on an id would otherwise beat the browser's own rule for hidden
     and show a closed section's insides. */
  #decision-section:not([hidden]) { display: contents; }
  .decision-brief, #decision-section > .roll {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: clamp(1.1rem, 2.5vw, 1.6rem);
    min-width: 0;
  }
  #decision-section > .roll { margin-top: 0; }

  /* Inside the brief, the result comes before the paperwork: on a phone the
     source links and the share row were filling the screen the outcome should
     have been on. */
  .decision-brief { display: flex; flex-direction: column; }
  .decision-brief #outcome { order: 1; }
  .decision-brief #vote-links { order: 2; }
  .decision-brief #vote-share { order: 3; }

  .decision-brief, #member-section { order: 1; }
  .map-section { order: 2; }
  #decision-section > .roll { order: 3; }
  .rail #country-panel { order: 4; }
  #tracker { order: 5; }

  /* The map card gives its padding back to the map on a phone. */
  .map-section { padding: .85rem .7rem 1rem; }
  .map-hint { font-size: .8rem; margin-bottom: .6rem; }
}

/* ---------------------------------------------------------------- decision */

.decision {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
}

.decision-meta { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: 0 0 .5rem; color: var(--ink-faint); font-size: .85rem; }

.chip {
  display: inline-block;
  padding: .18rem .55rem;
  border-radius: 999px;
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .02em;
  background: var(--surface-sunken);
  color: var(--ink-soft);
  border: 1px solid var(--line);
}
.chip-sample { background: color-mix(in srgb, var(--eu-gold) 22%, var(--surface)); border-color: var(--eu-gold); color: var(--ink); }

#decision-title {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  line-height: 1.15;
  margin: 0 0 .3rem;
  letter-spacing: -.012em;
  text-wrap: balance;
}
.decision-subtitle { margin: 0 0 .9rem; color: var(--ink-soft); font-size: 1.02rem; }
.decision-summary { margin: 0 0 1.2rem; max-width: 64ch; }

.decision-detail { margin-top: 1.1rem; border-top: 1px solid var(--line); padding-top: .9rem; }
.decision-detail summary { cursor: pointer; font-weight: 600; color: var(--ink-soft); font-size: .9rem; }
.decision-detail ul { margin: .7rem 0; padding-left: 1.1rem; color: var(--ink-soft); }
.decision-detail li { margin-bottom: .3rem; }
.rule { font-size: .9rem; color: var(--ink-soft); }
.sources { font-size: .88rem; }
.data-note { color: var(--ink-faint); font-style: italic; list-style: none; margin-left: -1.1rem; margin-top: .4rem; }

.outcome {
  background: var(--surface-sunken);
  border-radius: var(--radius);
  padding: 1.1rem;
  align-self: start;
}
.outcome-result { margin: 0 0 .9rem; font-weight: 650; font-size: 1.05rem; }
.outcome-adopted { color: var(--ink-for); }
.outcome-rejected, .outcome-blocked { color: var(--ink-against); }
.outcome-note { font-size: .85rem; color: var(--ink-soft); margin: .8rem 0 0; }

.tally { display: grid; grid-template-columns: repeat(4, 1fr); gap: .5rem; }
.tally-cell { background: var(--surface); border-radius: 8px; padding: .6rem .5rem; text-align: center; border-top: 3px solid var(--line-strong); }
.tally-for { border-top-color: var(--vote-for); }
.tally-against { border-top-color: var(--vote-against); }
.tally-abstain { border-top-color: var(--vote-abstain); }
.tally-absent { border-top-color: var(--vote-absent); }
.tally-number { display: block; font-size: 1.4rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.tally-label { display: block; font-size: .72rem; color: var(--ink-faint); }

.meters { display: grid; gap: .9rem; }
.meter-label { margin: 0 0 .3rem; font-size: .82rem; color: var(--ink-soft); }
.meter-track { position: relative; height: 12px; background: var(--surface); border-radius: 999px; overflow: hidden; border: 1px solid var(--line); }
.meter-fill { position: absolute; inset: 0 auto 0 0; background: var(--vote-absent); }
.meter.met .meter-fill { background: var(--vote-for); }
.meter.unmet .meter-fill { background: var(--vote-against); }
.meter-threshold { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--ink); opacity: .75; }
.meter-value { margin: .3rem 0 0; font-size: .8rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- map */

.map-section {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1rem, 2.5vw, 1.5rem);
}

/* The map follows the page down. Sticky is allowed only where it can behave:
   a two-column layout, and a window tall enough that the whole card — map,
   legend and all — fits on screen at once. The card is start-aligned in its
   grid area and that area ends where the full-width table begins, so the map
   releases by itself at the bottom of the row instead of riding over what
   comes after it. Anywhere else it simply scrolls like everything else. */
@media (min-width: 62.0625rem) and (min-height: 30rem) {
  .map-section {
    position: sticky;
    top: 1rem;
    align-self: start;
  }
}

.layer-tabs { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: 1rem; }
.layer-tabs button {
  font: inherit;
  font-size: .88rem;
  font-weight: 600;
  padding: .5rem .9rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
}
.layer-tabs button[aria-pressed="true"] { background: var(--eu-blue); border-color: var(--eu-blue); color: #fff; }
.layer-tabs button:hover { border-color: var(--ink-faint); color: var(--ink); }
.layer-tabs button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* The way back reads as a way back, not as another thing to switch on. */
.layer-back { border-style: dashed; }
.layer-back:hover { background: var(--surface-sunken); }

.map-heading {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.map-hint { margin: 0 0 .9rem; font-size: .85rem; color: var(--ink-faint); }
/* Said to a screen reader, out of the way for everyone else. */
.map-hint.is-quiet {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.map-wrap { position: relative; }
.map { position: relative; }
.eu-map {
  width: 100%;
  height: auto;
  max-height: min(78vh, 880px);
  display: block;
  margin-inline: auto;
  touch-action: manipulation;
}
@media (max-width: 62rem) { .eu-map { max-height: none; } }
/* Where the map is pinned it has to leave room for its own chrome — tabs,
   hint and legend, which together run to about fifteen rems — so the pinned
   card never grows taller than the screen it is pinned to. The map shrinks to
   fit a short window rather than the pin switching itself off. */
@media (min-width: 62.0625rem) and (min-height: 30rem) {
  .eu-map { max-height: min(calc(100svh - 15rem), 720px); }
}

/* The neighbours. Present enough to place the Union, quiet enough that the
   member states remain the only thing the eye lands on. */
.context-shape {
  fill: var(--context);
  stroke: var(--context-line);
  stroke-width: .6;
  stroke-linejoin: round;
  transition: fill .15s ease;
}
.outside { cursor: pointer; }
.outside:hover .context-shape { fill: color-mix(in srgb, var(--context) 80%, var(--ink)); }
.outside:focus { outline: none; }
.outside:focus-visible .context-shape { stroke: var(--focus); stroke-width: 2; }
.outside.is-selected .context-shape { stroke: var(--ink-soft); stroke-width: 1.2; }

.country { cursor: pointer; }
.country-shape { stroke: var(--surface); stroke-width: .8; stroke-linejoin: round; fill: var(--vote-na); transition: fill .15s ease; }
.country-hit { fill: transparent; }
.country-dot { fill: var(--vote-na); stroke: var(--surface); stroke-width: 1.2; }
.country:hover .country-shape, .country:focus-visible .country-shape { stroke: var(--ink); stroke-width: 1.4; }
.country.is-selected .country-shape { stroke: var(--ink); stroke-width: 2.2; }
.country.is-selected .country-dot { stroke: var(--ink); stroke-width: 2; }
.country:focus { outline: none; }
.country:focus-visible .country-shape { stroke: var(--focus); stroke-width: 2.4; }

/* ------------------------------------------------- the Union as one country

   On the way home the map says what the site is called. The internal
   frontiers fade out, every member takes the same colour, and the outline of
   the whole is traced in the Union's own gold; then the borders come back and
   the map is itself again.

   The outline is not a computed shape. It is one stroked copy of every member
   drawn underneath the filled ones: a stroke is centred on its path, so each
   country's own fill covers the inner half and a neighbour's fill covers the
   shared half of every border between them. What no fill covers is the
   Union's own coastline and land frontier. */
.union-edge {
  fill: none;
  stroke: var(--eu-gold);
  stroke-width: 0;
  stroke-linejoin: round;
  stroke-linecap: round;
  opacity: 0;
}
.eu-map.is-united .union-edge {
  stroke-width: 9;
  opacity: .95;
  transition: stroke-width .45s cubic-bezier(.2, .8, .3, 1), opacity .3s ease;
}
.eu-map.is-united-lit .union-edge { filter: drop-shadow(0 0 6px rgba(246, 199, 0, .75)); }
.eu-map.is-parting .union-edge { stroke-width: 0; opacity: 0; transition: stroke-width .4s ease, opacity .4s ease; }

/* One body: the frontiers between members go, and with them the difference
   between one country and the next. */
.eu-map.is-united .country-shape {
  fill: var(--eu-blue);
  stroke: var(--eu-blue);
  stroke-width: 1.1;
  transition: fill .5s ease, stroke .5s ease, stroke-width .5s ease;
}
.eu-map.is-united .country-dot { fill: var(--eu-blue); stroke: var(--eu-blue); }
/* The names would be a list of the parts, which is the opposite of the point. */
.eu-map.is-united .labels { opacity: 0; transition: opacity .35s ease; }
/* Coming back, the borders return a moment before the colours do, so the map
   reads as the Union resolving into its members. */
.eu-map.is-parting .country-shape { stroke: var(--surface); stroke-width: .8; transition: stroke .2s ease, stroke-width .2s ease, fill .45s ease .1s; }
.eu-map.is-parting .labels { opacity: 1; transition: opacity .4s ease .15s; }
/* Nothing to hover while the Union is one thing. */
.eu-map.is-united .country { pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .eu-map.is-united .union-edge,
  .eu-map.is-united .country-shape,
  .eu-map.is-united .labels,
  .eu-map.is-parting .union-edge,
  .eu-map.is-parting .country-shape,
  .eu-map.is-parting .labels { transition: none; }
  .eu-map.is-united-lit .union-edge { filter: none; }
}

/* White with a dark halo: the same label has to read on a dark green fill and
   on the pale grey of a country with nothing recorded. */
.country-label { font-size: 12px; font-weight: 700; fill: #fff; text-anchor: middle; dominant-baseline: central; paint-order: stroke; stroke: rgba(0,0,0,.6); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }
/* A name that sits outside its country is drawn exactly like every other one,
   only smaller. It was dark type on a thick pale halo, which was legible over
   the sea and read as highlighter pen everywhere else — and Luxembourg's,
   Slovenia's and the Netherlands' all land on the blue. White letters cut out
   of a dark outline read on the blue, on the grey and on the sea alike. */
.country-label-small { font-size: 11px; }
.label-leader { stroke: var(--ink-faint); stroke-width: .7; opacity: .65; }


/* Vote layer */
.layer-vote .country-shape, .layer-vote .country-dot { fill: var(--vote-na); }
.vote-for .country-shape, .vote-for .country-dot { fill: var(--vote-for); }
.vote-against .country-shape, .vote-against .country-dot { fill: var(--vote-against); }
.vote-abstain .country-shape, .vote-abstain .country-dot { fill: var(--vote-abstain); }
.vote-absent .country-shape, .vote-absent .country-dot { fill: var(--vote-absent); }
.vote-not-applicable .country-shape, .vote-not-applicable .country-dot { fill: var(--vote-na); }

/* Impact layer */
.impact-cost-1 .country-shape, .impact-cost-1 .country-dot { fill: var(--cost-1); }
.impact-cost-2 .country-shape, .impact-cost-2 .country-dot { fill: var(--cost-2); }
.impact-cost-3 .country-shape, .impact-cost-3 .country-dot { fill: var(--cost-3); }
.impact-gain-1 .country-shape, .impact-gain-1 .country-dot { fill: var(--gain-1); }
.impact-gain-2 .country-shape, .impact-gain-2 .country-dot { fill: var(--gain-2); }
.impact-gain-3 .country-shape, .impact-gain-3 .country-dot { fill: var(--gain-3); }
.impact-zero .country-shape, .impact-zero .country-dot,
.impact-none .country-shape, .impact-none .country-dot { fill: var(--zero); }

.map-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  background: var(--ink);
  color: var(--bg);
  padding: .45rem .6rem;
  border-radius: 6px;
  font-size: .82rem;
  line-height: 1.35;
  max-width: 15rem;
  box-shadow: var(--shadow);
}
.map-tip strong { display: block; }

.legend { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: .9rem; }

.legend ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .35rem 1rem; font-size: .82rem; color: var(--ink-soft); }
.legend li { display: flex; align-items: center; gap: .4rem; }
.swatch { width: 15px; height: 15px; border-radius: 4px; border: 1px solid var(--line-strong); background: var(--vote-na); display: inline-block; }
.swatch.vote-for { background: var(--vote-for); }
.swatch.vote-against { background: var(--vote-against); }
.swatch.vote-abstain { background: var(--vote-abstain); }
.swatch.vote-absent { background: var(--vote-absent); }
.swatch.vote-not-applicable { background: var(--vote-na); }
.swatch.impact-cost-1 { background: var(--cost-1); }
.swatch.impact-cost-2 { background: var(--cost-2); }
.swatch.impact-cost-3 { background: var(--cost-3); }
.swatch.impact-gain-1 { background: var(--gain-1); }
.swatch.impact-gain-2 { background: var(--gain-2); }
.swatch.impact-gain-3 { background: var(--gain-3); }
.swatch.impact-zero { background: var(--zero); }
.swatch.layer-neutral { background: var(--eu-blue); border-color: var(--eu-blue); }
.swatch.swatch-context { background: var(--context); border-color: var(--context-line); }

/* ---------------------------------------------------------------- motion */

/* The reveal. The Union takes the colour of the outcome, holds, then each
   member state turns to its own vote in a sweep from west to east. */
.eu-map.is-revealing .country:not(.revealed) .country-shape,
.eu-map.is-revealing .country:not(.revealed) .country-dot {
  fill: var(--reveal-fill, var(--vote-absent));
}
.eu-map.is-revealing.result-adopted { --reveal-fill: var(--vote-for); }
.eu-map.is-revealing.result-blocked,
.eu-map.is-revealing.result-rejected { --reveal-fill: var(--vote-against); }
.eu-map.is-revealing.result-recorded { --reveal-fill: var(--vote-absent); }

.eu-map.is-revealing .shapes { animation: outcome-pulse 1.1s ease-in-out infinite; }
@keyframes outcome-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .78; }
}

.country-shape, .country-dot { transition: fill .45s ease, opacity .25s ease; }

/* Emphasis: hover links the map and the table, dimming isolates one group. */
.country.is-hovered .country-shape { stroke: var(--ink); stroke-width: 1.6; }
.country.is-dimmed { opacity: .22; }
.country.is-dimmed.is-selected { opacity: 1; }

tbody tr.is-hovered { background: var(--surface-sunken); }
tbody tr.is-dimmed { opacity: .35; }

.meter-fill { transition: width .9s cubic-bezier(.25, .8, .3, 1); }


/* ---------------------------------------------------------------- numbers */

/* A vote count reads faster in the colour of the vote it counts. */
.n-for { color: var(--ink-for); font-weight: 600; }
.n-against { color: var(--ink-against); font-weight: 600; }
.n-abstain { color: var(--ink-abstain); font-weight: 600; }
.n-absent { color: var(--ink-faint); font-weight: 600; }
.n-for, .n-against, .n-abstain, .n-absent { font-variant-numeric: tabular-nums; }

.tally-for .tally-number { color: var(--ink-for); }
.tally-against .tally-number { color: var(--ink-against); }
.tally-abstain .tally-number { color: var(--ink-abstain); }
.tally-absent .tally-number { color: var(--ink-faint); }

.meter.met .meter-value strong { color: var(--ink-for); }
.meter.unmet .meter-value strong { color: var(--ink-against); }
.meter-value strong { font-weight: 600; font-variant-numeric: tabular-nums; }

/* The legend is a control, not a caption: each entry isolates its group. */
.legend li button {
  display: flex;
  align-items: center;
  gap: .4rem;
  font: inherit;
  font-size: .82rem;
  color: var(--ink-soft);
  background: transparent;
  border: 0;
  padding: .1rem .2rem;
  border-radius: 6px;
  cursor: pointer;
}
.legend li button:hover { background: var(--surface-sunken); color: var(--ink); }
.legend li button[aria-pressed="true"] { background: var(--ink); color: var(--surface); }
.legend-hint { margin: .5rem 0 0; font-size: .74rem; color: var(--ink-faint); }

@media (prefers-reduced-motion: reduce) {
  .eu-map.is-revealing .shapes { animation: none; }
  .country-shape, .country-dot, .meter-fill { transition: none; }
}

/* When no vote is open, the Union is the subject: member states in the
   Union's own blue, everything else in the same grey as always. */
.layer-neutral .country-shape, .layer-neutral .country-dot { fill: var(--eu-blue); }
.layer-neutral.is-hovered .country-shape { fill: color-mix(in srgb, var(--eu-blue) 82%, white); }

.back-to-votes {
  font: inherit;
  font-size: .8rem;
  font-weight: 600;
  color: var(--focus);
  background: transparent;
  border: 0;
  padding: 0;
  margin-right: .4rem;
  cursor: pointer;
}
.back-to-votes:hover { text-decoration: underline; }

.neutral-note {
  margin: 0;
  font-size: .9rem;
  color: var(--ink-soft);
  max-width: 60ch;
}

/* ---------------------------------------------------------------- panel */

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1rem, 2.5vw, 1.5rem);
}

.panel-empty h2 { margin-top: 0; font-family: var(--serif); }
.panel-empty p { color: var(--ink-soft); }

.panel-head { border-bottom: 1px solid var(--line); padding-bottom: .9rem; margin-bottom: 1rem; }
.panel-eyebrow { margin: 0; font-size: .78rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); }
.panel-head h2 { font-family: var(--serif); font-size: 1.75rem; margin: .2rem 0 .35rem; }
.panel-facts { margin: 0; font-size: .85rem; color: var(--ink-soft); }
.panel-actions { margin: .6rem 0 0; font-size: .82rem; }

/* The seat count opens the list of members who hold those seats. It reads as
   part of the line it sits in until you go near it, when it says it is a
   control — an underline, a caret that turns over, and nothing more. */
.seat-toggle {
  font: inherit;
  color: var(--ink);
  font-weight: 600;
  background: none;
  border: 0;
  border-radius: 5px;
  padding: .1rem .25rem;
  margin-left: -.25rem;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--ink-faint) 55%, transparent);
  text-underline-offset: 3px;
}
.seat-toggle:hover {
  background: var(--surface-sunken);
  text-decoration-color: currentColor;
}
.seat-caret {
  display: inline-block;
  margin-left: .3rem;
  font-size: .7em;
  color: var(--ink-faint);
}
.seat-toggle[aria-expanded="true"] .seat-caret { transform: rotate(180deg); }
@media (prefers-reduced-motion: no-preference) {
  .seat-caret { transition: transform .15s ease; }
}

/* The list itself: the same rows as a search result, so a member looks the
   same wherever you meet them. Capped and scrollable — Germany sends a
   hundred, and they should not push the rest of the panel off the screen. */
.country-meps { margin-top: .7rem; }
.country-meps ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .15rem;
  max-height: 19rem;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: .3rem;
  background: var(--surface-sunken);
}
/* A row is the mark, then the name over the group and the count. Laid out as
   a grid rather than a wrapping line: group names run long — "Greens /
   European Free Alliance" — and on a narrow panel a flex row broke them into
   a ragged block beside the name. */
.country-meps .mep-hit {
  background: var(--surface);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0 .5rem;
  padding: .35rem .45rem;
}
.country-meps .mep-hit .bd-tile,
.country-meps .mep-hit .face { grid-row: span 2; }
.country-meps .mep-name { grid-column: 2; }
.country-meps .mep-meta { grid-column: 2; flex: none; line-height: 1.3; }
.seat-note {
  margin: 0 0 .45rem;
  font-size: .76rem;
  color: var(--ink-faint);
}

/* Membership chips. A filled chip is a club the country is in; an outlined one
   is a club it stayed out of, which is as much of the story. */
.blocs { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .6rem; }
.bloc {
  font: inherit;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .01em;
  padding: .16rem .55rem;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
}
span.bloc { cursor: default; }
.bloc-in { background: color-mix(in srgb, var(--eu-blue) 12%, transparent); color: var(--eu-blue); border-color: color-mix(in srgb, var(--eu-blue) 30%, transparent); }
.bloc-out { background: transparent; color: var(--ink-faint); border-color: var(--line-strong); border-style: dashed; }
.bloc-in.bloc-euro { background: color-mix(in srgb, var(--vote-for) 12%, transparent); color: var(--ink-for); border-color: color-mix(in srgb, var(--vote-for) 32%, transparent); }
.bloc-in.bloc-nato { background: color-mix(in srgb, var(--nato) 14%, transparent); color: var(--nato-ink); border-color: color-mix(in srgb, var(--nato) 32%, transparent); }
.bloc-in.bloc-candidate { background: color-mix(in srgb, var(--eu-gold) 24%, transparent); color: var(--ink); border-color: color-mix(in srgb, var(--eu-gold) 60%, transparent); }
button.bloc:hover { filter: brightness(.94); }
button.bloc[aria-pressed="true"] { background: var(--ink); color: var(--surface); border-color: var(--ink); border-style: solid; }
.bloc-notes { margin: .45rem 0 0; font-size: .78rem; color: var(--ink-faint); display: grid; gap: .15rem; }
.bloc-note strong { color: var(--ink-soft); font-weight: 600; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bloc-in { color: var(--focus); }
  :root:not([data-theme="light"]) .bloc-in.bloc-euro { color: var(--ink-for); }
  :root:not([data-theme="light"]) .bloc-in.bloc-nato { color: var(--nato-ink); }
}
:root[data-theme="dark"] .bloc-in { color: var(--focus); }
:root[data-theme="dark"] .bloc-in.bloc-euro { color: var(--ink-for); }
:root[data-theme="dark"] .bloc-in.bloc-nato { color: var(--nato-ink); }

.card { border-top: 1px solid var(--line); padding-top: 1rem; margin-top: 1rem; }
.card:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.card h3 { font-size: .8rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); margin: 0 0 .6rem; }

.position { font-size: 1.3rem; font-weight: 700; margin: 0 0 .35rem; }
.position-for { color: var(--ink-for); }
.position-against { color: var(--ink-against); }
.position-abstain { color: var(--ink-abstain); }
.position-absent, .position-unknown { color: var(--ink-faint); }
.position-detail { margin: 0 0 .8rem; font-size: .88rem; color: var(--ink-soft); }
.no-vote { margin: 0; font-size: .92rem; color: var(--ink-soft); }


.vote-bar { display: flex; height: 14px; border-radius: 999px; overflow: hidden; background: var(--surface-sunken); margin-bottom: 1rem; }
.seg-for { background: var(--vote-for); }
.seg-against { background: var(--vote-against); }
.seg-abstain { background: var(--vote-abstain); }
.seg-absent { background: var(--vote-absent); }

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

/* A table narrower than its columns scrolls inside its own box rather than
   taking the page with it. */
/* The shadows appear only on the side that has more table behind it, and slide
   away as you scroll to that end — so a table too wide for a phone says so,
   instead of looking cut off. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(to right, var(--surface) 40%, transparent) left center / 2.2rem 100% no-repeat,
    linear-gradient(to left, var(--surface) 40%, transparent) right center / 2.2rem 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--ink) 16%, transparent), transparent)
      left center / .7rem 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--ink) 16%, transparent), transparent)
      right center / .7rem 100% no-repeat;
  background-attachment: local, local, scroll, scroll;
}
.table-scroll table { min-width: 17rem; background: var(--surface); }

/* On a narrow phone the five columns fit only if they are asked to. Tightening
   them beats a table clipped mid-column, which reads as a broken page rather
   than as something you can scroll. */
@media (max-width: 26rem) {
  .group-table { font-size: .78rem; }
  .group-table th, .group-table td { padding: .3rem .22rem; }
  .group-table thead th { font-size: .6rem; letter-spacing: .02em; }
  .group-cell { gap: .35rem; }
  .bd-tile-swatch { width: 1.8rem; min-width: 1.8rem; height: 1.3rem; }
}
caption { text-align: left; font-size: .78rem; color: var(--ink-faint); padding-bottom: .4rem; }
th, td { text-align: left; padding: .35rem .4rem; border-bottom: 1px solid var(--line); }
thead th { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-faint); font-weight: 600; }
.group-table td, .mep-table td { font-variant-numeric: tabular-nums; }
.cell-for { color: var(--ink-for); font-weight: 600; }
.cell-against { color: var(--ink-against); font-weight: 600; }
.cell-abstain { color: var(--ink-abstain); }
.cell-absent { color: var(--ink-faint); }

.vote-pill {
  display: inline-block; font-size: .72rem; font-weight: 650;
  padding: .12rem .5rem; border-radius: 999px;
  background: var(--surface-sunken); color: var(--ink-soft); border: 1px solid var(--line);
}
.vote-pill.vote-for { background: color-mix(in srgb, var(--vote-for) 16%, transparent); color: var(--ink-for); border-color: color-mix(in srgb, var(--vote-for) 40%, transparent); }
.vote-pill.vote-against { background: color-mix(in srgb, var(--vote-against) 16%, transparent); color: var(--ink-against); border-color: color-mix(in srgb, var(--vote-against) 40%, transparent); }
.vote-pill.vote-abstain { background: color-mix(in srgb, var(--vote-abstain) 18%, transparent); color: var(--ink-abstain); border-color: color-mix(in srgb, var(--vote-abstain) 40%, transparent); }

.impact-value { font-size: 1.5rem; font-weight: 700; margin: 0 0 .3rem; font-variant-numeric: tabular-nums; }
.impact-cost { color: var(--cost-3); }
.impact-gain { color: var(--gain-3); }
.impact-note, .impact-source { margin: 0; font-size: .84rem; color: var(--ink-soft); }
.empty { margin: 0; font-size: .88rem; color: var(--ink-faint); }

.country-note { font-size: .85rem; color: var(--ink-soft); }

/* ---------------------------------------------------------------- table */

.table-section {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1rem, 2.5vw, 1.5rem);
}
.table-section h2 { font-family: var(--serif); margin: 0 0 .2rem; font-size: 1.3rem; }
.table-hint { margin: 0 0 1rem; font-size: .85rem; color: var(--ink-faint); }
.table-scroll { overflow-x: auto; }
.table-empty { margin: 0; font-size: .9rem; color: var(--ink-soft); max-width: 70ch; }
#country-table { min-width: 44rem; }
#country-table thead button {
  font: inherit; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--ink-faint); font-weight: 600;
}
#country-table thead button:hover { color: var(--ink); }
#country-table tbody tr.is-selected { background: color-mix(in srgb, var(--eu-blue) 10%, transparent); }
.numeric { font-variant-numeric: tabular-nums; }
.link-button { font: inherit; background: none; border: 0; padding: 0; cursor: pointer; color: var(--focus); text-align: left; font-weight: 600; }

/* ---------------------------------------------------------------- intro */

.intro { margin-bottom: 1rem; }
.intro-lead {
  margin: 0 0 .35rem;
  font-family: var(--serif);
  font-size: clamp(1.02rem, 1.6vw, 1.2rem);
  line-height: 1.4;
  color: var(--ink);
  max-width: 46ch;
  text-wrap: balance;
}
.intro-stats {
  margin: 0;
  font-size: .78rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}

.header-about { font-weight: 600; }

.chip-planned {
  background: transparent;
  border: 1px dashed var(--line-strong);
  color: var(--ink-faint);
  text-transform: uppercase;
  font-size: .62rem;
  letter-spacing: .08em;
  vertical-align: middle;
  margin-left: .3rem;
}

.show-in-roll {
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  color: var(--focus);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
}
.show-in-roll:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- search */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.search { display: flex; gap: .4rem; margin-bottom: .7rem; }
.search input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: .9rem;
  padding: .5rem .75rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
}
.search input::placeholder { color: var(--ink-faint); }
.search button {
  font: inherit;
  font-size: .8rem;
  padding: .3rem .7rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
}
.search-status { margin: 0 0 .6rem; font-size: .8rem; color: var(--ink-faint); }

.mep-results { margin-top: .9rem; border-top: 1px solid var(--line); padding-top: .8rem; }
.mep-results h3 { font-size: .78rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); margin: 0 0 .5rem; }
.mep-results ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .25rem; max-height: 16rem; overflow-y: auto; }
.mep-hit {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .2rem .5rem;
  font: inherit;
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: .3rem .4rem;
  cursor: pointer;
  color: inherit;
}
/* ------------------------------------------------------------------ faces

   A member's official portrait, over their initials on the colour of their
   political group. The initials are drawn first and the photograph on top: if
   it never arrives — the Parliament has not published one, or the network is
   having a bad day — what stays is a face-shaped thing with the right name on
   it rather than a broken image. */
.face {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 50%;
  background: var(--surface-sunken);
  color: var(--ink-soft);
  font-weight: 700;
  line-height: 1;
  user-select: none;
}
.face-initials { font-size: .42em; letter-spacing: .02em; }
.face-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 22%;   /* the head, not the middle of the frame */
  opacity: 0;
}
/* Shown only once it has actually loaded, so a slow or missing portrait never
   flashes a broken image over the initials. */
.face-photo.is-there { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .face-photo { transition: opacity .2s ease; }
}

.face-sm { width: 2.1rem; height: 2.1rem; font-size: 2.1rem; }
.face-md { width: 2.6rem; height: 2.6rem; font-size: 2.6rem; }
.face-lg { width: 5.2rem; height: 5.2rem; font-size: 5.2rem; }

/* The top of a member's record: the face beside the name, the party under it. */
.member-head { display: flex; gap: 1rem; align-items: flex-start; }
.member-head-words { min-width: 0; flex: 1; }
.member-head h2 { margin-top: 0; }
.member-party {
  margin: .1rem 0 .4rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.mep-hit:hover { background: var(--surface-sunken); border-color: var(--line); }
/* Wherever a face joins a hit row, the row becomes a grid: face, then the name
   over the line about them. */
.mep-hit:has(.face) {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 .6rem;
}
.mep-hit:has(.face) .face { grid-row: span 2; }
.mep-hit:has(.face) .mep-name { grid-column: 2; }
.mep-hit:has(.face) .mep-meta { grid-column: 2; line-height: 1.3; }
.mep-hit:has(.face) .card-rule { grid-column: 3; grid-row: span 2; }
.mep-name { font-weight: 600; font-size: .88rem; }
.mep-meta { font-size: .78rem; color: var(--ink-faint); flex: 1; }

/* ---------------------------------------------------------------- tracker */

/* Votes are grouped by the session they were taken in, folded until asked for. */
.session-list { display: grid; gap: .5rem; }
.session-tools { margin: 0 0 .1rem; }
.session-tools button {
  font: inherit;
  font-size: .78rem;
  font-weight: 600;
  color: var(--focus);
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.session-tools button:hover { text-decoration: underline; }
.session {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}
.session > summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
  padding: .6rem .8rem;
  cursor: pointer;
  list-style: none;
  border-radius: 8px;
}
.session > summary::-webkit-details-marker { display: none; }
.session > summary::before {
  content: "▸";
  color: var(--ink-faint);
  font-size: .8rem;
  margin-right: .1rem;
  transition: transform .15s ease;
}
.session[open] > summary::before { content: "▾"; }
.session > summary:hover { background: var(--surface-sunken); }
.session-label { font-weight: 600; font-size: .92rem; flex: 1; }
.session-count { font-size: .78rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.session .decision-list { padding: 0 .5rem .6rem; }

/* ---------------------------------------------------------------- member */

.member-votes {
  list-style: none;
  margin: .6rem 0 0;
  padding: 0;
  display: grid;
  gap: .3rem;
  max-height: 32rem;
  overflow-y: auto;
}
.member-vote {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .2rem .6rem;
  align-items: baseline;
  font: inherit;
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: .35rem .45rem;
  cursor: pointer;
  color: inherit;
}
.member-vote:hover { background: var(--surface-sunken); border-color: var(--line); }
.member-vote .mv-title { font-size: .88rem; font-weight: 500; }
.member-vote .mv-date { font-size: .76rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.member-hit .mep-meta { flex: none; }

/* ---------------------------------------------------------------- roll-call */

/* The chamber in one line. Flat blocks rather than hatching, each one a filter,
   sized by how many members it holds. */
.roll { margin-top: 1.2rem; }

.roll-bar {
  display: flex;
  gap: 2px;
  height: 3.1rem;
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface-sunken);
}
.roll-bar .seg {
  position: relative;
  min-width: 3.4rem;
  border: 0;
  cursor: pointer;
  color: #fff;
  font: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .05rem;
  padding: 0 .3rem;
  transition: filter .15s ease, opacity .15s ease;
}
.roll-bar .seg:hover { filter: brightness(1.08); }
.roll-bar .seg-value { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; }
.roll-bar .seg-label {
  font-size: .62rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  opacity: .85;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 34rem) { .roll-bar .seg-label { display: none; } }
.roll-bar .seg-for { background: var(--vote-for); color: var(--on-for); }
.roll-bar .seg-against { background: var(--vote-against); color: var(--on-against); }
.roll-bar .seg-abstain { background: var(--vote-abstain); color: var(--on-abstain); }
.roll-bar .seg-absent { background: var(--vote-absent); color: var(--on-absent); flex: 0 0 auto; }
/* One segment chosen: the others step back rather than disappear. */
.roll-bar:has(.is-active) .seg:not(.is-active) { opacity: .45; }
.roll-bar .seg.is-active { box-shadow: inset 0 0 0 2px var(--ink); }

.roll-summary { margin: .6rem 0 1rem; font-size: .9rem; color: var(--ink-soft); }
.roll-summary .result { font-weight: 700; }

.roll-tabs { display: flex; gap: 1.1rem; border-bottom: 1px solid var(--line); }
.roll-tabs button {
  font: inherit;
  font-size: .9rem;
  font-weight: 600;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: .5rem 0;
  margin-bottom: -1px;
  cursor: pointer;
}
.roll-tabs button:hover { color: var(--ink); }
.roll-tabs button[aria-selected="true"] { color: var(--eu-ink); border-bottom-color: var(--eu-ink); }

.roll-filters {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin: .9rem 0 .5rem;
}
.roll-filters input, .roll-filters select {
  font: inherit;
  font-size: .85rem;
  padding: .4rem .55rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
}
.roll-filters input { flex: 1 1 12rem; min-width: 0; }
.roll-filters button {
  font: inherit;
  font-size: .8rem;
  padding: .3rem .7rem;
  border-radius: 8px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
}
.roll-count { margin: 0 0 .6rem; font-size: .8rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

.roll-members { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; max-height: 34rem; overflow-y: auto; }
/* A row is the face, then the name over the country and party, then the vote. */
/* One member: their face, their name over their country, group and party, and
   how they voted. A grid rather than a wrapping line, so the second line sits
   under the name instead of beside the face. */
.roll-member {
  width: 100%;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 .65rem;
  font: inherit;
  text-align: left;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  padding: .4rem .3rem;
  cursor: pointer;
  color: inherit;
}
.roll-member .face { grid-row: span 2; }
.roll-member:hover { background: var(--surface-sunken); }
.rm-name { grid-column: 2; font-weight: 600; font-size: .88rem; }
.rm-meta { grid-column: 2; font-size: .78rem; color: var(--ink-faint); line-height: 1.3; }
.roll-member .vote-pill { grid-column: 3; grid-row: span 2; }

.breakdown { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem; max-height: 40rem; overflow-y: auto; }
.breakdown-row {
  width: 100%;
  display: grid;
  /* Wide enough for a wordmark. Most of these marks are the group's name set
     in type, and squeezed into a square they shrink to an illegible strip.
     The third column is the mark that says the row opens something. */
  grid-template-columns: 4.6rem 1fr auto;
  align-items: start;
  gap: .75rem;
  font: inherit;
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 8px;
  padding: .55rem .4rem;
  cursor: pointer;
  color: inherit;
}
.breakdown-row:hover { background: var(--surface-sunken); }
.bd-go { align-self: center; color: var(--ink-faint); font-size: 1.2rem; line-height: 1; padding-right: .2rem; }
.breakdown-row:hover .bd-go { color: var(--ink); }

/* A plain tile rather than a party logo: it identifies the row without
   borrowing anyone's mark. */
.bd-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 3rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--surface-sunken);
  font-size: .64rem;
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: .01em;
  text-align: center;
  padding: 0 .15rem;
  overflow: hidden;
  color: var(--ink-soft);
}
.bd-tile-country { font-size: .82rem; letter-spacing: .04em; }
.bd-tile-group { border-width: 0; font-weight: 700; }
/* A logo replaces the colour rather than sitting on it — on a light tile in
   both themes, because a mark is drawn for the paper its group prints it on,
   not for whichever theme a reader happens to be using. */
.bd-tile.has-logo {
  background: #fff;
  border: 1px solid var(--line);
  padding: .2rem;
}
/* Unless the mark itself is white, in which case the group's own colour stays
   behind it — which is where that version of the mark is meant to sit. */
.bd-tile.has-logo.has-logo-on-colour { padding: .15rem; }
.bd-tile img { max-width: 100%; max-height: 100%; object-fit: contain; }

/* A group's mark next to its name in a table. The row already says which group
   it is, so the mark carries no lettering of its own — it is the group's
   colour, and its logo once one is added. It has to come after .bd-tile: same
   specificity, so the later rule is the one that sets the size. */
.group-cell { padding: 0; }
/* The group's name is a door to its members. */
.group-open {
  font: inherit;
  font-weight: inherit;
  color: inherit;
  text-align: left;
  width: 100%;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .45rem .5rem;
  border: 0;
  background: none;
  cursor: pointer;
}
.group-open:hover { background: var(--surface-sunken); }
.group-open:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.group-go { margin-left: auto; color: var(--ink-faint); font-size: 1.1rem; line-height: 1; }
.group-open:hover .group-go { color: var(--ink); }

/* Big enough for a mark to be a mark. At the size this was, a logo was a
   smudge and a white one on its group's colour was nothing at all. */
.bd-tile-swatch {
  width: 2.1rem;
  min-width: 2.1rem;
  height: 1.5rem;
  border-radius: 4px;
  padding: 0;
  flex: 0 0 auto;
}
.bd-tile-swatch.has-logo { padding: 1px; }

/* The same mark inside a badge, where the line is smaller. */
.badge-group { display: inline-flex; align-items: center; gap: .35rem; }
.badge-group .bd-tile-swatch { width: 1.5rem; min-width: 1.5rem; height: 1rem; border-radius: 3px; }

/* ------------------------------------------------------- a party's own line

   How one national party's members voted, on every vote, under the outcome.
   Open: the summary is the count and the rows are the members themselves,
   each with their face and how they voted. It folds, for a reader who wants
   the outcome without them. */
.delegation {
  margin: .9rem 0 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-sunken);
}
.delegation > summary {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .6rem .8rem;
  cursor: pointer;
  font-size: .92rem;
  list-style: none;
}
.delegation > summary::-webkit-details-marker { display: none; }
.delegation > summary::after {
  content: "▸";
  margin-left: auto;
  color: var(--ink-faint);
  font-size: .8rem;
  transition: transform .15s ease;
}
.delegation[open] > summary::after { transform: rotate(90deg); }
.delegation > summary:hover { background: color-mix(in srgb, var(--surface-sunken) 70%, var(--ink) 6%); }
.delegation > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.dg-mark { width: .85rem; height: .85rem; border-radius: 3px; flex: 0 0 auto; }
.dg-label { font-weight: 700; }
.dg-sum { color: var(--ink-soft); }
.dg-members {
  list-style: none;
  margin: 0;
  padding: 0 .8rem .3rem;
  display: grid;
  gap: .3rem;
}
.dg-members li { border-top: 1px solid var(--line); }
.dg-member {
  font: inherit;
  font-size: .9rem;
  width: 100%;
  display: flex;
  align-items: center;
  gap: .5rem;
  justify-content: space-between;
  padding: .35rem .25rem;
  border: 0;
  border-radius: 6px;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.dg-member:hover { background: var(--surface-sunken); }
.dg-member:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.dg-name { min-width: 0; margin-right: auto; }
.dg-note { margin: 0; padding: 0 .8rem .7rem; font-size: .8rem; color: var(--ink-faint); }

/* ------------------------------------------------------------------ sharing

   One vote, one card, at the foot of the brief. It was a row of bare word
   pills, which read as a row of tabs and told a reader nothing about where
   their link was going. Now the address is shown before it is sent, the
   picture and the phone's own sheet lead, and each place carries its own mark
   beside its name. */
.share-card {
  margin-top: 1.4rem;
  padding: .95rem 1.05rem 1.05rem;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.share-card-title {
  margin: 0 0 .7rem;
  font-family: var(--font);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* The address, spelled out. Seeing the link before sending it is what makes
   the card trustworthy; it is short enough now to be read at a glance. */
/* Every way of sharing, in one line, in the order people reach for them.

   It scrolls when it does not fit. On a phone the places that take a link sit
   past the right edge and are pushed into view, which is how every app does
   this — rather than wrapping into a block three rows deep that shoves the
   rest of the vote off the screen. The fade at the right edge is the only
   thing that says there is more; it goes when you reach the end. */
.share-scroller { position: relative; }
.share-scroller::after {
  content: "";
  position: absolute;
  inset-block: 0;
  right: -1px;
  width: 2.2rem;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--surface-sunken) 70%);
  opacity: 0;
  transition: opacity .15s ease;
}
.share-scroller.has-more::after { opacity: 1; }

.share-choices {
  display: flex;
  gap: .45rem;
  margin: 0;
  padding: 0 0 .15rem;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  /* No snap points. They re-aligned the row after the browser had scrolled a
     focused choice into view, which left two of them half off the edge for
     anyone tabbing through — a keyboard user aimed at a control they could
     only half see. The padding is what replaces them: it keeps a focused
     choice clear of both edges instead of flush against one. */
  scroll-padding-inline: 1rem;
  scrollbar-width: thin;
}
.share-choices::-webkit-scrollbar { height: 5px; }
.share-choices::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 999px;
}
.share-choice { flex: 0 0 auto; }

.share-address-field {
  display: block;
  width: 100%;
  margin-top: .5rem;
  min-width: 0;
  font: inherit;
  font-size: .78rem;
  padding: .42rem .6rem;
  color: var(--ink-faint);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  text-overflow: ellipsis;
}

/* The picture, its buttons and the line that explains them, as one thing.
   A share block that does not show you the thing it is about to send is
   asking for trust it has not earned; this one shows it, because by the time
   anyone is reading, it is already drawn. */
.share-make {
  display: flex;
  gap: .85rem;
  align-items: flex-start;
  margin-top: .7rem;
}
.share-make-side {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

/* The one button. A web page cannot hand a picture straight to Instagram
   Stories — that needs a native app with a Facebook App ID — so the share
   sheet is as direct as this gets, and it is the only thing offered until
   somebody asks for more. It is sized to say so. */
.share-act.is-lead {
  width: 100%;
  justify-content: center;
  font-size: .95rem;
  padding: .8rem 1rem;
  border-radius: 11px;
}
.share-act.is-lead svg { width: 1.15rem; height: 1.15rem; }

/* Everything else, folded. */
/* The other ways to send it, in the open. These used to be behind a fold
   labelled "Other ways to share", which charged a click for something that
   was never advanced: six places that each take a link, and the clipboard.
   The label stays, as a quiet heading rather than a button, so the row still
   reads as the alternative to the picture above it. */
/* The way into Instagram, shown only when the share sheet did not come. It is
   a link rather than a button because that is what it is: it leaves the site.
   Quieter than the story button above it — it is the second thing to press,
   never the first. */
.share-open-ig {
  margin-top: .5rem;
  width: 100%;
  justify-content: center;
}

.share-rest {
  margin-top: .9rem;
  padding-top: .8rem;
  border-top: 1px solid var(--line);
}
.share-rest-label {
  margin: 0 0 .45rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.story-preview {
  flex: 0 0 auto;
  width: 5.4rem;
  margin: 0;
  text-align: center;
}
.story-preview > img,
.story-preview::before {
  display: block;
  width: 5.4rem;
  /* The card's own shape, so what is on screen is what lands. */
  aspect-ratio: 9 / 16;
  border-radius: 8px;
  border: 1px solid var(--line);
  /* The picture's own ground, so the frame before it arrives is the same
     colour as the picture that fills it. */
  background: #e7eaf1;
  object-fit: cover;
}
/* Until it is drawn, the frame carries the vote's own colour along its top
   edge rather than being a hole that appears to be broken. */
.story-preview:not(.is-ready)::before {
  content: "";
  background:
    linear-gradient(180deg, var(--vote-absent) 0 3px, transparent 3px),
    #e7eaf1;
}
.story-preview[data-result="adopted"]:not(.is-ready)::before {
  background: linear-gradient(180deg, var(--ink-for) 0 3px, transparent 3px), #e7eaf1;
}
.story-preview[data-result="rejected"]:not(.is-ready)::before {
  background: linear-gradient(180deg, var(--ink-against) 0 3px, transparent 3px), #e7eaf1;
}
.story-preview figcaption {
  /* Wider than the picture it sits under, so the two words stay on one line
     without making the picture bigger than it needs to be. */
  width: 7rem;
  margin: .3rem -.8rem 0;
  font-size: .66rem;
  line-height: 1.3;
  color: var(--ink-faint);
}
.story-preview.is-ready > img { box-shadow: 0 2px 8px rgba(16, 24, 40, .16); }
@media (prefers-reduced-motion: no-preference) {
  .story-preview > img { animation: preview-in .28s cubic-bezier(.2,.7,.3,1); }
  @keyframes preview-in { from { opacity: 0; transform: translateY(3px); } }
}

.share-make .share-note { margin-top: 0; }

.share-act {
  display: inline-flex;
  align-items: center;
  gap: .42rem;
  flex: none;
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  line-height: 1;
  padding: .5rem .8rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.share-act svg { width: 1rem; height: 1rem; flex: none; color: var(--ink-faint); }
.share-act:hover { border-color: var(--ink-faint); }
.share-act:hover svg { color: var(--ink); }
.share-act:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.share-act[disabled] { opacity: .6; cursor: default; }

/* The picture leads: it is the one thing here that cannot be got any other
   way, and it is what a story is made of. */
.share-act.is-primary {
  color: #fff;
  background: var(--eu-blue);
  border-color: var(--eu-blue);
}
.share-act.is-primary svg { color: rgba(255, 255, 255, .8); }
.share-act.is-primary:hover { background: #0a3078; border-color: #0a3078; }
.share-act.is-primary:hover svg { color: #fff; }

.share-place { font-weight: 500; color: var(--ink-soft); }
.share-place:hover { color: var(--ink); }

/* What the picture does, and what to do with it once it is made. It says the
   same thing before and after, so nobody has to remember the step they are
   on; it only goes brighter while it is fresh. */
.share-note {
  margin: .7rem 0 0;
  font-size: .78rem;
  line-height: 1.45;
  color: var(--ink-faint);
}
.share-note.is-live { color: var(--ink); font-weight: 500; }


/* --------------------------------------------------------------- motion

   Things that appear should arrive rather than snap into place — enough to
   show the reader where to look, little enough that a fast reader never waits
   for it. Anyone who has asked their system for less motion gets none. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }

  #decision-section:not([hidden]),
  #member-section:not([hidden]),
  .panel-body:not([hidden]) {
    animation: arrive .22s cubic-bezier(.2, .7, .3, 1) both;
  }

  .session > summary { transition: background-color .15s ease; }
  .decision-card, .roll-member, .breakdown-row { transition: background-color .12s ease, border-color .12s ease; }
}

@keyframes arrive {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------ loading

   Static JSON on a CDN usually lands in a blink, but on a slow phone the page
   would otherwise sit blank with no sign that anything was happening. */
.loading-thread {
  position: fixed;
  inset-inline: 0;
  top: 0;
  height: 2px;
  z-index: 40;
  background: linear-gradient(90deg, transparent, var(--eu-blue), var(--eu-gold), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity .25s ease;
  pointer-events: none;
}
.is-loading .loading-thread { opacity: 1; animation: thread 1.1s ease-in-out infinite; }

@keyframes thread {
  from { background-position: -40% 0; }
  to { background-position: 140% 0; }
}

.load-error {
  grid-column: 1 / -1;
  margin: 0 0 1rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--vote-against);
  border-radius: 12px;
  background: color-mix(in srgb, var(--vote-against) 8%, var(--surface));
  max-width: 62ch;
}
.load-error p { margin: 0 0 .6rem; }
.load-error p:last-child { margin-bottom: 0; }
.load-error-detail { font-size: .82rem; color: var(--ink-soft); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

@media (prefers-reduced-motion: reduce) {
  .is-loading .loading-thread { animation: none; }
}



/* A link that has to look like the way out — used on the not-found page. */
.button-link {
  display: inline-block;
  margin: .3rem .4rem .3rem 0;
  padding: .55rem 1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}
.button-link:hover { border-color: var(--ink-faint); }
.button-link:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.cite {
  margin: 0 0 .9rem;
  padding: .8rem 1rem;
  border-left: 3px solid var(--eu-blue);
  background: var(--surface-sunken);
  border-radius: 0 8px 8px 0;
  font-size: .92rem;
  max-width: 66ch;
}
.cite-url { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; }

/* --------------------------------------------------------------- printing

   A vote printed or saved as PDF is a document, not a screenshot of an app:
   the record, the numbers and the roll-call, without the controls that only
   work when they can be clicked. */
@media print {
  :root { --surface: #fff; --surface-sunken: #fff; --ink: #000; --line: #999; }
  body { background: #fff; color: #000; }
  .site-header .header-meta,
  .join, .volt-strip,
  .map-section, .layer-tabs, .filters, .roll-filters, .share-card, .skip-link,
  .decision-list, #session-list, .search-form, .back-to-votes, .roll-reset,
  #sample-banner { display: none !important; }
  main { display: block; max-width: none; padding: 0; }
  .rail { display: block; }
  .card, .panel, .delegation { border: 1px solid #999; break-inside: avoid; }
  .delegation > summary::after { content: ""; }
  .delegation[open] .dg-members, .dg-members { display: block; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .75em; word-break: break-all; }
  .roll-members li, .breakdown li { break-inside: avoid; }
}

.bd-main { display: grid; gap: .28rem; min-width: 0; }
.bd-label { font-weight: 650; font-size: .92rem; line-height: 1.25; }
.bd-sub { font-size: .78rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.bd-bar {
  display: flex;
  gap: 1px;
  height: .6rem;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-sunken);
}
.bd-seg { transition: flex .2s ease; }
.bd-for { background: var(--vote-for); }
.bd-against { background: var(--vote-against); }
.bd-abstain { background: var(--vote-abstain); }
.bd-absent { background: var(--vote-absent); }

.breakdown li.is-hovered .breakdown-row,
.roll-members li.is-hovered .roll-member { background: var(--surface-sunken); }
.breakdown li.is-dimmed, .roll-members li.is-dimmed { opacity: .35; }

.vote-links { margin: 0 0 1rem; }

/* ------------------------------------------------- where to check the vote

   The claim this whole site rests on is that every figure came from the
   Parliament. So the addresses that prove it are given a block of their own,
   with the document each one opens said in plain words — not a row of bare
   links, and never again a link to a homepage that holds nothing about this
   vote in particular. */
.sources {
  margin: 1.1rem 0 0;
  padding: .9rem 1rem 1rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--eu-blue);
  border-radius: var(--radius);
  background: var(--surface);
}
.sources-title {
  margin: 0 0 .6rem;
  font-family: var(--font);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.source-list {
  display: grid;
  gap: .15rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.source-link {
  display: block;
  padding: .4rem .5rem;
  margin-inline: -.5rem;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
}
.source-link:hover { background: var(--surface-sunken); }
.source-link:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.source-label {
  display: block;
  font-size: .88rem;
  font-weight: 600;
  color: var(--focus);
}
/* The document the vote is actually recorded in leads, and looks like it. */
.source-link.is-record .source-label { font-size: .95rem; }
.source-out { margin-left: .3rem; font-size: .78em; opacity: .75; }
.source-note {
  display: block;
  margin-top: .05rem;
  font-size: .78rem;
  line-height: 1.4;
  color: var(--ink-faint);
}
.source-link:hover .source-label { text-decoration: underline; text-underline-offset: .18em; }
.vote-source {
  font-size: .82rem;
  font-weight: 500;
  color: var(--focus);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--focus) 35%, transparent);
}
.vote-source:hover { border-bottom-color: var(--focus); }

a.permalink { font-size: .8rem; }

button.permalink {
  font: inherit;
  font-size: .8rem;
  color: var(--focus);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
}
.copy-fallback { font-size: .75rem; width: 100%; margin-top: .3rem; }



.tracker {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(1rem, 2.5vw, 1.4rem);
}

.tracker-head {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem 1rem;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: .9rem;
}
.tracker-head h2 {
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-faint);
  margin: 0;
}

.tracker-filters { display: flex; flex-wrap: wrap; gap: .3rem; }
.tracker-filters button {
  font: inherit;
  font-size: .78rem;
  font-weight: 500;
  padding: .22rem .6rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
}
.tracker-filters button:hover { border-color: var(--line-strong); color: var(--ink); }
.tracker-filters button[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--surface);
}

/* The whole list, unfolded. It is the way into the site, so it is not hidden
   behind a scrollbar of its own — the page scrolls. */
.decision-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .4rem;
}
.feed-empty { font-size: .85rem; color: var(--ink-faint); padding: .5rem 0; }

.decision-card {
  width: 100%;
  display: grid;
  gap: .3rem;
  text-align: left;
  font: inherit;
  cursor: pointer;
  padding: .6rem .7rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: 8px;
  background: transparent;
  color: inherit;
}
.decision-card:hover { background: var(--surface-sunken); border-color: var(--line-strong); }
.decision-card.is-current {
  background: color-mix(in srgb, var(--eu-blue) 8%, transparent);
  border-color: color-mix(in srgb, var(--eu-blue) 35%, transparent);
  border-left-color: var(--eu-blue);
}

.card-top { display: flex; align-items: center; gap: .5rem; font-size: .74rem; color: var(--ink-faint); }
.card-top time { font-variant-numeric: tabular-nums; }
.card-title { font-weight: 600; font-size: .95rem; line-height: 1.3; }
.decision-card.is-current .card-title { color: var(--eu-blue); }
.card-foot { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem; font-size: .74rem; color: var(--ink-faint); }
.card-rule { color: var(--ink-faint); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .decision-card.is-current .card-title { color: var(--ink); }
}
:root[data-theme="dark"] .decision-card.is-current .card-title { color: var(--ink); }

/* The institution is the first thing to know about a decision, so it is the
   one place in the interface where colour is used for identity, not state. */
.badge {
  display: inline-block;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: .15rem .45rem;
  border-radius: 4px;
  border: 1px solid currentColor;
  color: var(--ink-soft);
}
.badge-parliament { color: var(--body-parliament); }
.badge-council { color: var(--body-council); }
.badge-commission { color: var(--body-commission); }

.result { font-weight: 600; }
.result-adopted { color: var(--ink-for); }
.result-blocked, .result-rejected { color: var(--ink-against); }

.panel-close {
  float: right;
  font: inherit;
  font-size: .75rem;
  color: var(--ink-faint);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .15rem .6rem;
  cursor: pointer;
}
.panel-close:hover { color: var(--ink); border-color: var(--line-strong); }

/* ---------------------------------------------------------------- prose */

.prose {
  display: block;
  max-width: 46rem;
  margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 3rem) clamp(1rem, 4vw, 2.5rem) 4rem;
}
.prose h1 {
  font-family: var(--serif);
  font-size: clamp(1.8rem, 3.4vw, 2.4rem);
  line-height: 1.15;
  margin: 0 0 1rem;
  letter-spacing: -.015em;
}
.prose h2 {
  font-family: var(--serif);
  font-size: 1.3rem;
  margin: 2.2rem 0 .6rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}
.prose-lead { font-size: 1.08rem; color: var(--ink-soft); margin: 0 0 1.4rem; }
.prose p { max-width: 66ch; margin: 0 0 .9rem; }
.prose ul { max-width: 66ch; padding-left: 1.1rem; margin: 0 0 .9rem; }
.prose li { margin-bottom: .5rem; }
.prose-table { width: 100%; margin: 1rem 0; font-size: .9rem; }
.prose-table th, .prose-table td { padding: .45rem .5rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.prose-table thead th { font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); }

/* ---------------------------------------------------------------- footer */

/* The footer says what this is and where it came from, and it uses the width
   of the screen to do it: on a laptop the three parts stand side by side
   across the page rather than in a column in the corner. It lines up with the
   same 1560px measure as the page above it, so the whole thing reads as one
   sheet. Below 62rem it stacks. */
/* ------------------------------------------------------------ the top strip

   Whose site this is, said once, before anything else: the party's mark and
   one line. It is a strip rather than a band — thin enough that the tracker's
   own header is still the first thing with weight on the page, and it does not
   ask for anything. The asking waits for the foot of the page. */
.volt-strip {
  background: #502379;
  color: #fff;
  padding: .5rem clamp(1rem, 4vw, 2.5rem);
}
.volt-strip-inner {
  max-width: 1560px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: clamp(.75rem, 2.5vw, 1.25rem);
}
.volt-strip p {
  margin: 0;
  font-size: .92rem;
  font-weight: 600;
  letter-spacing: -.01em;
  color: #fff;
}
.volt-strip .join-mark { height: 1.15rem; }

@supports ((-webkit-mask-image: url("../brand/volt.svg")) or (mask-image: url("../brand/volt.svg"))) {
  .volt-strip .join-mark { width: 2.8rem; height: 1.15rem; margin-right: 0; }
}

@media (max-width: 30rem) {
  .volt-strip p { font-size: .84rem; }
}

/* ------------------------------------------------------------------- join

   A band between the records and the footer: one line, and one door. At the
   end of a record an argument is worse than an invitation, so there is no
   argument — the reader has just spent their time on how Europe was decided,
   and this says only that deciding it is open to them.

   Flat Volt purple, no gradient and no glow. It reads as a party asking
   rather than as the tracker speaking, and it should look like a statement
   rather than an advertisement. */
.join {
  background: #502379;
  color: #fff;
  padding: clamp(1.9rem, 4.5vw, 2.9rem) clamp(1rem, 4vw, 2.5rem);
}
.join-inner {
  max-width: 1560px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem clamp(1.5rem, 4vw, 3rem);
}
/* The party's own mark, once, so the line beside it has an author.

   Volt publishes it as a single shape with no colour of its own — its site
   fills it from the surrounding text — so the file is kept exactly as
   published and this paints it, white on the purple, which is how the party
   uses it on a dark ground. A browser without masks gets the name set in
   type, which is what stood here before. */
.join-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .62);
  padding-right: clamp(1.25rem, 3.5vw, 2.5rem);
  border-right: 1px solid rgba(255, 255, 255, .25);
}

@supports ((-webkit-mask-image: url("../brand/volt.svg")) or (mask-image: url("../brand/volt.svg"))) {
  /* A mask clips everything the element draws, its border included, so the
     rule that separates the lettered fallback from the line is gone here — and
     the mark does not need one. */
  .join-mark {
    width: 4.6rem;
    height: 1.9rem;
    padding-right: 0;
    border-right: 0;
    margin-right: clamp(1.25rem, 3.5vw, 2.5rem);
    background: #fff;
    -webkit-mask: url("../brand/volt.svg") no-repeat left center;
    mask: url("../brand/volt.svg") no-repeat left center;
    -webkit-mask-size: contain;
    mask-size: contain;
  }
  .join-mark-text { display: none; }
}
.join h2 {
  margin: 0;
  flex: 1 1 18rem;
  min-width: 0;
  font-size: clamp(1.25rem, 2.4vw, 1.7rem);
  font-weight: 650;
  line-height: 1.2;
  letter-spacing: -.02em;
  color: #fff;
}
/* Outlined, not filled: the quieter of the two ways to make a button obvious,
   and the one that does not shout across the record above it. It fills on
   hover, which is where the invitation becomes an offer. */
.join-button {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  font: inherit;
  font-size: .95rem;
  font-weight: 600;
  letter-spacing: .01em;
  padding: .72rem 1.35rem;
  border: 1.5px solid rgba(255, 255, 255, .55);
  border-radius: 999px;
  background: transparent;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease;
}
.join-button:hover { background: #fff; color: #3d1a5c; border-color: #fff; }
.join-button:focus-visible { outline: 2px solid #f6c700; outline-offset: 3px; }
.join-arrow { transition: transform .18s ease; }
.join-button:hover .join-arrow { transform: translateX(2px); }

/* Where the party can be found, under the invitation and quieter than it: a
   reader not ready to join can still follow. Named rather than drawn as icons,
   because eight unlabelled glyphs are a puzzle and eight words are not. */
.join-social {
  max-width: 1560px;
  margin: 1.15rem auto 0;
  padding-top: .95rem;
  border-top: 1px solid rgba(255, 255, 255, .18);
  display: flex;
  flex-wrap: wrap;
  gap: .3rem 1.35rem;
}
.join-social a {
  font-size: .84rem;
  font-weight: 600;
  color: rgba(255, 255, 255, .78);
  text-decoration: none;
  padding: .15rem 0;
  border-bottom: 1px solid transparent;
}
.join-social a:hover { color: #fff; border-bottom-color: rgba(255, 255, 255, .6); }
.join-social a:focus-visible { outline: 2px solid #f6c700; outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .join-button, .join-arrow { transition: none; }
  .join-button:hover .join-arrow { transform: none; }
}

/* On one column the rule under the name would cut the line in half. */
@media (max-width: 46rem) {
  .join-inner { align-items: flex-start; gap: .55rem; }
  .join-mark { padding-right: 0; margin-right: 0; border-right: 0; }
  .join h2 { flex-basis: 100%; }
  .join-button { margin-top: .5rem; }
}

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--surface-sunken, var(--surface));
  padding: 2rem clamp(1rem, 4vw, 2.5rem) 2.5rem;
  color: var(--ink-faint);
  font-size: .84rem;
}
.footer-inner {
  max-width: 1560px;
  margin: 0 auto;
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.6fr) minmax(0, .7fr);
  align-items: start;
}
.footer-lead p { margin: 0; max-width: 42ch; }
.footer-name {
  font-weight: 700;
  color: var(--ink);
  font-size: .95rem;
  letter-spacing: .01em;
  margin-bottom: .35rem !important;
}

/* Four plain facts, the ones a reader checks before trusting a number. */
.footer-facts {
  margin: 0;
  display: grid;
  gap: .85rem 2rem;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.footer-facts dt {
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: .2rem;
}
.footer-facts dd { margin: 0; color: var(--ink-faint); }
.footer-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .95em; }

.footer-links { display: grid; gap: .45rem; justify-items: start; }
.footer-links a { font-weight: 600; }

.footer-note {
  max-width: 1560px;
  margin: 1.75rem auto 0;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  font-size: .78rem;
}

@media (max-width: 62rem) {
  .footer-inner { grid-template-columns: minmax(0, 1fr); }
}

.load-error {
  grid-column: 1 / -1;
  background: color-mix(in srgb, var(--vote-against) 12%, var(--surface));
  border: 1px solid var(--vote-against);
  border-radius: var(--radius);
  padding: 1rem;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------ sharing the site

   A button in the header with a caret, and a short menu under it. The caret
   turns over when the menu is open, which is the whole of the animation. */
.site-share { position: relative; display: inline-flex; }

.site-share-button {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .3rem .6rem .3rem .7rem;
  font: inherit;
  font-size: .8rem;
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.site-share-button:hover { color: var(--ink); border-color: var(--ink-faint); }
.site-share-button .caret { width: .8rem; height: .8rem; }
.site-share-button[aria-expanded="true"] { color: var(--ink); border-color: var(--ink-faint); }
.site-share-button[aria-expanded="true"] .caret { transform: rotate(180deg); }

@media (prefers-reduced-motion: no-preference) {
  .site-share-button .caret { transition: transform .18s cubic-bezier(.2,.7,.3,1); }
  .site-share-menu { animation: arrive .16s cubic-bezier(.2,.7,.3,1) both; }
}

.site-share-menu {
  position: absolute;
  top: calc(100% + .4rem);
  right: 0;
  z-index: 30;
  min-width: 12rem;
  padding: .3rem;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .16), 0 2px 6px rgba(0, 0, 0, .08);
}
.site-share-menu hr { margin: .3rem .4rem; border: 0; border-top: 1px solid var(--line); }

.share-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  padding: .5rem .6rem;
  font: inherit;
  font-size: .85rem;
  font-weight: 500;
  color: var(--ink);
  text-align: left;
  text-decoration: none;
  background: none;
  border: 0;
  border-radius: 8px;
}
.share-row:hover, .share-row:focus-visible { background: var(--surface-sunken); }
.share-row svg { width: 1.05rem; height: 1.05rem; flex: none; color: var(--ink-soft); }
.share-row:hover svg { color: var(--ink); }

/* On a phone the Share button sits at the right of the header, so the menu
   hangs from its right edge and grows leftwards. Pinning it to the left, as
   it once was, pushed it off the screen and gave the page a sideways scroll. */
@media (max-width: 40rem) {
  .site-share-menu {
    right: 0;
    left: auto;
    max-width: calc(100vw - 1.5rem);
  }
}

/* ---------------------------------------------------- how a control behaves

   One set of manners for everything you can press, so the map, the filters,
   the share row and the theme button all answer the same way:

     - the pointer says it is pressable, and a tap does not wait 300ms to see
       whether it was a double-tap, nor flash a grey box on Android;
     - hovering lifts the surface a little;
     - pressing moves it down a hair — the feedback that makes a control feel
       immediate is the one that happens on the way down, not after the click;
     - focus draws the same ring everywhere, and only for keyboards.

   Anything that sets its own background or border still does; this is the
   behaviour underneath. */
button,
summary,
[role="button"],
.share-act,
.button-link,
.vote-source,
.permalink {
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

button:focus-visible,
summary:focus-visible,
a:focus-visible,
[role="button"]:focus-visible,
select:focus-visible,
input:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 6px;
}

/* The press. Small, fast, and never on the map: a country moving under the
   finger would read as the map sliding. */
@media (prefers-reduced-motion: no-preference) {
  button:not(.country):not(.seg),
  .share-act,
  .button-link,
  .decision-card,
  .roll-member,
  .breakdown-row,
  .session > summary {
    transition: background-color .12s ease, border-color .12s ease,
                color .12s ease, transform .08s ease, box-shadow .12s ease;
  }

  .share-act:active,
  .button-link:active,
  .tracker-filters button:active,
  .layer-tabs button:active,
  #unfold-all:active,
  .legend li button:active,
  .roll-filters button:active,
  .panel-close:active,
  .back-to-votes:active,
  .show-in-roll:active { transform: translateY(1px); }

  .decision-card:active,
  .roll-member:active,
  .breakdown-row:active { transform: scale(.995); }
}

/* A pressed segment of the vote bar answers by brightening, not by moving:
   it is part of a measured bar and must not change size. */
.roll-bar .seg { transition: filter .12s ease, opacity .12s ease; }
.roll-bar .seg:hover { filter: brightness(1.08); }
.roll-bar .seg:active { filter: brightness(.94); }

/* ------------------------------------------------------------- touch sizes

   A finger is not a cursor. Where the pointer is coarse, every control gets
   enough height to be hit without aiming: the legend chips, the layer tabs,
   the share row, the header link and the roll-call filters were all built at
   mouse sizes. Nothing moves on a laptop. */
@media (pointer: coarse) {
  /* Boxed controls grow to a comfortable height. */
  .legend li button,
  .layer-tabs button,
  .share-act,
  .tracker-filters button,
  .bloc,
  #unfold-all {
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
  }
  .legend li button { padding-block: .5rem; }

  /* Text links get their room from padding instead. Stretching an underlined
     link to a fixed height leaves the underline floating below the words. */
  .header-about,
  .permalink,
  .back-to-votes,
  .vote-source,
  .show-in-roll {
    display: inline-block;
    width: fit-content;
    padding-block: .6rem;
  }
  /* The line under these is a border, which would sit at the bottom of the
     padding rather than under the words. A real underline stays with the text. */
  .vote-source {
    border-bottom: 0;
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--focus) 45%, transparent);
    text-decoration-thickness: 1px;
    text-underline-offset: .3em;
  }
  .roll-filters input,
  .roll-filters select,
  .roll-filters button,
  .search input { min-height: 2.9rem; }
  .roll-member, .decision-card, .breakdown-row { min-height: 3rem; }
  .panel-close { min-height: 2.6rem; padding-inline: .9rem; }
}


/* ---------------------------------------------------------------- phone

   Below 40rem the page is a single column and every element has to earn its
   height. Type comes down a step, the cards give back some padding, and a
   session line fits on one line, so a term of sittings is a scroll rather
   than a journey. Nothing here shrinks a tap target: the block above sets
   those, and this one only touches type, spacing and wrapping. */
@media (max-width: 40rem) {
  /* The brand takes a share of the row rather than all of it, so the Share
     button and the theme toggle stay beside it instead of wrapping below. */
  .brand { flex: 1 1 55%; }
  .brand-tagline {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  main { padding: .8rem .75rem 2.5rem; gap: .8rem; }
  .rail { gap: .8rem; }
  .tracker, .panel, .decision,
  .decision-brief, #decision-section > .roll { padding: .9rem .85rem; }

  .intro { margin-bottom: .75rem; }
  .intro-lead { font-size: 1rem; max-width: none; }
  .panel-empty h2 { font-size: 1.3rem; }
  .panel-body h2, .panel-title { font-size: 1.25rem; }

  /* One sitting, one line. */
  .session > summary { padding: .55rem .6rem; gap: .45rem; align-items: center; }
  .session-label { font-size: .83rem; line-height: 1.3; }
  .session-count { font-size: .72rem; white-space: nowrap; }
  .session-list { gap: .4rem; }

  .site-footer { padding: 1.5rem 1rem 2rem; }
  .footer-facts { gap: .75rem 1rem; }
}

/* The narrowest phones still in use — 320 CSS pixels. The brand gives up more
   of the row so the two controls stay beside it, and the tagline holds to a
   single line. */
@media (max-width: 24rem) {
  .brand { flex: 1 1 45%; gap: .45rem; }
  .brand-tagline { -webkit-line-clamp: 1; font-size: .7rem; }
  .site-share-button { padding-inline: .6rem; }
  main { padding: .7rem .6rem 2rem; }
}


/* Modal dialog */
.modal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
}

.modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}

.modal-content {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
  padding: 1.5rem;
  max-width: 500px;
  max-height: 80vh;
  overflow-y: auto;
  width: 90%;
}

.modal-content h2 {
  margin: 0 0 1rem;
  font-size: 1.25rem;
}

.modal-close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  background: none;
  border: none;
  padding: 0.25rem;
  cursor: pointer;
  color: var(--ink-soft);
  border-radius: 50%;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.modal-close:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}

.modal-close:focus {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.modal-close svg {
  display: block;
}

/* Shortcuts list */
.shortcuts-list {
  margin: 0;
}

.shortcuts-list dt {
  display: inline-block;
  min-width: 140px;
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 0.5rem 0;
}

.shortcuts-list dd {
  display: inline-block;
  margin: 0 0 1rem 1rem;
  color: var(--ink-soft);
}

.shortcuts-list kbd {
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.125rem 0.375rem;
  font-family: monospace;
  font-size: 0.875rem;
  margin-right: 0.25rem;
}


.search {
  position: relative;
  display: flex;
  align-items: center;
}

.search input[type="search"] {
  flex: 1;
  padding: 0.75rem 1rem 0.75rem 2.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 1rem;
  background: var(--surface);
  color: var(--ink);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.search input[type="search"]:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus) 20%, transparent);
}

.search input[type="search"]::placeholder {
  color: var(--ink-faint);
}

#search-clear {
  position: absolute;
  right: 0.5rem;
  background: none;
  border: none;
  padding: 0.25rem;
  cursor: pointer;
  color: var(--ink-soft);
  border-radius: 50%;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.2s ease, color 0.2s ease;
}

#search-clear:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}

#search-clear:focus {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.search-icon {
  position: absolute;
  left: 0.75rem;
  color: var(--ink-faint);
  pointer-events: none;
}

/* Accessibility improvements */

/* Ensure sufficient color contrast in all themes */
:root[data-theme="light"] {
  --text-on-blue: #ffffff;
  --text-on-surface: var(--ink);
}

:root[data-theme="dark"] {
  --text-on-blue: #ffffff;
  --text-on-surface: var(--ink);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --text-on-blue: #ffffff;
    --text-on-surface: var(--ink);
  }
}

/* Focus indicators */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

button:focus-visible,
[role="button"]:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Skip link - already styled but ensure it's visible on focus */
.skip-link:focus {
  top: 0;
  color: white;
}

/* Ensure interactive elements are clearly identifiable */
[role="button"],
button,
a[href],
input[type="search"],
select,
textarea {
  cursor: pointer;
}

/* Touch targets for mobile */
@media (hover: none) {
  [role="button"],
  button,
  a[href],
  input[type="search"],
  select,
  textarea,
  .country-hit {
    min-width: 44px;
    min-height: 44px;
  }
}

/* High contrast mode support */
@media (forced-colors: active) {
  .map path {
    forced-color-adjust: none;
  }
  
  button,
  [role="button"] {
    border: 2px solid ButtonText;
  }
  
  input[type="search"] {
    border: 2px solid GrayText;
  }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  
  .loading-thread {
    animation: none;
    background: var(--eu-blue);
  }
}

/* Screen reader only utilities */
.visually-hidden,
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Ensure form controls have associated labels */
.search input[type="search"] {
  /* Already has visually-hidden label */
}

/* Table accessibility */
.table-section table {
  border-collapse: collapse;
  width: 100%;
}

.table-section th,
.table-section td {
  padding: 0.5rem;
  text-align: left;
  border: 1px solid var(--line);
}

.table-section th {
  background: var(--surface-sunken);
  font-weight: 600;
}

.table-section caption {
  caption-side: top;
  font-weight: 600;
  padding: 0.5rem 0;
}

/* Ensure links are distinguishable */
a {
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 2px;
}

a:hover,
a:focus {
  text-decoration-thickness: 2px;
}

/* Remove underline from buttons that look like links */
button,
[role="button"] {
  text-decoration: none;
}










