/* Bilateral Navigator — portal stylesheet, shared by every service.
   Palette and metrics are the demo files' in tmp/, kept verbatim so the two
   can be compared side by side. The demo hardcodes Croatia and Sweden as
   `hr`/`se`; here the two sides are `c1`/`c2`, because the Library renders
   any pair. */

:root{
  --navy:#0E1F33; --navy-deep:#091625; --navy-panel:#132A44; --navy-card:#10233B;
  --sky:#4FB8E7; --sky-soft:#9AD4F0;
  --c1-ink:#7FA3DD; --c2-ink:#29ADE4;
  --sim-c1:#2F5D9E; --sim-c2:#00A0E2;
  --text:#E7F0F7; --text-soft:#A9BDCF;
  --line:rgba(79,184,231,.16); --stripe:rgba(255,255,255,.045);
  --radius:14px; --shadow:0 12px 32px rgba(4,12,22,.5);
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
html,body{overflow-x:hidden;max-width:100vw}
body{font-family:'Albert Sans',system-ui,sans-serif;background:var(--navy);
  color:var(--text);min-height:100vh}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
a{color:var(--sky);text-decoration:none}
img{max-width:100%}

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

/* Barely-there scrollbars, everywhere. */
*{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.16) transparent}
*::-webkit-scrollbar{width:5px;height:5px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:rgba(255,255,255,.16);border-radius:99px}
*::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.3)}

/* ===== topbar ===== */
.topbar{position:sticky;top:0;z-index:60;display:flex;align-items:center;
  gap:14px;padding:12px 22px;background:rgba(9,22,37,.93);
  backdrop-filter:blur(9px);border-bottom:1px solid var(--line)}
.brand{display:flex;align-items:baseline;gap:7px;white-space:nowrap;font-size:15px}
.brand b{font-weight:800}
.brand span{color:var(--sky);font-weight:600}
.topnav{font-size:12.5px;color:var(--text-soft);white-space:nowrap;min-width:0;
  overflow:hidden;text-overflow:ellipsis}
.topnav a{color:var(--text-soft)}
.topnav a:hover{color:var(--text)}
.topnav .sep{opacity:.5;margin:0 8px}

/* ===== flag disc ===== */
.cflag{display:inline-flex;width:18px;height:18px;border-radius:50%;
  overflow:hidden;flex:0 0 auto;background:#28466B;
  box-shadow:0 0 0 1px rgba(255,255,255,.22)}
.cflag img{width:100%;height:100%;object-fit:cover}
.cflag.big{width:24px;height:24px}
.cflag.sm{width:15px;height:15px}

/* ===== page frame ===== */
.wrap,.subwrap{max-width:1220px;margin:0 auto;padding:28px 20px 80px}
.pv-foot{max-width:1220px;margin:0 auto;padding:16px 20px 40px;font-size:12px;
  color:var(--text-soft);border-top:1px solid var(--line);line-height:1.6}
@media (max-width:640px){.wrap,.subwrap{padding:20px 12px 60px}}

/* ===== index: heading ===== */
.flags{display:flex;gap:8px;margin-bottom:14px}
.flags .cflag{width:34px;height:34px}
h1{font-size:clamp(26px,4vw,44px);font-weight:800;line-height:1.1;margin-bottom:10px}
h1 span{color:var(--sky)}
.lede{font-family:'Source Serif 4',serif;font-size:16.5px;line-height:1.65;
  color:var(--text-soft);max-width:730px}
.hint{margin-top:10px;font-size:12px;color:var(--text-soft)}

/* ===== index: ten square flip tiles ===== */
.chgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin-top:26px}
@media(min-width:900px){.chgrid{grid-template-columns:repeat(5,1fr);gap:18px}}
.tile{position:relative;aspect-ratio:1/1;perspective:1100px;cursor:pointer}
.flip{position:absolute;inset:0;transform-style:preserve-3d;
  transition:transform .6s cubic-bezier(.22,1,.36,1)}
.tile.flipped .flip{transform:rotateY(180deg)}
@media(hover:hover){.tile:hover .flip{transform:rotateY(180deg)}}
/* Focus is not gated on hover: a Tab key exists on touch hardware too, and
   without this the next tab stop after the tile is a link on the hidden
   back face. */
.tile:focus-within .flip{transform:rotateY(180deg)}
.face{position:absolute;inset:0;backface-visibility:hidden;
  -webkit-backface-visibility:hidden;border-radius:var(--radius);overflow:hidden;
  border:1px solid var(--line)}
/* A chapter with no opener picture keeps the tile, on a flat panel ground. */
.front{background:var(--navy-panel)}
.front img{width:100%;height:100%;object-fit:cover;display:block}
.front::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(9,22,37,.03),rgba(9,22,37,.42) 52%,rgba(9,22,37,.95))}
/* The menu title standard, shared with the service matrix (.mtile .lab): the
   number sits in a fixed-width right-aligned column so chapter 10 lines up
   with the single digits beside it, a hairline separates it from the title,
   and the title breaks across two lines. Replaces a stacked "CHAPTER 01" that
   spent a line on a word every tile repeated. */
.front .fl{position:absolute;left:12px;right:12px;bottom:11px;z-index:2;
  display:flex;align-items:center;gap:9px}
.front .n{font-family:'IBM Plex Mono',monospace;font-size:15px;font-weight:600;
  color:var(--sky-soft);line-height:1;flex:0 0 auto;
  font-variant-numeric:tabular-nums;min-width:2ch;text-align:right}
.front .vr{flex:0 0 auto;width:1px;align-self:stretch;background:var(--sky-soft);
  opacity:.5}
.front .cn{font-size:clamp(13.5px,1.5vw,19px);font-weight:800;line-height:1.15;
  min-width:0;color:var(--text);text-shadow:0 2px 12px rgba(0,0,0,.65)}
.front .cn w{display:block}
/* The connector in a title like "Communication & Media" is greyed so the two
   real words carry the weight. */
.amp{color:rgba(255,255,255,.42);font-weight:700}
.front .turn{position:absolute;top:10px;right:11px;z-index:2;font-size:15px;
  color:#fff;opacity:.8}
.back{transform:rotateY(180deg);background:linear-gradient(160deg,#123457,#0a1a2e);
  display:flex;flex-direction:column;padding:9px}
.back .bh{font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--sky);font-weight:800;margin-bottom:4px;line-height:1.15;flex:0 0 auto}
.back .bl{display:flex;flex-direction:column;gap:1px;flex:1 1 auto;min-height:0;
  justify-content:safe center;overflow:auto}
.back a{display:flex;align-items:center;gap:7px;padding:4px 6px;border-radius:7px;
  font-size:clamp(10px,1.05vw,13px);font-weight:600;color:var(--text);
  line-height:1.12;transition:background .15s}
.back a:hover,.back a:focus-visible{background:rgba(79,184,231,.16);outline:none}
.back a .d{width:5px;height:5px;border-radius:50%;background:var(--sky);flex:0 0 auto}

/* ===== section: sub-head ===== */
.sub-head{display:flex;gap:22px;align-items:center;flex-wrap:wrap;
  padding-bottom:20px;border-bottom:1px solid var(--line)}
.sub-head h1{font-size:clamp(24px,3.4vw,40px);font-weight:800;margin:6px 0 8px}
/* B1 of the prototype's 2026-08-30 change set: the header is the badge and the
   title, and the title column carries no width floor. `flex:1 1 0` with
   `min-width:0` is what lets a long title WRAP inside its own column instead of
   pushing the header wider; the old `min-width:240px` forced the whole title
   block onto its own line under the badge on a narrow phone. Two lines of title
   beside the badge is the intended look. The `.flagpair` and `.signrow` rules
   that stood here went with the markup B1 removed. */
.sub-head .headtitle{flex:1 1 0;min-width:0}
.sub-head .headtitle h1{margin:0}

.outcome{font-family:'Source Serif 4',serif;font-style:italic;font-size:16px;
  color:var(--text-soft);max-width:680px;line-height:1.6}
.outcome mark{background:none;color:var(--sky);font-style:normal;font-weight:600}
/* B2: the third-country paragraph now closes the page under the similarity
   chart, where it is a paragraph of its own rather than part of the header
   block. The prototype writes this as `.closenote+.outcome`; here every element
   is wrapped in a `.pv-el`, so the chart's `.closenote` is not the outcome's
   sibling and the rule is hung on the position instead. */
main>.outcome{margin-top:16px}

.section-label{font-size:11.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--sky);font-weight:700;margin:34px 0 13px}

/* ===== section: similarity ring ===== */
.headviz{display:flex;flex-direction:column;align-items:center;gap:12px;flex:0 0 auto}
.ring{flex:0 0 auto;width:96px;height:96px;position:relative}
.ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.ring .track{fill:none;stroke:rgba(255,255,255,.09);stroke-width:8}
.ring .arc{fill:none;stroke:var(--sky);stroke-width:8;stroke-linecap:round;
  transition:stroke-dashoffset 1.1s cubic-bezier(.22,1,.36,1)}
.ring .val{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-family:'IBM Plex Mono',monospace;font-weight:600;
  font-size:19px;color:#fff}
.ring .val small{font-size:12px;color:var(--sky);margin-left:1px}
@media (max-width:640px){.headviz{flex-direction:row;align-items:center;gap:14px}}

/* ===== section: two-column layout ===== */
.sublayout{display:grid;grid-template-columns:minmax(0,1fr) 250px;gap:30px;
  align-items:start;margin-top:4px}
.pv-el{min-width:0}
.rail .section-label{margin-top:0}

/* ===== section: reader card ===== */
.reader{background:var(--navy-card);border:1px solid var(--line);
  border-radius:var(--radius);padding:22px 24px}
.prose{font-family:'Source Serif 4',serif;font-size:15.5px;line-height:1.72;color:#D5E2ED}
.prose p+p,.prose .more p{margin-top:12px}
.pv-h3{font-size:16px;font-weight:800;margin-bottom:10px}
/* Open by default so the page reads with the script blocked; library.js adds
   .folded on load and .expanded on click. */
.reader.folded .more,.narr.folded .more{display:none}
.reader.folded.expanded .more,.narr.folded.expanded .more{display:block;margin-top:12px}
.linkbtn{margin-top:13px;color:var(--sky);font-weight:700;font-size:13px;
  display:none;align-items:center;gap:6px}
.reader.folded>.linkbtn,.narr.folded>.linkbtn{display:inline-flex}
.linkbtn:hover{text-decoration:underline}
.linkbtn .chev{transition:transform .25s;display:inline-block}
.reader.expanded>.linkbtn .chev,.narr.expanded .linkbtn .chev{transform:rotate(180deg)}

@media (max-width:1020px){
  /* align-items:stretch is REQUIRED. The base .sublayout rule sets
     align-items:start for the two-column grid, and in a flex column that same
     declaration stops the children stretching: .rail collapses to its content
     width (58px measured in WebKit) and the image strip shows two thirds of one
     thumbnail. Chromium stretches regardless, which is why this only ever
     showed on iPhone. The prototype's _tools/fix_mobile_rail.py. */
  .sublayout{display:flex;flex-direction:column;align-items:stretch;
    /* B3 of the 2026-08-30 change set: the rail moves above the text here, so
       the gallery sits directly on top of the Introduction heading. The 30px
       column gap plus the 34px heading margin left 64px of air between a strip
       of thumbnails and the first line of reading. Halved, on both halves --
       the heading half is the rule below. */
    gap:15px}
  /* Named by position, not by wrapper class. The renderer's own wrapper is
     `.pv-el` and the prototype has a bare `.section-label`; hanging the rule on
     either one alone means a template change on the other side silently
     restores the 34px. Both forms are covered. */
  .sublayout>main>:first-child .section-label:first-child,
  .sublayout>main>.section-label:first-child{margin-top:17px}
  .rail{order:-1;min-width:0;max-width:100%;width:100%;align-self:stretch}
}

/* ===== section: image rail ===== */
.thumbs{display:flex;flex-direction:column;gap:13px}
.thumb{border-radius:12px;overflow:hidden;background:var(--navy-card);
  border:1px solid var(--line);text-align:left;
  transition:transform .25s ease,border-color .25s ease;flex:0 0 auto}
.thumb:hover,.thumb:focus-visible{transform:translateY(-3px);border-color:var(--sky)}
/* Two frames, because the pictures come in two shapes: the RESIZED_V
   derivative is 536x720 and the RESIZED_H one 720x521. One 3/4 frame for both
   meant `cover` cut a landscape photo down to 54% of its width. `.h` is set
   from the picture's `format` (see el/images.html.j2), so the frame matches
   the file and nothing is lost. Custom uploads that follow neither shape are
   still cover-cropped into whichever frame their format names. */
.thumb .ph{aspect-ratio:536/720;position:relative;display:block}
.thumb .ph.h{aspect-ratio:720/521}
.thumb .ph .phimg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* Whose picture this is. Top left over the photograph, above the image but
   below nothing else -- the thumb has no other overlay. The ring is what keeps
   an 18px flag legible against an arbitrary photograph. */
.thumb .cc{position:absolute;top:9px;left:9px;z-index:1;display:block}
.thumb .cc .cflag{width:18px;height:18px;box-shadow:0 0 0 1px rgba(255,255,255,.5),
  0 1px 4px rgba(0,0,0,.55)}
.thumb .tt{display:block;padding:8px 11px;font-size:11.5px;font-weight:600;
  line-height:1.35;color:var(--text)}
.thumb.selected{border-color:var(--sky);box-shadow:0 0 0 1px var(--sky)}

/* The caption is in the markup so it is readable without JavaScript. Once
   the script runs the viewer shows it in full, so the rail hides it. */
.thumb .tcap{display:block;padding:0 11px 9px;font-size:11px;line-height:1.4;
  color:var(--text-soft)}
.js .thumb .tcap{display:none}

/* ===== section: image viewer popup =====
   A fixed panel, vertically centred in the viewport, whose right edge tracks
   the image rail's left edge through `--vx` (library.js measures the rail).
   It used to be an in-flow panel above the two columns, which meant opening a
   thumb from halfway down a long section threw the reader back to the top of
   the page. Nothing scrolls now.

   With the script blocked the panel is never filled and `.open` never added,
   so it stays invisible and inert and the rail's own `.tcap` captions carry
   the text instead (see `.js .thumb .tcap`). */
.viewer{position:fixed;z-index:70;top:50%;right:var(--vx,20px);
  transform:translateY(-50%) scale(.97);transform-origin:100% 50%;
  opacity:0;pointer-events:none;
  transition:opacity .28s ease,transform .28s cubic-bezier(.22,1,.36,1)}
.viewer.open{opacity:1;pointer-events:auto;transform:translateY(-50%) scale(1)}
/* The dimmed page behind the popup. A pseudo-element rather than a node: there
   is nothing to keep in sync, and a click on it reports `.viewer` itself as
   the target (pseudo-element boxes hit-test as their originator), which is how
   library.js separates a backdrop click from a click on the panel. */
.viewer.open::before{content:'';position:fixed;inset:0;z-index:-1;
  background:rgba(4,10,18,.62);backdrop-filter:blur(2px)}
/* The panel is shaped by the picture, like the rail's frames above: `--ar` and
   `--wmax` hold the two shapes and library.js flips them by adding `.h` from
   the thumb's data-fmt. Landscape gets the wider cap since 400px would leave
   it only 290px tall.

   Width follows the viewport height as well as the cap, not a flat --wmax: a
   400px-wide portrait panel stands 537px tall, and a vertically centred panel
   that tall overflows a short window at both ends, where an in-flow one merely
   got scrolled past. The third term keeps a wide panel off the left edge —
   --vx is the gap the popup keeps from the right (see .viewer), so it is also
   how much room is left. The vh line is the fallback for browsers without svh
   — a dropped `width` would collapse the panel, since .big's img is
   positioned. */
.viewer-in{--ar:.744;--wmax:400px;position:relative;
  width:min(var(--wmax),calc((100vh - 48px) * var(--ar)),
            calc(100vw - var(--vx,20px) - 24px));
  width:min(var(--wmax),calc((100svh - 48px) * var(--ar)),
            calc(100vw - var(--vx,20px) - 24px));
  border-radius:var(--radius);overflow:hidden;border:1px solid var(--sky);
  box-shadow:var(--shadow)}
.viewer-in.h{--ar:1.382;--wmax:560px}
.viewer-in .big{aspect-ratio:var(--ar);position:relative;display:block}
.viewer-in .big img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.vcap{position:absolute;left:0;right:0;bottom:0;z-index:2;max-height:40%;
  overflow-y:auto;background:linear-gradient(180deg,rgba(4,10,18,0),rgba(4,10,18,.78) 26%);
  padding:16px 14px 10px}
.vcap .vt{font-size:13.5px;font-weight:800;color:#fff;line-height:1.3}
.vcap .vd{font-size:11px;color:#D9E6F0;line-height:1.5;margin-top:4px}
.vclose{position:absolute;top:8px;right:8px;z-index:3;width:30px;height:30px;
  border-radius:50%;background:rgba(4,10,18,.6);color:#fff;font-size:14px;
  display:flex;align-items:center;justify-content:center;
  border:1px solid rgba(255,255,255,.3)}
.vclose:hover{background:rgba(4,10,18,.85)}

@media (max-width:1020px){
  /* align-items:flex-start, because the row holds both frames now: the default
     stretch would give a 104px-wide landscape card the height of a portrait
     one and leave the difference blank under the photo. */
  .thumbs{flex-direction:row;align-items:flex-start;
    overflow-x:auto;overflow-y:hidden;padding-bottom:8px;
    overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;
    touch-action:pan-x;scroll-snap-type:x proximity}
  .thumb{flex:0 0 104px;scroll-snap-align:start}
  .thumb .tt{display:none}
  .thumb:hover,.thumb:focus-visible{transform:none}
  /* The rail is above the main column here, so "left of the images" has no
     meaning: centre the popup instead. --vx is left unread. */
  .viewer{right:auto;left:50%;transform:translate(-50%,-50%) scale(.97);
    transform-origin:50% 50%}
  .viewer.open{transform:translate(-50%,-50%) scale(1)}
  .viewer-in{width:min(var(--wmax),calc(100vw - 32px),
                       calc((100vh - 48px) * var(--ar)));
    width:min(var(--wmax),calc(100vw - 32px),
              calc((100svh - 48px) * var(--ar)))}
}

/* ===== section: table =====
   .trow0, .thead and .trowbtn share one column template so the three stay
   aligned; the responsive rules below drop Source, then Year, from all of
   them at once. */
.tblwrap{background:var(--navy-card);border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden}
.tscroll{overflow-x:auto}
.trow0,.thead,.trowbtn{display:grid;gap:2px;align-items:center;padding:0 12px;
  grid-template-columns:minmax(200px,1fr) 74px 24px 30px 24px 74px 38px 48px 20px}
.trow0{background:var(--navy-deep);padding-top:10px;padding-bottom:2px}
.trow0 .t-title{font-weight:800;font-size:14px;color:#fff}
.trow0 .t-cty{font-weight:800;font-size:14px;letter-spacing:.02em;
  display:inline-flex;align-items:center;gap:7px}
.trow0 .t-cty.c1{grid-column:2/4;justify-content:flex-end;color:var(--c1-ink)}
.trow0 .t-cty.c2{grid-column:5/7;justify-content:flex-start;color:var(--c2-ink)}
.thead{background:var(--navy-deep);font-size:10px;letter-spacing:.08em;
  text-transform:uppercase;font-weight:700;color:var(--text-soft)}
.thead>span{padding:7px 0 9px;text-align:center}
.thead .h-ind{text-align:left}
.tgroup{padding:9px 16px 5px;font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--sky);font-weight:700;
  background:rgba(79,184,231,.06);border-top:1px solid var(--line)}
.trow{border-top:1px solid rgba(255,255,255,.05)}
.trow.tint .trowbtn{background:var(--stripe)}
.trowbtn{width:100%;text-align:left;padding-top:8px;padding-bottom:8px;
  transition:background .2s}
.trowbtn:hover:not(.noexpand){background:rgba(79,184,231,.09)!important}
.trowbtn:focus-visible{outline:2px solid var(--c2-ink);outline-offset:-2px}
/* A row with nothing to expand into is rendered as a plain div (see
   table.html.j2), not a <button> — no hover/focus affordance that would
   promise an expand that isn't there. */
.trowbtn.noexpand{cursor:default}
.tind{font-size:13.5px;font-weight:600;color:var(--text);min-width:0}
.tind .pv-smalldesc{display:block;font-weight:400;font-size:11px;
  color:var(--text-soft);margin-top:1px}
.tval{font-family:'IBM Plex Mono',monospace;font-size:12.5px;text-align:right;
  white-space:nowrap;color:#fff;padding-right:10px}
.tval.c2{text-align:left;padding-left:10px;padding-right:0}
.tmid{display:flex;justify-content:center;align-items:center}
.trend{display:flex;align-items:flex-end;gap:2.5px;height:16px}
.trend i{width:4.5px;border-radius:1.5px 1.5px 0 0;display:block}
.trend.c1 i{background:var(--c1-ink)}
.trend.c2 i{background:var(--c2-ink)}
.trend i:last-child{box-shadow:0 0 6px rgba(79,184,231,.55)}
.tsrc{font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--text-soft);
  text-align:center;white-space:nowrap}
.tchev{color:var(--text-soft);transition:transform .25s;text-align:center;font-size:11px}
.trow.open .tchev{transform:rotate(180deg)}
.gapsvg{display:block}
.closenote{font-size:11.5px;color:var(--text-soft);margin-top:10px;
  line-height:1.55}
/* Only inside the table wrapper is this an inset caption at a reading
   measure; the similarity figure caption spans its full-width chart. */
.tblwrap .closenote{max-width:760px;padding:0 12px 12px}

/* ===== section: table tabs ===== */
.tbltabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
.tbltab{font-size:12.5px;font-weight:700;padding:7px 14px;border-radius:999px;
  border:1.5px solid var(--line);background:rgba(9,22,37,.6);color:var(--text-soft);
  transition:all .18s}
.tbltab:hover{border-color:var(--sky);color:var(--text)}
.tbltab.on{background:rgba(79,184,231,.18);border-color:var(--sky);color:#fff}
/* Every pane is in the HTML; the script hides the inactive ones, so with the
   script blocked both tables read one after the other. */
.tblpane+.tblpane{margin-top:18px}
.js .tblpane{display:none}
.js .tblpane.on{display:block}

/* ===== section: table responsive — drop Source, then Year, then squeeze ===== */
@media (max-width:880px){
  .c-src{display:none!important}
  .trow0,.thead,.trowbtn{grid-template-columns:minmax(150px,1fr) 68px 22px 28px 22px 68px 36px 20px}
}
@media (max-width:640px){
  .c-yr{display:none!important}
  .trow0,.thead,.trowbtn{grid-template-columns:minmax(120px,1fr) 62px 21px 27px 21px 62px 18px}
  .tind{font-size:12.5px}
  .tval{font-size:11.5px}
}
@media (max-width:420px){
  .trow0,.thead,.trowbtn{grid-template-columns:minmax(96px,1fr) 54px 20px 25px 20px 54px 16px;
    gap:2px;padding:0 8px}
  .tind{font-size:12px}
  .tind .pv-smalldesc{font-size:10px}
  .tval{font-size:10.5px}
  .trow0 .t-cty{font-size:12.5px;gap:5px}
  .trow0 .t-title{font-size:12.5px}
}

/* ===== section: table row detail ===== */
/* Open in the markup; library.js adds .js-fold, which closes them. */
.tdetail{padding:15px 16px 18px;background:rgba(9,22,37,.55);
  border-top:1px dashed var(--line)}
.js .trow .tdetail{display:none}
.js .trow.open .tdetail{display:block;animation:fadeUp .3s ease both}
@keyframes fadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
/* The publisher's own definition of the indicator, set in the reading serif
   because it is prose and can run to a paragraph. */
.method{font-family:'Source Serif 4',serif;font-size:14px;line-height:1.6;
  color:#CBDAE7;max-width:760px}
.method b{color:var(--text)}
/* Two one-liners of the same weight: the unit/topics of the measurement, and
   which end of the value range holds rank #1. */
.arrownote,.detmeta{margin-top:7px;font-size:11.5px;color:var(--text-soft)}
.arrownote b,.detmeta b{color:var(--sky);font-weight:600}
.worldctx{margin-top:9px;font-size:11px;color:var(--text-soft);max-width:760px}
.srcfull{margin-top:12px;font-size:11px;color:var(--text-soft);line-height:1.5}
.srcfull b{font-family:'IBM Plex Mono',monospace;color:var(--sky);font-weight:500}

/* ===== section: rank ruler ===== */
.ruler{margin-top:14px;max-width:760px}
/* Each end is a span holding "#1 Country" / "Country #193", so the rank number
   and its country name stay together when the two ends are pushed apart. */
.rends{display:flex;justify-content:space-between;font-size:11px;
  color:var(--text-soft);margin-bottom:7px;min-height:14px;gap:12px}
.rends>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rends>span:last-child{text-align:right}
.rends b{font-family:'IBM Plex Mono',monospace;font-weight:500;color:var(--text)}
.rbody{position:relative}
.rtrack{position:relative;height:12px;border-radius:7px;
  background:linear-gradient(90deg,#4FB8E7,#2F6FB0 45%,#1B3A5E 80%,#122740)}
.rdot{position:absolute;top:50%;transform:translate(-50%,-50%);z-index:2}
.rdot .cflag{width:20px;height:20px;
  box-shadow:0 0 0 2px #0B1B2E,0 0 0 3px rgba(255,255,255,.35)}
.rlabels{position:relative;height:20px;margin-top:8px}
.rlab{position:absolute;top:0;font-size:11.5px;white-space:nowrap;color:var(--text);
  transform:translateX(-50%)}
.rlab b{font-family:'IBM Plex Mono',monospace;font-weight:600;color:var(--sky)}

/* ===== section: two narratives ===== */
.narratives{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.narr{background:var(--navy-card);border:1px solid var(--line);
  border-radius:var(--radius);padding:20px 22px;display:flex;flex-direction:column}
.narr h3{font-size:16px;font-weight:800;margin-bottom:10px;display:flex;
  align-items:center;gap:9px}
@media (max-width:840px){.narratives{grid-template-columns:1fr}}

/* ===== section: closing similarity chart ===== */
.closegrid{display:flex;gap:16px;flex-wrap:wrap}
/* Full width by request, rather than the reference's content-sized boxes. */
.cbox{border-radius:12px;overflow:hidden;border:1px solid var(--line);
  background:var(--navy-card);display:flex;flex-direction:column;flex:1 1 260px;
  min-width:0}
.cbox .cbh{padding:8px 13px;font-weight:700;font-size:13px;color:#fff}
.cbox.a .cbh{background:#1E3864}
.cbox.b .cbh{background:#1786B5}
.cbscale{display:flex;justify-content:space-between;padding:6px 13px 2px;
  font-size:9.5px;letter-spacing:.08em;color:var(--text-soft);text-transform:uppercase}
.cblist{position:relative;max-height:400px;overflow-y:auto;padding:6px 8px 8px}
.crow{display:grid;grid-template-columns:18px 1fr;gap:6px;align-items:center;
  padding:2.5px 1px}
.crow .cbar{position:relative;height:19px;border-radius:5px;
  background:rgba(255,255,255,.05);overflow:hidden}
.crow .cbar i{position:absolute;left:0;top:0;bottom:0;border-radius:5px;opacity:.32}
.cbox.a .cbar i{background:linear-gradient(90deg,#2F5D9E,#4FB8E7)}
.cbox.b .cbar i{background:linear-gradient(90deg,#0E6E96,#29ADE4)}
.crow .cname{position:absolute;left:7px;top:50%;transform:translateY(-50%);
  display:flex;gap:5px;align-items:baseline;font-size:10.5px;white-space:nowrap}
.crow .cname b{font-family:'IBM Plex Mono',monospace;font-weight:500;
  color:var(--text-soft);font-size:9.5px}
.crow .cpct{position:absolute;top:50%;transform:translate(-100%,-50%);
  font-family:'IBM Plex Mono',monospace;font-size:9.5px;color:var(--sky-soft);
  white-space:nowrap;background:rgba(9,22,37,.6);padding:1px 4px;border-radius:5px}
.crow.mark .cbar{box-shadow:inset 0 0 0 1.5px var(--sky)}
.crow.mark .cbar i{opacity:.6}
.crow.mark .cname{font-weight:800;color:#fff}
.crow.break{justify-items:center}
/* The break glyph is one flat dark shape on transparent, so it would vanish
   here. Drawn as a mask, not an <img>: the PNG's alpha gives the shape and
   the fill is the row's own colour. */
.cdots{display:inline-block;width:1.2rem;height:1.2rem;background:var(--text-soft);
  -webkit-mask:var(--dots) center / contain no-repeat;
          mask:var(--dots) center / contain no-repeat}
@media (max-width:640px){.closegrid{flex-direction:column;align-items:stretch}}

/* ===== section: generic element cards =====
   The vocabulary every element the demo does not cover uses. One card idiom,
   so a timeline and an expert quote sit on the same ground as a reader. */
.pv-card{background:var(--navy-card);border:1px solid var(--line);
  border-radius:var(--radius);padding:18px 20px;margin-bottom:14px}
.pv-muted{font-size:11.5px;color:var(--text-soft);line-height:1.55}
.pv-note{background:var(--navy-panel);border:1px solid var(--line);
  border-radius:12px;padding:10px 14px;font-size:12.5px;color:var(--text-soft)}
.pv-tag{font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--sky);
  background:rgba(79,184,231,.13);border:1px solid rgba(79,184,231,.35);
  padding:2px 8px;border-radius:6px}
.pv-quote{font-family:'Source Serif 4',serif;font-style:italic;font-size:14.5px;
  line-height:1.65;color:#D5E2ED}
.pv-byline{font-size:12px;margin-top:8px}
.pv-byline strong{color:var(--text)}
.pv-scroll{overflow-x:auto}
.pv-smalldesc{font-size:.85em;color:var(--text-soft)}
.pv-cols{display:grid;gap:1.5rem;grid-template-columns:repeat(2,minmax(0,1fr))}
@media (max-width:48rem){.pv-cols{grid-template-columns:1fr}}

/* The plain ranked/grouped tables: the Library's treaties and members
   elements, and both UN Insights doors' tables (door 01's seats/memberships
   and officials, door 02's seats and co-service) — inout has its own
   pv-io-t, and the rest of the "generic card" templates aren't tables. */
.pv-t{width:100%;font-size:.8rem;border-collapse:collapse}
.pv-t td{padding:.1rem .3rem}
.pv-t td:not(:first-child){text-align:right;white-space:nowrap}
.pv-t tbody tr:nth-child(odd){background:var(--stripe)}
.pv-key{font-weight:600;color:var(--sky)}
/* treaties/members: the ordinal is the visual first column, but the
   description after it is prose, not a short right-aligned figure — override
   .pv-t's default assumption that later columns are numeric. */
.pv-t td.pv-t-idx{text-align:right;white-space:nowrap;vertical-align:top}
.pv-t td.pv-t-desc{text-align:left;white-space:normal}

/* .pv-fig: img-fluid/d-block/mx-auto's replacement — a centred, capped figure
   image, used by knowledge, cuisine and dipfootprint. Leadership's portrait
   is .pv-lead-img instead: it floats rather than centres. */
.pv-fig{display:block;margin:0 auto;max-width:100%}

/* inout's right-hand percentage column: was Bootstrap's ms-auto. */
.pv-io-p{white-space:nowrap;margin-left:auto}

/* leadership: portrait floated at the top-left of a bio that wraps around
   it — was Bootstrap's clearfix + float-start. */
.pv-lead-country{color:var(--text-soft)}
.pv-lead{overflow:hidden;margin-bottom:14px}
.pv-lead-img{float:left;width:30%;border-radius:8px;margin:0 10px 6px 0}

/* ===== section: timeline =====
   A card on the same ground as the table: a header naming the two sides, a
   chip row, and the timeline itself scrolling inside a fixed-height box rather
   than running the length of the page (a leadership pair is ~130 entries over
   as many centuries).

   .tlbox scrolls on both axes: the grid keeps a min-width because the book
   gives the timeline a whole page and much under 34rem every second name
   wraps, so on a narrow viewport this box takes over rather than the page. */
.tlwrap{background:var(--navy-card);border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden}
.tlhead,.tlgrid{display:grid;grid-template-columns:1fr 60px 1fr}
.tlhead{padding:10px 18px;background:var(--navy-deep)}
.tlhead div{font-weight:800;font-size:14px}
.tlhead div:first-child{text-align:right;color:var(--c1-ink)}
.tlhead div:last-child{color:var(--c2-ink)}
.tlchips{display:flex;gap:8px;padding:10px 18px;flex-wrap:wrap;
  align-items:center;border-bottom:1px solid var(--line)}
.tlchip{padding:5px 13px;border-radius:999px;border:1px solid var(--line);
  font-size:12.5px;font-weight:700;color:var(--text-soft);text-decoration:none;
  transition:border-color .2s,color .2s,background .2s}
.tlchip:hover,.tlchip:focus-visible{border-color:var(--sky);color:#fff;
  background:rgba(79,184,231,.14)}
/* The caption the book prints under the figure. Pushed to the far end of the
   chip row so it reads as the card's subtitle without taking its own line. */
.tlcap{margin-left:auto;font-size:11px;color:var(--text-soft)}
.tlbox{max-height:560px;overflow:auto;padding:14px 18px;position:relative}
.tlgrid{row-gap:3px;min-width:34rem}
/* One element spanning every slot. Empty slots collapse to nothing and leave
   only their row gap, so a century with no entries reads as a quiet stretch of
   axis — the sparseness of `rbr` becomes the timeline's sense of scale. */
.tlaxis{grid-column:2;justify-self:center;width:2px;
  background:linear-gradient(180deg,rgba(79,184,231,.5),rgba(79,184,231,.12))}
.tle{font-size:12.5px;line-height:1.45;padding:1px 0;scroll-margin-top:40px}
.tle.l{grid-column:1;text-align:right;padding-right:14px}
.tle.r{grid-column:3;padding-left:14px}
.tle .yy{font-family:'IBM Plex Mono',monospace;color:var(--sky);font-size:11.5px;
  white-space:nowrap}
/* type=2: an era or event, not a person — the book sets it all bold. */
.tle.ev{font-weight:800;color:#fff}
.tle a{color:var(--text);text-decoration:none;
  border-bottom:1px dotted rgba(79,184,231,.55)}
.tle a:hover,.tle a:focus-visible{color:var(--sky)}
.wikinote{font-size:11.5px;color:var(--text-soft);padding:9px 18px;
  border-top:1px solid var(--line)}

/* ===== section: inout =====
   The element every other one is sized to: the book gives this a whole
   two-column page, and the ranking and the breakout each need about half of
   it — hence the 56rem breakpoint below. */
.pv-io{width:100%}
.pv-io-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;align-items:start}
@media (max-width:56rem){.pv-io-grid{grid-template-columns:1fr}}
.pv-io-t{width:100%;font-size:.8rem}
.pv-io-t th{font-weight:600;font-size:.7rem;text-transform:uppercase;
  letter-spacing:.03em;color:var(--text-soft);border-bottom:1px solid var(--line);
  padding:.1rem .3rem}
.pv-io-t td{padding:.1rem .3rem}
.pv-io-t td:not(:first-child),.pv-io-t th:not(:first-child){text-align:right;white-space:nowrap}
.pv-io-t tbody tr:nth-child(odd){background:var(--stripe)}
/* The rows the right-hand side details — the book sets them bold and in the
   country's colour, and hangs the arrows off them. */
.pv-io-key{font-weight:600;color:var(--sky)}
.pv-io-g{margin-bottom:.9rem}
.pv-io-gh{font-weight:600;color:var(--sky);font-size:.8rem;margin-bottom:.1rem}
.pv-io-arrow{opacity:.7}
.pv-io-row{display:flex;gap:.4rem;font-size:.8rem;padding:.05rem .3rem}
.pv-io-row:nth-child(odd){background:var(--stripe)}
.pv-io-pos{flex:0 0 1.2rem;color:var(--text-soft)}
.pv-io-name{flex:1 1 auto}
.pv-io-x{color:var(--sky);font-weight:600}
.pv-io-other{font-style:italic;color:var(--text-soft)}

/* ===== section: media =====
   The book's 4-column block, striped as it stripes it. Lived inline in
   section.html.j2's extrastyle block until d82173f dropped that block
   without relocating these rules — media.html.j2 has rendered as one
   un-columned, unstriped, un-ellipsised list ever since. Recovered from
   d82173f^ and given a home here alongside timeline's and inout's own
   relocated element CSS. */
.pv-media{column-count:4;column-gap:1rem;min-width:34rem}
.pv-media-row{display:flex;justify-content:space-between;gap:.4rem;
  font-size:.8rem;padding:.05rem .2rem;break-inside:avoid}
.pv-media-row:nth-child(odd){background:var(--stripe)}
.pv-media-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pv-media-cov{color:var(--text-soft);white-space:nowrap}

/* ===== portal shell =====
   The rules above this point predate the portal shell -- they came from the
   Library (formerly preview) stylesheet, which portal.css started life as a
   copy of. These rules are the shell's own: the pair label, the ten-service
   tile grid and its two states, and the footer stamp. The .tile class name
   collides with the Library's chapter flip-tile above (square, 3D
   perspective, hover flip) -- these service tiles are a plain list, not a
   flip card, so the first three declarations undo the
   perspective/aspect-ratio/cursor this class inherits from that earlier
   rule. Minimal by decision: this is the shell's skeleton, not a visual
   design pass -- unlike the Library, which got one (see
   docs/portal-design-drift.md). */
.pair{font-size:12.5px;color:var(--text-soft)}

@media(min-width:900px){.tiles{grid-template-columns:repeat(5,1fr)}}

.sourceline{max-width:1220px;margin:0 auto;padding:16px 20px 40px;font-size:12px;
  color:var(--text-soft);border-top:1px solid var(--line)}


/* ======================================================================
   THE CARD MENU (portal/home.html.j2)

   Ported from the prototype's _live/site/index.html. Two numbers here are
   deliberately NOT the ones .wrap/.subwrap use: a card menu is 1260px wide
   against the 1220px of a Library page, and its top padding is 26px against
   28px. The prototype sets both explicitly, so they are an opinion, not drift.
   ====================================================================== */
.menuwrap{max-width:1260px;margin:0 auto;padding:26px 20px 80px}

.tgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:13px}
@media(min-width:900px){.tgrid{grid-template-columns:repeat(5,1fr);gap:16px}}

/* A tile is square and the photograph fills it. `overflow:hidden` is what
   keeps the image inside the 14px radius. */
.mtile{position:relative;aspect-ratio:1/1;border-radius:var(--radius);
  overflow:hidden;border:1px solid var(--line);display:block;min-width:0;
  transition:transform .18s,border-color .18s,box-shadow .18s}
a.mtile:hover,a.mtile:focus-visible{transform:translateY(-3px);
  border-color:rgba(79,184,231,.55);box-shadow:0 14px 30px rgba(4,12,22,.55);
  outline:none}
.mtile picture,.mtile img{display:block;width:100%;height:100%;object-fit:cover}
/* The scrim. Without it the title is unreadable over a light photograph. */
.mtile::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(9,22,37,.05),rgba(9,22,37,.44) 50%,rgba(9,22,37,.95))}

/* The menu title standard: a bare number left of a two-line title, a hairline
   between them, nothing else on the photograph. */
.mtile .lab{position:absolute;left:12px;right:12px;bottom:10px;z-index:2;
  display:flex;align-items:center;gap:9px}
.mtile .n{font-family:'IBM Plex Mono',monospace;font-size:15px;font-weight:600;
  color:var(--sky-soft);line-height:1;flex:0 0 auto;
  font-variant-numeric:tabular-nums;min-width:2ch;text-align:right}
.mtile .vr{flex:0 0 auto;width:1px;align-self:stretch;background:var(--sky-soft);
  opacity:.5}
/* Colour set explicitly: an open tile is an <a>, and the global a{color:--sky}
   would otherwise tint the title blue. The standard says the title is white. */
.mtile .cn{font-size:clamp(12.5px,1.35vw,16.5px);font-weight:800;line-height:1.15;
  min-width:0;color:var(--text);text-shadow:0 2px 12px rgba(0,0,0,.7)}
.mtile .cn w{display:block}

/* An announced service: the photograph drains of colour and the title greys.
   Desaturating rather than hiding keeps the shape of the finished platform
   visible from the first screen. */
.mtile.off{cursor:default;filter:saturate(.3)}
.mtile.off .cn{color:var(--text-soft)}
.mtile.off::after{background:linear-gradient(180deg,rgba(9,22,37,.5),
  rgba(9,22,37,.8) 45%,rgba(9,22,37,.97))}

/* ===== service 02: UN Insights ===== */
/* Headline figures: the four numbers the page is an argument about. */
.un-figs{display:flex;flex-wrap:wrap;gap:14px;margin:22px 0 4px}
.un-fig{flex:1 1 160px;background:var(--navy-card);border:1px solid var(--line);
  border-radius:var(--radius);padding:14px 18px}
.un-fig b{display:block;font-family:'IBM Plex Mono',monospace;font-size:26px;
  font-weight:600;color:#fff;line-height:1.1}
.un-fig span{font-size:11.5px;color:var(--text-soft)}

.un-t th{font-weight:600;font-size:.7rem;text-transform:uppercase;
  letter-spacing:.08em;color:var(--text-soft);text-align:left;
  padding:.1rem .3rem;border-bottom:1px solid var(--line)}
.un-t th:not(:first-child){text-align:right}

/* The seat filter is CSS only: four radios and :has(). Every row is in the
   markup, so with the script blocked, or :has() unsupported, the page simply
   shows the whole table -- the same progressive-enhancement rule the Library's
   folds follow, and the reason there is no un.js. */
.un-chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
.un-chips input{position:absolute;width:1px;height:1px;opacity:0}
.un-chips label{border:1px solid var(--line);border-radius:99px;
  padding:4px 12px;font-size:12px;font-weight:600;color:var(--text-soft);
  cursor:pointer;transition:background .15s,color .15s}
.un-chips label:hover{background:rgba(79,184,231,.16);color:var(--text)}
.un-chips input:checked+label{background:var(--sky);border-color:var(--sky);
  color:var(--navy-deep)}
.un-chips input:focus-visible+label{outline:2px solid var(--sky-soft);
  outline-offset:2px}
.un-seats:has(#un-who-both:checked) tbody tr:not([data-who="both"]),
.un-seats:has(#un-who-a:checked) tbody tr:not([data-who="a"]),
.un-seats:has(#un-who-b:checked) tbody tr:not([data-who="b"]){display:none}

/* ---- Election Watch (UN Insights door 03) -------------------------------- */

/* Two independent CSS-only filters: an expiry window and a procedure tag.
   Every row is in the markup, so with :has() unsupported the page simply shows
   all of them. A row carries data-win="365 730" when it is inside a year,
   "730" when inside two, and "" beyond -- so the ~= test ANDs cleanly with the
   tag filter without either knowing about the other. */
.ew:has(#ew-win-365:checked) tbody tr:not([data-win~="365"]),
.ew:has(#ew-win-730:checked) tbody tr:not([data-win~="730"]),
.ew:has(#ew-tag-ROTATION:checked) tbody tr:not([data-tag="ROTATION"]),
.ew:has(#ew-tag-GROUP_SLATE:checked) tbody tr:not([data-tag="GROUP_SLATE"]),
.ew:has(#ew-tag-CONTESTED:checked) tbody tr:not([data-tag="CONTESTED"]),
.ew:has(#ew-tag-UNDERSUBSCRIBED:checked) tbody tr:not([data-tag="UNDERSUBSCRIBED"]),
.ew:has(#ew-tag-APPOINTED:checked) tbody tr:not([data-tag="APPOINTED"]),
.ew:has(#ew-tag-UNKNOWN:checked) tbody tr:not([data-tag="UNKNOWN"]),
.ew:has(#ew-tag-UNCLASSIFIED:checked) tbody tr:not([data-tag="UNCLASSIFIED"]){display:none}

.ew-leg{display:grid;gap:7px;margin:14px 0 4px;font-size:12.5px;
  color:var(--text-soft)}
.ew-chip{display:inline-block;border-radius:99px;padding:1px 9px;font-size:11px;
  font-family:'IBM Plex Mono',monospace;border:1px solid var(--line);
  margin-right:7px;white-space:nowrap}
.ew-contested{background:rgba(231,111,81,.18);border-color:rgba(231,111,81,.5)}
.ew-group_slate{background:rgba(79,184,231,.16);border-color:rgba(79,184,231,.45)}
.ew-rotation{background:rgba(138,201,158,.16);border-color:rgba(138,201,158,.45)}
.ew-undersubscribed{background:rgba(233,196,106,.16);border-color:rgba(233,196,106,.5)}
.ew-appointed,.ew-unknown,.ew-unclassified{background:transparent;
  color:var(--text-soft)}
.ew-warn{color:#e9c46a}
.ew-queued{font-size:10.5px;color:#e9c46a;margin-left:5px}
.ew-race{font-size:14px;margin:20px 0 8px;font-weight:600}
.ew-race span{font-weight:400;font-size:12px;margin-left:8px}

/* The field switcher. The group codes vary by pair, so the races are marked
   data-side="a"/"b" and these two rules stay static. With :has() unsupported
   every race shows, which is the "Both" state and is readable. */
.ewf:has(#ew-grp-a:checked) .ew-racebox[data-side="b"],
.ewf:has(#ew-grp-b:checked) .ew-racebox[data-side="a"]{display:none}

/* NOT .un-chips: that class visually hides its inputs (its radios are
   presented by their labels), which would make a real search box
   invisible. .ew-find carries its own layout for the same reason. */
.ew-find{display:flex;flex-wrap:wrap;gap:8px;align-items:center;
  margin-bottom:12px}
.ew-find input[type=search]{background:var(--navy-card);
  border:1px solid var(--line);border-radius:99px;padding:5px 13px;
  color:var(--text);font-size:12.5px;min-width:240px}
.ew-find .count{font-size:11.5px}
