/* ============================================================================
   Apex Minds — "The Ruled Page" system layer.  See DESIGN.md.
   Loaded AFTER styles.v2.css. Two jobs:
     1. Remap the existing v2 token names onto the system palette so the whole
        app (list / detail) adopts it with no per-component edits.
     2. Add the landing-hero + search-page components.

   A working instrument, not a campaign. The зошит у клітинку concept survives
   as ACCENT, not wallpaper: the faint grid rules only surfaces that carry
   mathematics, the red pencil appears only where the archive corrects its own
   printed source, and nothing else borrows the metaphor.

   Neutrals and typefaces are inherited from Apex Talent (indigo/bone, plus
   Unbounded / Inter / Caveat); the accent is NOT. #5BF55B is a poster colour
   and unreadable across 162 pages of results, so cobalt — the blue pen the
   зошит is written in — stays the single action colour. Themes are NOT
   colour-coded.
   ============================================================================ */

:root {
    /* ── Neutrals (inherited: Apex Talent indigo / bone) ──────────────────── */
    --ground:#F6F6EE;           /* the page            — bone-100 */
    --card:#FBFBF2;             /* any raised surface  — bone-50  */
    --ink:#191E36;              /* all primary text    — indigo-900 */
    --ink-2:#4A5170;            /* secondary text      — 6.9:1 on ground */
    --line:#D8D6CA;             /* rules and dividers  — never text */
    --grid:#DDE2F2;             /* the 23px клітинка ruling — decorative only */

    /* THE INK-MUTED FLOOR. #6A6F7E is the lightest colour permitted to set
       text anywhere, at any size: 4.60:1 on --ground, 4.93:1 on --card.
       The retired --ink-3 (#8A8F9C) measured 2.97:1 and was the single cause
       of every contrast failure in the 2026-09-20 audit. It survives ONLY as
       a hairline / icon stroke and must never be given to a text node. */
    --ink-muted:#6A6F7E;
    --ink-3:#8A8F9C;            /* DEPRECATED for text — strokes + hairlines only */

    /* ── Accent: the blue pen (action) ────────────────────────────────────── */
    --cobalt:#2C46D8;           /* fills, borders, focus ring, selected state */
    --cobalt-deep:#1E2E9A;      /* cobalt as TEXT — 10.8:1 on ground */
    --cobalt-wash:#ECEEFB;      /* hover beds, chip fills, selected rows */
    --cobalt-hover:#243AB4;

    /* ── Accent: the red pencil (editorial correction ONLY) ───────────────── */
    /* Not an error colour and not a destructive colour. If red shows up for any
       other reason the metaphor is spent and real corrections stop reading. */
    --red:#D6453D;
    --red-ink:#B3241C;
    --red-wash:#FBE9E7;

    /* ── Accent: sprout (inherited) — the handwritten voice + confirmation ── */
    /* #5BF55B never sets text; --sprout-ink does (5.2:1 on ground). */
    --sprout:#5BF55B;
    --sprout-ink:#007E0D;
    --sprout-wash:#DAF6D8;

    /* ── Type ─────────────────────────────────────────────────────────────── */
    /* Every face here sets Ukrainian in its own glyphs. Fraunces and DM Sans
       were removed 2026-09-20: both are Latin-only upstream, so the 59px hero
       rendered in Georgia and all Cyrillic body text fell to the OS default.
       The --font-serif alias (it pointed at Unbounded) was retired
       2026-09-27, when a real serif arrived: a rule naming "serif" and
       getting a geometric sans was a trap. The display face is --font-display;
       the one serif is --font-math. */
    --font-sans:'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-display:'Unbounded', system-ui, sans-serif;
    --font-script:'Caveat', cursive;
    /* A problem's own text — statement, answer, solution — is set in the face
       its mathematics is drawn in, so prose and formulas are one typeface.
       KaTeX_Main next: the same design, and it carries the ≤ ≥ ⇒ that the
       CMU subset leaves out. */
    --font-math:'CMU Serif', KaTeX_Main, 'Times New Roman', serif;

    /* ── THE SIZE RAMP. Seven steps, every one a whole pixel. ──────────────
       This replaces 26 fractional rem values (.6 / .62 / .7 / .72 / .74 /
       .75 / .78 / .8 / .82 / .83 / .84 / .85 / .855 / .86 / .875 / .88 / .9 /
       .92 / .95 / .96 / .98 / 1 / 1.02 / 1.05 / 1.06 / 1.08 …), of which
       EIGHT rendered below the 13px floor this system claims to hold and
       eight more landed inside a 1.6px band — 13.12 / 13.6 / 13.68 / 13.76 /
       14.08 / 14.4 / 14.72 — where no reader can perceive a step. A ramp whose
       neighbours differ by half a pixel is not a hierarchy; it is noise that
       also refuses to align. Sizes are px, not rem, because a ramp built on
       hundredths of a root is how the fractions got here.

       Two sizes carry prose, and the split is deliberate: --fs-body is the
       SCANNING voice (a three-line snippet in a result card, an input), and
       --fs-read is the READING voice (a statement or solution the student
       stays inside for an hour). They never appear on the same surface, so
       the 1px difference is never asked to signal hierarchy. */
    --fs-micro:13px;    /* the floor — counts, captions, chips, eyebrows */
    --fs-label:14px;    /* facet rows, metadata, nav, buttons, tabs */
    --fs-body:16px;     /* inputs, result snippets, UI prose */
    --fs-read:17px;     /* statement + solution: the mathematics, and the
                           largest body text on the site by intent */
    --fs-lead:20px;     /* hero subtitle, dialog + section titles */
    /* Problem text in --font-math. NOT a new step: it is the 20px step.
       Computer Modern's x-height is 0.43em to Inter's 0.55em, so CMU at 17px
       reads a size smaller than the Inter it replaces; at 20px it matches
       Inter at 16–17px, and it is where KaTeX already drew the formulas
       (1.21em of 17px ≈ 20.6px) — the math keeps its size, the words join it. */
    --fs-math:var(--fs-lead);
    --fs-title:clamp(26px, 3.2vw, 34px);   /* problem title */
    --fs-display:clamp(34px, 5.2vw, 58px); /* the landing hero, and nothing else */

    /* ── Remap v2 tokens onto the system palette (one source of truth) ────── */
    --bg-primary:#F6F6EE;
    --bg-secondary:#FBFBF2;
    --text-primary:#191E36;
    --text-secondary:#4A5170;
    --border-color:#D8D6CA;

    --color-primary-50:#ECEEFB;
    --color-primary-100:#DDE2F8;
    --color-primary-200:#C2C9F0;
    --color-primary-500:#2C46D8;
    --color-primary-600:#243AB4;
    --color-primary-700:#1E2E9A;
    --color-primary-800:#172478;

    --brand-blue:#2C46D8;
    --brand-navy:#191E36;
    --brand-blue-50:#ECEEFB;
    --brand-blue-700:#1E2E9A;

    --color-gray-50:#F6F6EE;
    --color-gray-100:#F1EFE5;
    --color-gray-200:#D8D6CA;
    --color-gray-300:#C6C3B4;

    /* ── Depth: FLAT AT REST ──────────────────────────────────────────────── */
    /* Nothing that sits still casts a shadow. Separation is the ground→card
       tonal step, a 1px hairline, and the grid. Hover changes colour and
       border; it never lifts. The retired --shadow-lg was a 40px cobalt bloom
       under a 1px border — the exact combination this rule exists to prevent.
       --shadow-sm/-md are kept as no-ops so v2 rules that still reference them
       resolve to nothing instead of to a stale glow. */
    --shadow-sm:none;
    --shadow-md:none;
    --shadow-lg:none;
    /* The one real shadow: a surface that has taken over the screen. */
    --shadow-overlay:0 8px 32px -12px rgba(25,30,54,.28);

    /* ── Shape ────────────────────────────────────────────────────────────
       Four steps and a pill, and every corner in the sheet is one of them.
       The old scale (4 / 8 / 16) described the DOC; the CODE shipped eleven
       radii — 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 16 — which is the type ramp's
       disease in another property: one composition on the landing carried a
       13px field, a 9px button inside it and a 4px button beside it.

       Where the two disagreed, the code was closer to right: this is a tool
       schoolchildren use, and the shipped corners are softer than «4px for
       nearly everything» claimed. So the scale follows the shipped intent,
       regularised, and the tiers are nested — a control inside a card is one
       step down, which is what makes a corner look drawn rather than applied. */
    --radius-xs:4px;     /* marks, inline bits, a chip inside a control */
    --radius-sm:8px;     /* controls: buttons, inputs, chips, facet rows */
    --radius-md:12px;    /* cards: result, featured, the search field */
    --radius-lg:16px;    /* the problem panel, dialogs, the filter sheet */
    --radius-xl:16px;    /* alias, kept for styles.v2.css rules */
    --radius-full:999px; /* pills only */

    /* ── Space ────────────────────────────────────────────────────────────
       The inherited Apex Talent scale, now with 20 and 40 in it. The claim
       that "nothing uses a value off it" was false in about thirty places,
       and the off-scale values were not random: 18 / 20 / 22 / 26 / 28 / 36 /
       38 clustered in exactly the two gaps the scale left open (16→24 and
       32→48). Components kept reaching there because a card's inner padding
       and a section's top rule genuinely want a value between 16 and 24. Add
       the two rungs, snap the literals, and the rule becomes true instead of
       aspirational. */
    --space-3xs:4px;  --space-2xs:8px;  --space-xs:12px; --space-sm:16px;
    --space-ms:20px;  --space-md:24px;  --space-lg:32px; --space-ml:40px;
    --space-xl:48px;  --space-2xl:64px; --space-3xl:96px;

    /* The page gutter. It was a flat 28px at every width, which on a 375px
       phone spent 15% of the screen before the panel's own padding took
       another 15%: 30% of the primary device's viewport was chrome. It now
       opens with the viewport. */
    --gutter:clamp(16px, 4vw, 32px);

    /* ── Motion ───────────────────────────────────────────────────────────── */
    --ease:cubic-bezier(.2,0,0,1);
    --dur-fast:120ms;
    --dur-sheet:220ms;

    --header-h:65px;            /* matches .site-header height */
    --content-max:1200px;
    /* The result column is sized so the CARD is the measure: 256px rail +
       768px results, less the column's 24px padding and the card's own 20px,
       leaves a 680px snippet line — about 75 Ukrainian characters. At the old
       1120 it was 87, and capping the text inside a 1120-wide card only moved
       the problem into 180px of empty card. */
    --search-max:992px;

    /* Drawn checkmark, used as a mask by every checkbox on the site. */
    --tick:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'><path d='m4 12.5 5.5 5.5L20 6'/></svg>");

    /* ── The legacy --at-* family (Apex Talent v1) ────────────────────────
       styles.v2.css still styles the footer and a few chrome pieces from these.
       They were never remapped, which is why the footer survived every earlier
       palette pass at 3.16:1 with a 1.11:1 separator dot. Remap rather than
       chase the rules: one source of truth, same as the v2 tokens above. */
    --at-navy:#191E36;
    --at-ink:#343D70;       /* link ink — 8.9:1 on --ground */
    --at-muted:#6A6F7E;     /* was #7880b0 at 3.16:1 */
    --at-cream:#F6F6EE;
    --at-warm:#D8D6CA;
    --at-green:#5BF55B;

    accent-color:var(--cobalt);
    color-scheme:light;
}

/* Tabular data lines up: counts, years, problem numbers, pagination. */
.facet-count, .search-count, .cs b, .rcard-meta, .search-pager,
.coll-stats b, .fp-meta { font-variant-numeric:tabular-nums; }

/* Surfaces the browser draws, not us — themed so they belong to the page. */
::selection { background:var(--cobalt-wash); color:var(--cobalt-deep); }
input, textarea { caret-color:var(--cobalt); }

body { background:var(--ground); color:var(--ink); }
/* Sticky footer: keep <footer> pinned to the viewport bottom when a view's
   content is shorter than the screen (large/tall monitors). */
body { min-height:100vh; display:flex; flex-direction:column; }
.site-footer { margin-top:auto; }
/* Every view starts `hidden`, so until the router reveals the first one the
   body is just the header and the rule above pins the footer to the viewport
   bottom, where it painted and then jumped below the fold as the view appeared
   (mobile CLS ~0.12 on /, /search, /guide, /topics). Invisible, not removed:
   it keeps its box, so revealing it moves nothing. main.js boot() sets
   .routed once the first route has rendered. */
html:not(.routed) .site-footer { visibility:hidden; }
/* Same for a text query (/search?q=…): the route resolves at once but the
   results come seconds later from the worker, behind a «Шукаю…» placeholder. */
body:has(.search-skeleton) .site-footer { visibility:hidden; }
:focus-visible { outline:2px solid var(--cobalt); outline-offset:3px; }

/* ── Header ──────────────────────────────────────────────────────────────── */
.site-header { background:var(--card); border-bottom:1px solid var(--line); }
/* The header sticks at the top of the unobscured viewport, but iPhone in-app
   browsers (iOS 26) draw the page under the status bar and their URL pill above
   it: scrolled, the page showed through over a header parked ~120pt down
   (user, 2026-09-30). Safari itself fills that band with the header's colour;
   an in-app web view does not, and iOS never paints a sticky or fixed element
   above the viewport, so the header cannot cover it (a ::before on it was
   clipped, 2026-10-01). Ordinary content does paint there: this strip is plain
   absolute content that main.js keeps just above the header on scroll. It tucks
   40px under the header so a frame of scroll lag never opens a gap. */
.header-cover {
    position:absolute; left:0; right:0; top:-100vh; height:calc(100vh + 40px);
    z-index:39; background:var(--card); pointer-events:none; will-change:transform;
}
@media print { .header-cover { display:none; } }
.site-header-inner { max-width:1120px; margin:0 auto; padding:16px var(--gutter); display:flex; align-items:center; justify-content:space-between; }
/* The lockup follows Apex Talent's: a large tile and the name in Unbounded
   700 caps, 14px, beside it (the parent sets a 38px tile to 14px caps). */
.site-header .site-brand {
    font-family:var(--font-display); font-weight:700; font-size:var(--fs-label); letter-spacing:.02em;
    color:var(--ink); display:inline-flex; align-items:center; gap:12px; text-decoration:none;
    text-transform:uppercase; border:0;
}
/* The mark is drawn, not set: a √ glyph fell to whatever system face the
   device had, since Unbounded has none. One path, shared with favicon.svg. */
.site-brand .brand-mark { width:34px; height:34px; flex:none; display:block; }
.site-brand .brand-mark rect { fill:var(--cobalt); }
.site-brand .brand-mark .grid { fill:none; stroke:#fff; stroke-opacity:.13; stroke-width:2; }
.site-brand .brand-mark .root { fill:none; stroke:var(--card); stroke-width:7.6; stroke-linecap:round; stroke-linejoin:round; }
.site-nav-links { display:flex; gap:6px; align-items:center; }
.site-nav-links a, .site-nav-links button {
    font-size:var(--fs-label); color:var(--ink-2); padding:7px 13px; border-radius:var(--radius-sm); text-decoration:none; border:0; background:none;
}
.site-nav-links a:hover, .site-nav-links button:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
/* The glyph before a nav label is Apex Talent's own (`.apex-nav__sym`): the
   same paths, 18px in a 24 box, 1.5 stroke, 7px from the word. «Про проєкт»
   takes the flag of the parent's «Про Фонд». */
.site-nav-links > a:not(.nav-cta) { display:inline-flex; align-items:center; gap:7px; }
.nav-sym { width:18px; height:18px; flex:none; display:block; }
/* Same drawn-box family as the hero search bar and the "Усі задачі" body
   button (outlined, ink border, hard ink bottom-line, cobalt on hover). */
.site-nav-links .nav-cta {
    background:var(--card); color:var(--ink); font-weight:600;
    border:1.5px solid var(--line);
    transition:border-color .15s, color .15s, background .15s;
}
.site-nav-links .nav-cta:hover {
    background:var(--cobalt-wash); color:var(--cobalt-deep); border-color:var(--cobalt);
}
/* The page you are on (aria-current, main.js markCurrentNav): cobalt text and a
   2px cobalt rule under the link, kept off the 8px corners; «Усі задачі», a
   drawn box already, takes the cobalt border instead. Hover stays the wash, so
   the two never read alike. No weight change: the row would shift. */
.site-nav-links > a[aria-current="page"] { color:var(--cobalt-deep); }
.site-nav-links > a[aria-current="page"]:not(.nav-cta) { position:relative; }
.site-nav-links > a[aria-current="page"]:not(.nav-cta)::after {
    content:""; position:absolute; left:8px; right:8px; bottom:1px; height:2px;
    border-radius:1px; background:var(--cobalt);
}
.site-nav-links > a.nav-cta[aria-current="page"] { border-color:var(--cobalt); }

.view-inner { max-width:1120px; margin:0 auto; padding:0 var(--gutter); }

/* ── Landing hero ────────────────────────────────────────────────────────────
   Clean cream ground — the клітинка grid stays the cards' alone. Left-aligned
   to share the left margin with the stats masthead and the card grid below. */
.landing-hero {
    position:relative; padding:var(--space-2xl) 0 var(--space-lg); text-align:left; max-width:none;
    background:var(--ground);
}
.hero-inner { max-width:1120px; margin:0 auto; padding:0 var(--gutter); }
/* Scoped to .landing-hero to beat styles.v2.css `.landing-hero .hero-title`. */
.landing-hero .hero-title {
    font-family:var(--font-display); font-weight:600; font-size:var(--fs-display);
    line-height:1.04; letter-spacing:-.028em; margin:0 0 var(--space-sm); color:var(--ink);
    text-wrap:balance; max-width:17ch;
}
.landing-hero .hero-title em { font-style:italic; color:var(--cobalt); }
.landing-hero .hero-sub {
    font-family:var(--font-sans); font-weight:400; font-size:var(--fs-lead);
    line-height:1.45; letter-spacing:-.01em; margin:0 0 var(--space-lg); color:var(--ink-2);
    text-wrap:balance;
}
/* From 760px the headline is ONE line across the content frame and the search
   field runs the frame's full width under it, so the hero, the archive panel
   and «Задача дня» share both edges. Every arrangement with the headline in a
   left column left the right half of the first screen empty or filled it with
   something that did not belong there (user, 2026-10-01). The whole headline
   measures 20× its font size; 4.4vw keeps that inside the frame at any width,
   and 50px is where it meets the 1056px frame. */
@media (min-width:760px){
    .landing-hero .hero-title { font-size:clamp(34px, 4.4vw, 50px); max-width:none; white-space:nowrap; }
}

.hero-search {
    display:flex; align-items:center; gap:12px; margin:0;
    background:var(--card); border:1.5px solid var(--line); border-radius:var(--radius-md); padding:6px 6px 6px 18px;
    transition:border-color .15s, box-shadow .15s;
}
.hero-search:hover { border-color:var(--color-gray-300); }
.hero-search:focus-within { border-color:var(--cobalt); box-shadow:0 0 0 3px var(--cobalt-wash); }
.hero-search svg { flex:none; color:var(--ink-2); }
.hero-search input { flex:1; border:0; background:transparent; font-family:var(--font-sans); font-size:var(--fs-read); color:var(--ink); padding:12px 0; outline:none; min-width:0; }
.hero-search-go { font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body); color:var(--card); background:var(--cobalt); border:0; border-radius:var(--radius-sm); padding:12px 22px; cursor:pointer; white-space:nowrap; transition:background .15s; }
.hero-search-go:hover { background:var(--cobalt-deep); }
@media (max-width:560px){
    .landing-hero { padding:48px 0 24px; }
    .hero-search-go { padding:12px 16px; }
}
/* ── The archive panel ───────────────────────────────────────────────────────
   A first visitor could not tell what the archive holds (first user feedback,
   2026-10-01). The answer is one object under the search: a panel whose head
   line is the archive's size and whose body is its contents — four предмети,
   one column each, with their largest розділи and counts, every row a link to
   /search?topic=<slug>. The numbers belong to the panel, not to the search
   field: they are the total the columns break down (user). Set like the «Про
   проєкт» contents (.about-toc): a name, a dotted leader, a count. */
.archive {
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-lg);
    padding:var(--space-md); margin-bottom:var(--space-2xl);
}
.archive-head {
    display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:var(--space-2xs) var(--space-md);
    padding-bottom:var(--space-sm); margin-bottom:var(--space-md); border-bottom:1px solid var(--line);
}
.archive-all {
    display:inline-flex; align-items:center; gap:6px; white-space:nowrap; text-decoration:none;
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label); color:var(--cobalt-deep);
}
.archive-all svg { transition:transform var(--dur-fast) var(--ease); }
.archive-all:hover { text-decoration:underline; text-underline-offset:3px; }
@media (hover:hover) and (prefers-reduced-motion:no-preference){ .archive-all:hover svg { transform:translateX(3px); } }
.archive-toc { display:grid; grid-template-columns:minmax(0,1fr); gap:var(--space-ms); align-items:start; }
.toc-subject h2 {
    margin:0 0 var(--space-3xs); border-bottom:1px solid var(--ink);
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body); line-height:1.3; letter-spacing:0;
}
.toc-subject h2 a { display:flex; align-items:baseline; justify-content:space-between; gap:var(--space-xs); padding-bottom:var(--space-2xs); color:var(--ink); text-decoration:none; }
.toc-subject h2 .toc-n { font-weight:500; font-size:var(--fs-label); }
.toc-subject ul { list-style:none; margin:0; padding:0; }
.toc-subject li a {
    display:flex; align-items:baseline; gap:var(--space-2xs); padding:5px 0;
    font-family:var(--font-sans); font-size:var(--fs-label); line-height:1.4; color:var(--ink); text-decoration:none;
}
.toc-name { flex:0 1 auto; min-width:0; }
.toc-lead { flex:1; min-width:12px; border-bottom:1px dotted var(--line); transform:translateY(-3px); }
.toc-n { flex:none; color:var(--ink-2); font-variant-numeric:tabular-nums; white-space:nowrap; }
.toc-subject a:hover .toc-name { color:var(--cobalt-deep); text-decoration:underline; text-underline-offset:3px; }
.toc-subject a:hover .toc-lead { border-bottom-color:var(--cobalt); }
.toc-more {
    margin-top:var(--space-3xs); padding:4px 0; border:0; background:none; cursor:pointer;
    font-family:var(--font-sans); font-weight:500; font-size:var(--fs-label); color:var(--cobalt-deep);
}
.toc-more:hover { text-decoration:underline; text-underline-offset:3px; }
@media (pointer:coarse){
    .toc-subject li a { min-height:44px; align-items:center; padding:0; }
    .toc-subject h2 a { min-height:44px; align-items:center; padding-bottom:0; }
    .toc-lead { transform:none; }
    .toc-more { min-height:44px; }
}
@media (pointer:coarse){ .archive-all { min-height:44px; } }
@media (min-width:561px){
    .archive { padding:var(--space-md) var(--space-lg) var(--space-lg); }
    .archive-toc { grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--space-lg) var(--space-ml); }
}
@media (min-width:1000px){
    .archive-toc { grid-template-columns:repeat(4,minmax(0,1fr)); gap:var(--space-lg); }
}

/* ── Archive stats line: numbers in the display face but small, so they read
   as a confident credit line — not a dashboard. ── */
.coll-stats { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--space-3xs) var(--space-sm); margin:0; font-family:var(--font-sans); font-size:var(--fs-label); color:var(--ink-2); }
.coll-stats .cs { display:inline-flex; align-items:baseline; gap:7px; }
.coll-stats b { font-family:var(--font-display); font-weight:700; font-size:var(--fs-body); letter-spacing:-.01em; color:var(--ink); font-variant-numeric:tabular-nums; }
.coll-stats .cs-sep { font-style:normal; color:var(--ink-muted); }
/* The section eyebrow was DELETED 2026-09-20. An uppercase kicker floating
   above content is a label standing in for a heading; the masthead's stats line
   already names what this section is. (It was also the smallest functional text
   on the site at 10.24px.) */
/* "Усі задачі" — the outlined CTA of the guide, about and 404 pages. Echoes the
   hero search bar's drawn-box look; secondary to search, so outlined, not filled. */
.btn-all {
    display:inline-flex; align-items:center; gap:9px;
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body); color:var(--ink);
    background:var(--card); border:1px solid var(--ink); border-radius:var(--radius-sm);
    padding:14px 28px; min-height:44px; text-decoration:none;
    transition:border-color .15s, color .15s, background .15s;
}
.btn-all svg { transition:transform .15s; }
.btn-all:hover { border-color:var(--cobalt); color:var(--cobalt-deep); background:var(--cobalt-wash); }
.btn-all:hover svg { transform:translateX(3px); }
@media (max-width:560px){ .coll-stats .cs-sep { display:none; } .coll-stats { gap:4px 16px; } }

/* ════════ FEATURED PROBLEMS — "a page from the notebook" ══════════════════
   The one bold move. Each card is an entry on зошит paper: faint клітинка grid
   + the red margin rule down the left that every Ukrainian notebook has. The
   statement is written in the "writing area" to the right of the rule; KaTeX
   pre-renders its math at build time (tools/export/render_featured.mjs). */
.featured { padding-top:0; }
.fp-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:24px; }
.fp-card {
    position:relative; display:flex; flex-direction:column; min-height:188px;
    border:1px solid var(--line); border-radius:var(--radius-md); background:var(--card);
    padding:var(--space-md) var(--space-md) var(--space-md) 52px; text-decoration:none; overflow:hidden;
    /* Whisper-faint клітинка — just enough paper texture to read as a notebook
       without the grid lines competing with the statement text for contrast. */
    background-image:
        linear-gradient(rgba(44,70,216,.045) 1px,transparent 1px),
        linear-gradient(90deg,rgba(44,70,216,.045) 1px,transparent 1px);
    background-size:23px 23px; background-position:-1px -1px;
    transition:border-color var(--dur-fast) var(--ease);
}
/* The red margin rule: a single pencil line ~38px from the card's left edge,
   the way a зошит у клітинку is ruled. Statement text starts to its right. */
.fp-card::before {
    content:""; position:absolute; top:0; bottom:0; left:38px; width:1.5px;
    background:var(--red); opacity:.55;
}
.fp-card:hover { border-color:var(--cobalt); }
.fp-card:hover::before { opacity:.8; }
/* The head: subject left, public ID in the top-right corner, on one baseline.
   The subject is typeset with the other section labels, far below. The ID is
   the problem page's locator voice (.problem-pid) at the card's caption size:
   500 in ink-muted against the subject's 600 ink-2, so the two uppercase
   strings read as label and reference, not two labels.
   Like the footer, the CARD's width picks the arrangement, so a column of
   cards never mixes the two: the widest pair («КОМБІНАТОРИКА» +
   «VUOM-22-23-083») is ~271px, so below 300px of writing area (every phone)
   the ID always stacks under the subject, the way the code sits under the
   title on the problem page. Stacked is the default, for browsers without
   container queries. */
.fp-head {
    display:flex; flex-direction:column; align-items:flex-start;
    gap:4px; margin-bottom:12px;
}
@container fp (min-width:300px) {
    .fp-head { flex-direction:row; align-items:baseline; justify-content:space-between; gap:16px; }
}
.fp-pid {
    font-family:var(--font-sans); font-weight:500; font-size:var(--fs-micro); line-height:1.3;
    letter-spacing:.03em; font-variant-numeric:tabular-nums; color:var(--ink-muted); white-space:nowrap;
    transition:color var(--dur-fast) var(--ease);
}
.fp-card:hover .fp-pid { color:var(--ink-2); }
.fp-statement {
    /* Set in the math's own face, like the problem page (--font-math). */
    flex:1; font-family:var(--font-math); font-size:var(--fs-math); line-height:1.5; color:var(--ink);
    /* The statement bakes raw "$…$" markdown (crawlable); KaTeX typesets it on
       load. Keep it hidden until typeset, then fade in at full ink — so there is
       no grey "loading" tint and no raw-delimiter flash, just black text appearing. */
    opacity:0; transition:opacity .25s ease-out;
}
.fp-card.is-typeset .fp-statement { opacity:1; }

/* ── Featured skeleton loader ────────────────────────────────────────────────
   Shown while data_v2/featured.json fetches. Reuses the .fp-card shell (border,
   red margin rule, клітинка paper) and fills the writing area with shimmer bars
   — a faint cobalt sweep that reads like blue-pen ink writing onto the page. */
/* Sized like a typical real card (measured 2026-09-30: 233-324 px at two
   columns, 258-408 px at one), so the swap to the real draw barely moves what
   sits below the grid. */
.fp-card.fp-skel { pointer-events:none; min-height:263px; }
.fp-skel .sk {
    display:block; border-radius:var(--radius-xs);
    background:linear-gradient(90deg, var(--cobalt-wash) 0%, #FBFBFE 45%, var(--cobalt-wash) 90%);
    background-size:220% 100%; animation:fp-shimmer 1.25s linear infinite;
}
.fp-skel .sk-domain { width:84px; height:9px; margin-bottom:16px; }
.fp-skel .sk-body { flex:1; display:flex; flex-direction:column; gap:10px; }
.fp-skel .sk-line { height:13px; }
.fp-skel .sk-line.sk-short { width:58%; }
.fp-skel .sk-meta { width:120px; height:10px; margin-top:16px; }
@keyframes fp-shimmer { from { background-position:120% 0; } to { background-position:-120% 0; } }
@media (prefers-reduced-motion:reduce){ .fp-skel .sk { animation:none; } }
/* The card's footer: the source on the left, «Розв'язати →» on the right —
   no year and no № (user, 2026-09-28: the public ID in the card's head is the
   problem's reference, and it carries the season or year). It used to be one
   wrapping flex row with the full catalog name (up to 67 characters) and the
   cue as a CSS ::after, so at half width the cue fell onto a line of its own,
   right-aligned under nothing. The source was also bold cobalt, which on this
   site means «link» — but the card is one link, and its action is the cue,
   which sat in muted grey. Cobalt now goes to the action.

   Two arrangements, chosen by the CARD's width, not the viewport's (two
   columns at 900px make a narrower card than one column at 640px):
   - narrow (default): the source takes the first line, the cue the second,
     both on the writing edge, so the pair reads as one caption block and the
     cue is never a lone item pushed to the far right under nothing.
   - ≥330px of writing area: one line, source left, cue right. The longest
     short name («Відбір на міжнародні олімпіади», 206px) plus the gap and the
     cue measures 319px. With the year it needed 420.
   The source is the only item that gives way: a name COMP_SHORT lacks
   ellipsizes, it never wraps. The narrow layout is the default so a browser
   without container queries still gets a footer that fits. */
.fp-card { container:fp / inline-size; }
.fp-meta {
    margin-top:16px; font-family:var(--font-sans); font-size:var(--fs-micro); line-height:1.4; color:var(--ink-muted);
    display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 8px; min-width:0;
}
.fp-comp {
    flex:1 0 100%; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    font-weight:600; color:var(--ink-2);
}
.fp-go {
    flex:none; display:inline-flex; align-items:center; gap:5px;
    font-weight:600; color:var(--cobalt-deep); white-space:nowrap;
    transition:color var(--dur-fast) var(--ease);
}
.fp-go svg { flex:none; transition:transform var(--dur-fast) var(--ease); }
.fp-card:hover .fp-go { color:var(--cobalt); }
@media (hover:hover) and (prefers-reduced-motion:no-preference) {
    .fp-card:hover .fp-go svg { transform:translateX(3px); }
}
@container fp (min-width:330px) {
    .fp-meta { flex-wrap:nowrap; }
    .fp-comp { flex:0 1 auto; }
    .fp-go { margin-left:auto; padding-left:16px; }
}
/* «Задачі дня»: the page's light ending, below everything (user, 2026-10-01).
   A heading outside, then TWO notebook cards in the .fp-grid above — two, so
   each keeps the half-frame width the card was drawn for; one card across the
   whole frame set the statement in 110-character lines. */
.day-head {
    display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--space-3xs) var(--space-sm);
    margin:0 0 var(--space-sm);
}
.day-head h2 { margin:0; font-family:var(--font-display); font-weight:600; font-size:var(--fs-lead); line-height:1.2; letter-spacing:-.01em; color:var(--ink); }
.day-date { font-family:var(--font-sans); font-size:var(--fs-label); color:var(--ink-2); }
.featured { padding-bottom:88px; }

@media (max-width:680px){
    .fp-grid { grid-template-columns:minmax(0,1fr); }
    .fp-card { padding-left:38px; min-height:0; }
    .fp-card::before { left:26px; }
}

/* Section labels. These are the page's structure — «Умова задачі», «Пов'язані
   теми», «Джерело» — and they were the two things a structural heading must
   never be: the least legible text on the page, and the faintest.

   Unbounded is a geometric display face drawn for 40px and up; at 13px, set
   in CAPS, tracked to .14em, in Ukrainian, its near-monoline strokes and wide
   apertures stop resolving into words. It also could not fit: «УМОВА ЗАДАЧІ»
   wrapped to two lines on a 375px phone, beside a secondary control, which is
   what a broken heading looks like. And Cyrillic caps need LESS tracking than
   Latin, not more — the forms are already wide and uniform, so .14em pulls
   them apart into loose letters instead of a word.

   Inter 600 at .06em sets them cleanly at 13px, and --ink-2 (6.9:1) puts them
   a step above --ink-muted, which is the floor for text and the wrong home
   for a heading. Unbounded now means brand and real headline, nothing else —
   which is what makes the title read as a title. */
.problem-card-detail .problem-content-header h3,
.problem-card-detail .topics-header,
.problem-card-detail .problem-more-h,
.problem-card-detail .citation-header-label,
.problem-card-detail .solution-detail-title,
.facet-group > h4, .tp-head > h4,
.fp-domain {
    font-family:var(--font-sans); font-weight:600;
    font-size:var(--fs-micro); line-height:1.3; letter-spacing:.06em;
    text-transform:uppercase; color:var(--ink-2);
}

/* ── Display mathematics wider than its column ────────────────────────────
   This was silently destroying content. A display equation renders at its
   natural width; every surface that carries one — the problem panel, the
   featured card, the result card — clips with `overflow:hidden` to hold its
   radius and its клітинка grid. Nothing in between let the equation scroll,
   so the overflow was simply cut off and unreachable.

   Measured on ukr_2010_f9f585 at 375px: the statement's one equation is
   1149px of содержание inside a 261px column. Seventy-eight per cent of the
   expression a student is asked to evaluate was invisible, on the device
   PRODUCT.md names as the primary one. On desktop the same equation ended
   mid-fraction at the panel's right border.

   The cause is a migration leftover: the only overflow rule in the sheet
   targets `mjx-container[display="true"]` — MathJax, removed 2026-07-01. The
   KaTeX equivalent was never written.

   Display math now scrolls inside its own box, with the classic two-layer
   scroll shadow (a --card-coloured cover under an ink gradient) so the
   affordance appears only on the side that actually has more to show. The
   equation clears the клітинка grid behind it, which is how mathematics is
   written on ruled paper anyway. */
.katex-display {
    overflow-x:auto; overflow-y:hidden;
    /* Radicals, large operators and tall fractions sit slightly outside the
       line box; a bare scroll container would shave them. */
    padding-block:4px;
    /* KaTeX centres display math inside a full-width block, which is what the
       printed books do and what stays. Verified that it is safe here: when the
       equation is wider than the column, centring has nothing to centre, so
       the expression starts flush with the container's left edge (measured
       leftDelta 0, scrollLeft 0) and every glyph is reached by scrolling
       right. Short equations still sit centred. */
    scrollbar-width:thin; scrollbar-color:var(--color-gray-300) transparent;
    overscroll-behavior-x:contain;
    background:
        linear-gradient(to right, var(--card) 30%, rgba(251,251,242,0)) left center,
        linear-gradient(to left,  var(--card) 30%, rgba(251,251,242,0)) right center,
        radial-gradient(farthest-side at 0 50%, rgba(25,30,54,.16), rgba(25,30,54,0)) left center,
        radial-gradient(farthest-side at 100% 50%, rgba(25,30,54,.16), rgba(25,30,54,0)) right center;
    background-repeat:no-repeat;
    background-size:36px 100%, 36px 100%, 14px 100%, 14px 100%;
    background-attachment:local, local, scroll, scroll;
}
.katex-display::-webkit-scrollbar { height:8px; }
.katex-display::-webkit-scrollbar-track { background:transparent; }
.katex-display::-webkit-scrollbar-thumb {
    background:var(--color-gray-300); border-radius:var(--radius-full);
    border:2px solid var(--card); background-clip:padding-box;
}
/* An inline formula can be wider than a phone column too — a long chain of
   equalities, say. Let it wrap at its own relation symbols rather than push
   the page sideways. */
.katex { max-width:100%; }

/* One typeface for a problem's text. KaTeX sets math at 1.21em because
   Computer Modern looks small beside an ordinary text face; beside CMU Serif —
   Computer Modern itself — that correction would make every formula 21% larger
   than the words it sits in. On surfaces set in --font-math the math is the
   text's own size. */
.fp-statement .katex,
.rcard .rbody .katex,
.problem-card-detail .problem-content .katex,
.problem-card-detail .solution-content-wrap .katex,
.problem-card-detail .solution-inline .katex,
.problem-card-detail .answer-reveal-content .katex,
.problem-card-detail .editorial-note-body .katex { font-size:1em; -webkit-text-stroke-width:0; }
/* Same design, two digitisations. KaTeX's fonts (from the AMS/BaKoMa
   outlines) carry 31% more ink than CMU Serif's (autotraced from Metafont) at
   identical advance widths — measured over digits, lowercase and operators,
   Main vs Roman ×1.31 and Math vs Italic ×1.33. Left alone, every number in a
   sentence read as bold: «стороною **36** покривають». A .02em outline
   brings the words to the formulas' colour (the formulas are the reference and
   are not touched, above); .025em overshoots. Width only — the stroke colour
   is currentcolor by default, so a red <mark> strokes red. */
.fp-statement,
.rcard .rbody,
.problem-card-detail .problem-content,
.problem-card-detail .solution-content-wrap,
.problem-card-detail .solution-inline,
.problem-card-detail .answer-reveal-content,
.problem-card-detail .editorial-note-body { -webkit-text-stroke-width:.02em; }
/* KaTeX's fonts have no Cyrillic. It tags a Cyrillic run inside a formula
   (`\text{км}` — 1,485 published formulas) `.cyrillic_fallback` and ships no
   rule for the class, so the run fell through `.katex`'s stack to Times New
   Roman: a third face inside the formula. Give it the text face. */
.katex .cyrillic_fallback { font-family:var(--font-math); }
/* A table inside a solution sits one step under the text, as in the books.
   Its base rule is 0.95rem — 15.2px, pinned to the root, not the text — which
   in Computer Modern's small x-height is a size too small to read a grid of
   residues by. */
.problem-card-detail .markdown-table { font-size:var(--fs-read); }

/* ── Generic tag/chip (neutral; no domain colour) ────────────────────────── */
.atag { display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-micro); padding:var(--space-3xs) var(--space-xs); border-radius:var(--radius-full); border:1px solid var(--line); color:var(--ink-2); background:var(--ground); }
/* A one-country competition's flag, ahead of its name (js/util/comp-flag.js).
   Apple / Noto first so those platforms draw their own; 'Apex Flags'
   (assests/fonts/fonts.css) covers Windows, which has none. */
.cflag {
    font-family:'Apple Color Emoji', 'Noto Color Emoji', 'Apex Flags', sans-serif;
    font-weight:400; margin-right:.35em;
}
.atag .cflag, .meta-badge .cflag { margin-right:0; }
/* In a result card the same tag is a source line, not a chip: it repeats
   verbatim down the whole column, it carries no choice, and at 375px it wrapped
   to two lines inside a full-radius pill — which is what a pill cannot do. Drop
   the box, keep the words, let the statement lead. */
.rcard-top .atag {
    padding:0; border:0; background:none; color:var(--ink-muted);
    border-radius:0; line-height:1.35;
}

/* ── Dismiss control (✕) ─────────────────────────────────────────────────
   ONE ✕ for every "make this go away": the modal close, the search-field
   clear, the topic-finder clear, the «Усі теми» dialog close. A dismissal is
   not an action with a name — a word ("Закрити") reads as a button competing
   with the panel's own content, and a translated word competes differently in
   every language. The glyph is the system's, drawn from the same 24×24 /
   2px-stroke icon set as js/views/topic-picker.js::ICON.close, so the modal
   and the dialog dismiss with the identical mark.

   Borderless on purpose — this is the one control exempt from the "1px ink
   border on everything actionable" rule, because a boxed ✕ in a corner reads
   as a second card. The 44px target rule is met by the padding of the surface
   it sits in, not by the glyph box. Hover is the quiet wash, never a lift.

   Size roles: default 34px (modal / dialog corner), and the inset variants
   below keep their own geometry (26px in the search box, 20px inside the
   topic-find field). The filter pill's ✕ is NOT a member: it is a chip-
   internal target with a cobalt-wash bed (see .search-pill .x). */
.icon-x, .td-x, .tp-find-x, .search-box .clear-x {
    display:grid; place-items:center;
    width:34px; height:34px; border-radius:var(--radius-sm); padding:0;
    border:0; background:none;
    color:var(--ink-2); cursor:pointer;
    transition:background .14s, color .14s;
}
.icon-x:hover, .td-x:hover, .tp-find-x:hover, .search-box .clear-x:hover {
    background:var(--ground); color:var(--ink);
}

/* ════════ SEARCH PAGE ════════════════════════════════════════════════════ */
#searchView { min-height:60vh; }
.search-top { position:sticky; top:var(--header-h); z-index:5; padding:16px var(--gutter); border-bottom:1px solid var(--line); background:var(--card); }
.search-top-inner { max-width:var(--search-max); margin:0 auto; display:flex; gap:var(--space-sm); align-items:center; }
.search-box {
    flex:1; display:flex; align-items:center; gap:10px; min-width:0;
    background:var(--card); border:1.5px solid var(--ink); border-radius:var(--radius-md); padding:5px 6px 5px 16px;
    transition:border-color .15s, box-shadow .15s;
}
.search-box:focus-within { border-color:var(--cobalt); box-shadow:0 0 0 3px var(--cobalt-wash); }
.search-box svg { flex:none; color:var(--ink-2); }
.search-box input { flex:1; border:0; background:transparent; font-size:var(--fs-body); color:var(--ink); padding:11px 0; outline:none; min-width:0; }
.search-box .clear-x { flex:none; width:26px; height:26px; border-radius:var(--radius-sm); color:var(--ink-muted); }
.search-count { font-size:var(--fs-micro); color:var(--ink-2); white-space:nowrap; }
.search-count b { font-family:var(--font-display); font-weight:700; color:var(--ink); }
.search-status { font-size:var(--fs-micro); color:var(--ink-muted); white-space:nowrap; }

/* 288px, not 256. The rail's whole design rests on names WRAPPING rather than
   ellipsizing — the name is the only thing a reader can choose with — and at
   256px the name column came out 119px after the checkbox, the count and the
   chevron had taken their share. Seven of the 36 розділи then broke over three
   lines and one over four, which is where a row stops reading as a row. 288
   gives the name ~151px and costs the results column 32px of ~860. */
.search-body { max-width:var(--search-max); margin:0 auto; display:grid; grid-template-columns:288px minmax(0,1fr); gap:0; }
/* The rail is ~1,870px tall at full height against a ~742px laptop viewport.
   Sticky with overflow:visible meant «Змагання» and «Рік» sat below the fold at
   EVERY scroll offset — measured at six positions, «Рік» never became visible
   once. It scrolls inside itself now; 93px is the sticky search bar above it. */
.search-facets {
    border-right:1px solid var(--line); padding:22px 16px 22px 20px; align-self:start;
    position:sticky; top:calc(var(--header-h) + 93px); min-width:0;
    max-height:calc(100vh - var(--header-h) - 93px);
    overflow-y:auto; overscroll-behavior:contain;
    scrollbar-width:thin; scrollbar-color:var(--line) transparent;
}
.search-facets::-webkit-scrollbar { width:10px; }
.search-facets::-webkit-scrollbar-track { background:transparent; }
.search-facets::-webkit-scrollbar-thumb { background:var(--line); border-radius:var(--radius-full); border:3px solid var(--ground); background-clip:padding-box; }
.search-facets:hover::-webkit-scrollbar-thumb { background:var(--ink-3); background-clip:padding-box; }
.facet-group { margin-bottom:var(--space-ms); }
.facet-group > h4, .tp-head > h4 { margin:0 0 12px; }
.facet-opt {
    display:flex; align-items:center; gap:10px; min-height:44px; padding:4px 8px;
    margin:0 -8px; border-radius:var(--radius-sm);
    font-size:var(--fs-label); color:var(--ink); cursor:pointer; user-select:none;
    transition:background var(--dur-fast) var(--ease);
}
.facet-opt:hover { background:var(--cobalt-wash); }
/* One checkbox for every facet, topics included: a REAL <input type=checkbox>
   with appearance:none, so keyboard, focus and semantics are the platform's and
   only the paint is ours. The tick is a drawn stroke (mask), not a ✓ glyph. */
.facet-opt .box, .tp-box {
    appearance:none; -webkit-appearance:none; margin:0;
    width:15px; height:15px; flex:none; border:1.5px solid var(--line); border-radius:var(--radius-xs);
    background:var(--card); cursor:pointer; position:relative;
    transition:background .13s, border-color .13s;
}
.tp-box { margin-top:3px; }
.facet-opt:hover .box, .tp-pick:hover .tp-box { border-color:var(--ink-muted); }
.facet-opt .box:checked, .tp-box:checked { background:var(--cobalt); border-color:var(--cobalt); }
.facet-opt .box:checked::after, .tp-box:checked::after {
    content:""; position:absolute; inset:0; background:var(--card);
    -webkit-mask:var(--tick) center/9px 9px no-repeat; mask:var(--tick) center/9px 9px no-repeat;
}
.facet-opt .box:focus-visible, .tp-box:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }
.facet-opt.on { font-weight:600; }
.facet-opt .ct { margin-left:auto; font-size:var(--fs-micro); color:var(--ink-muted); font-variant-numeric:tabular-nums; }
.facet-more { font-size:var(--fs-micro); color:var(--cobalt); background:none; border:0; padding:6px 0; cursor:pointer; }
.facet-more:hover { color:var(--cobalt-deep); text-decoration:underline; }
/* A sub-heading inside a facet group («Олімпіадні етапи», «Українські»), with
   the section's own total. Flush with the row content: rows cancel their 8px
   padding with a negative margin. */
.facet-sub { display:flex; align-items:baseline; gap:8px; margin:var(--space-xs) 0 var(--space-3xs);
    font-family:var(--font-sans); font-size:var(--fs-micro); line-height:1.3; letter-spacing:.04em;
    text-transform:uppercase; color:var(--ink-muted); }
.facet-group > h4 + .facet-sub, .facet-group > h4 + .facet-sec > .facet-sub { margin-top:0; }
.facet-sub .ct { margin-left:auto; text-transform:none; letter-spacing:0; font-variant-numeric:tabular-nums; }
/* A section's «Ще N →» / «Згорнути» sits directly under its own rows. */
.facet-sec .facet-more { display:block; }

/* ── «Тематика»: rail navigator + «Усі теми» dialog ──────────────────────────
   The гурток taxonomy is 250 nodes over exactly three rungs (4 предмети → 36
   розділів → 210 тем), and it will not fit a 256px rail as an indented tree —
   see js/views/topic-picker.js for the full reasoning. The rail now shows ONE
   rung at a time, which buys back the width the indents were eating: names WRAP
   here, they never ellipsize, because the name is the only thing a reader can
   choose with. Fan-out is at most 11, and the list has no scroller of its
   own: the rail scrolls every facet as one (see .tp-list). */
.tp-head { display:flex; align-items:baseline; justify-content:space-between; gap:var(--space-2xs); margin-bottom:var(--space-xs); }
.tp-head > h4 { margin:0; }

.tp-all {
    display:inline-flex; align-items:center; gap:5px; flex:none;
    font-size:var(--fs-micro); font-weight:600; color:var(--cobalt); background:none;
    border:0; padding:2px 0; cursor:pointer; transition:color .15s;
}
.tp-all:hover { color:var(--cobalt-deep); }
.tp-all svg { opacity:.85; }

/* Search field — same drawn-box idiom as the page's own search input. */
.tp-find {
    display:flex; align-items:center; gap:7px; padding:0 8px;
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-sm);
    transition:border-color .15s, box-shadow .15s;
}
.tp-find:focus-within { border-color:var(--cobalt); box-shadow:0 0 0 3px var(--cobalt-wash); }
.tp-find > svg { flex:none; color:var(--ink-muted); }
.tp-find-input {
    flex:1; min-width:0; border:0; background:none; outline:none;
    font:inherit; font-size:var(--fs-label); color:var(--ink);
    padding:var(--space-xs) 0; min-height:42px;
}
.tp-find-input::placeholder { color:var(--ink-muted); }
.tp-find-x { flex:none; width:20px; height:20px; border-radius:var(--radius-xs); color:var(--ink-muted); }

/* Year lens, mirrored from the dialog so it is never an invisible filter. */
.tp-lens {
    display:inline-flex; align-items:center; gap:6px; margin-top:8px;
    font-size:var(--fs-micro); font-weight:600; padding:3px 7px 3px 9px; border-radius:var(--radius-full);
    background:var(--cobalt-wash); color:var(--cobalt-deep); border:0; cursor:pointer;
}
.tp-lens:hover { background:#DFE3F8; }
.tp-note { margin-top:var(--space-xs); font-size:var(--fs-micro); letter-spacing:.02em; color:var(--ink-muted); }

/* Where am I / how do I get back. */
.tp-crumb { margin-top:var(--space-xs); }
.tp-crumb-trail {
    font-size:var(--fs-micro); letter-spacing:.03em; color:var(--ink-muted);
    padding-left:29px; margin-bottom:1px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* Centred, not flex-start: the row beside the back arrow is a `.tp-row` and now
   carries the 44px floor, while `.tp-back` is 30px. Top-aligned, the arrow sat
   7px above the middle of the box it belongs to — the same mismatch the chevron
   had, from the same cause. The 1px nudge went with it; it hand-corrected the
   old top-aligned layout and now double-counts. */
.tp-crumb-row { display:flex; align-items:center; gap:2px; }
.tp-back {
    flex:none; width:27px; height:30px; display:grid; place-items:center;
    border:0; background:none; border-radius:var(--radius-sm); color:var(--ink-2); cursor:pointer;
    transition:background .15s, color .15s;
}
.tp-back:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.tp-crumb-row .tp-row { flex:1; min-width:0; }
/* «You are here» is a FILL, not an outline. The background was var(--ground) —
   the same colour as the rail behind it — so the only thing drawing the row was
   a 1px inset border, and once the 44px floor gave it height that read as an
   empty rectangle ruled around the name rather than as a plate the name sits
   on. Paper white against the rail's cream states it with no line at all, and
   the vertical padding goes: the row's own floor already sets its height, so
   the extra 5px only made the frame look oversized. */
.tp-here {
    background:var(--card); border-radius:var(--radius-sm); padding:0 7px;
}
.tp-here .tp-name { font-weight:700; }

/* The rail is the ONE scroller; the list is not a second one. It was capped at
   min(52vh, 460px) with overflow:auto + overscroll-behavior:contain, and both
   halves stopped the rail under the pointer. At a 760px viewport every предмет
   overflowed the cap (Алгебра 491px in 395px), so a wheel over the list moved
   the list; and `contain` ends the scroll chain even where there is nothing to
   scroll — the four предмети fill their box exactly (scroll range 0), yet every
   wheel over them died there. Uncapped, a panel is at most 11 rows and search
   stops at RAIL_HITS, so the list just grows and the rail scrolls. */
.tp-list { margin-top:8px; min-width:0; }

/* One row = [filter checkbox] [name (+ path) ] [count] [open ›].
   Two affordances, never ambiguous: the box filters, the row opens. A leaf has
   nothing to open so its row filters instead — and because the taxonomy is
   strictly three rungs, a panel is all branches or all leaves, never both. */
.tp-row {
    display:flex; align-items:flex-start; gap:8px; min-width:0;
    border-radius:var(--radius-sm); margin:1px 0; padding:1px 2px 1px 0;
    transition:background .13s;
}
.tp-row.is-branch:hover { background:var(--ground); }
.tp-text { flex:1; min-width:0; display:flex; flex-direction:column; gap:1px; padding:4px 0; }
.tp-pick { display:flex; align-items:flex-start; gap:8px; min-width:0; cursor:pointer; }
.tp-name {
    font-size:var(--fs-label); line-height:1.32; color:var(--ink);
    min-width:0; overflow-wrap:anywhere;
}
.tp-row.on .tp-name { font-weight:650; color:var(--cobalt-deep); }
.tp-name mark { background:var(--red-wash); color:var(--red); border-radius:var(--radius-xs); padding:0 1px; font-weight:600; }
.tp-trail {
    font-size:var(--fs-micro); line-height:1.3; color:var(--ink-muted); padding-left:23px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.tp-years {
    font-size:var(--fs-micro); letter-spacing:.02em; color:var(--cobalt); padding-left:23px;
    font-variant-numeric:tabular-nums;
}
.tp-ct {
    flex:none; padding-top:5px; font-size:var(--fs-micro); color:var(--ink-muted);
    font-variant-numeric:tabular-nums;
}
.tp-row.on .tp-ct { color:var(--cobalt); }
/* Selections live one or two rungs below what this panel shows — say so here,
   or the filter is invisible from where the reader is standing. */
/* A worded pill («1 обрана»), not a filled circle with a bare number: the
   circle read as a notification and sat beside the problem count as a second
   unexplained figure, with only a hover title to tell them apart. It is inline
   in the name, so in the 256px rail it follows a short name on the same line and
   drops under a long one instead of squeezing the name into mid-word breaks. */
.tp-sel {
    display:inline-block; vertical-align:1px; white-space:nowrap;
    padding:2px 7px; border-radius:var(--radius-full);
    font-size:var(--fs-micro); line-height:1; font-weight:400; font-variant-numeric:tabular-nums;
    background:var(--cobalt-wash); color:var(--cobalt-deep);
}
.tp-open {
    flex:none; width:34px; align-self:stretch; min-height:44px;
    display:grid; place-items:center; border:0; background:none;
    border-radius:var(--radius-sm); color:var(--ink-muted); cursor:pointer;
    transition:background .13s, color .13s, transform .13s;
}
.tp-open:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); transform:translateX(1px); }
.tp-open-slot { flex:none; width:34px; }

.tp-more {
    display:flex; align-items:center; gap:4px; width:100%; margin-top:6px;
    font-size:var(--fs-micro); font-weight:600; color:var(--cobalt); background:none;
    border:0; padding:6px 0; cursor:pointer; text-align:left;
}
.tp-more:hover { color:var(--cobalt-deep); }
.tp-empty { padding:10px 0 2px; }
.tp-empty p { margin:0 0 8px; font-size:var(--fs-micro); line-height:1.45; color:var(--ink-2); }
.tp-empty-act {
    font-size:var(--fs-micro); font-weight:600; color:var(--cobalt); background:none;
    border:0; padding:0; cursor:pointer; text-decoration:underline;
    text-underline-offset:3px; text-decoration-thickness:1px;
}
.tp-empty-act:hover { color:var(--cobalt-deep); }

/* Drill transition: the panel arrives from the side you travelled. State, not
   decoration — it says "deeper" or "back" in the 160ms you already spend. */
@keyframes tp-in  { from { opacity:0; transform:translateX(9px);  } to { opacity:1; transform:none; } }
@keyframes tp-out { from { opacity:0; transform:translateX(-9px); } to { opacity:1; transform:none; } }
.tp-list.in  { animation:tp-in  .16s cubic-bezier(.16,1,.3,1) both; }
.tp-list.out { animation:tp-out .16s cubic-bezier(.16,1,.3,1) both; }

/* ── «Усі теми» dialog ──────────────────────────────────────────────────────
   Earned, not reflexive: 250 topics at a width where their names are legible is
   a reading task the rail cannot hold. Native <dialog> for the focus trap, Esc
   and the top layer; selection applies live behind it, so «Готово» only closes. */
.td {
    /* A modal <dialog> is centred by the UA with `margin:auto` against its
       `inset:0` fixed box. styles.v2.css opens with `*, *::before, *::after {
       margin:0 }`, which wins over the UA sheet and pins the dialog to the
       viewport's top-left — 440px of dead space down one side at 1600px, and
       the panel sitting hard against the top edge at every width. Put the
       margin back; `auto` is what the element is designed around. */
    margin:auto;
    width:min(1160px, calc(100vw - 36px)); max-height:calc(100vh - 52px);
    padding:0; border:0; border-radius:var(--radius-lg);
    background:var(--card); color:var(--ink);
    box-shadow:0 2px 6px rgba(25,30,54,.06), 0 44px 90px -46px rgba(25,30,54,.55);
    flex-direction:column; overflow:hidden;
}
.td[open] { display:flex; }
.td::backdrop { background:rgba(25,30,54,.40); backdrop-filter:blur(2px); }
.td-head {
    display:grid; grid-template-columns:1fr auto auto; gap:10px 18px; align-items:end;
    padding:22px 26px 16px; border-bottom:1px solid var(--line);
    background:var(--card);
}
.td-titles { grid-column:1; }
.td-titles h2 {
    font-family:var(--font-display); font-weight:800; font-size:var(--fs-lead);
    letter-spacing:-.02em; margin:0 0 4px; color:var(--ink);
}
.td-titles p { margin:0; font-size:var(--fs-micro); color:var(--ink-2); }
.td-find {
    grid-column:2; grid-row:1; display:flex; align-items:center; gap:8px;
    width:min(300px, 40vw); padding:0 11px;
    background:var(--card); border:1.5px solid var(--line); border-radius:var(--radius-sm);
    transition:border-color .15s, box-shadow .15s;
}
.td-find:focus-within { border-color:var(--cobalt); box-shadow:0 0 0 3px var(--cobalt-wash); }
.td-find > svg { flex:none; color:var(--ink-muted); }
.td-find-input { flex:1; min-width:0; border:0; background:none; outline:none; font:inherit; font-size:var(--fs-label); color:var(--ink); padding:9px 0; }
.td-find-input::placeholder { color:var(--ink-muted); }
.td-lens { grid-column:1 / -1; grid-row:2; display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.td-lens-label { font-size:var(--fs-micro); font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-2); margin-right:var(--space-3xs); }
.td-year {
    width:32px; height:32px; display:grid; place-items:center;
    font:inherit; font-size:var(--fs-micro); font-weight:600; font-variant-numeric:tabular-nums;
    color:var(--ink-2); background:var(--card); border:1.5px solid var(--line);
    border-radius:var(--radius-sm); cursor:pointer; transition:border-color .14s, color .14s, background .14s;
}
.td-year:hover { border-color:var(--ink-muted); color:var(--ink); }
.td-year.on { background:var(--cobalt); border-color:var(--cobalt); color:var(--card); }
.td-year-clear { font:inherit; font-size:var(--fs-micro); color:var(--ink-2); background:none; border:0; padding:0 4px; cursor:pointer; text-decoration:underline; text-underline-offset:3px; }
.td-year-clear:hover { color:var(--ink); }
.td-x { grid-column:3; grid-row:1; justify-self:end; align-self:center; }

/* The dialog has a max-height but no height, so the body must be sized from its
   content and shrink to fit — `flex:1` (a 0% basis) resolves to zero in Safari
   and the whole curriculum collapsed to the body's 16px of padding. */
.td-head, .td-foot { flex:none; }
.td-body { flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain; padding:2px 26px 14px; }
.td-note { margin:16px 0 4px; font-size:var(--fs-micro); color:var(--ink-2); }
.td-hits { columns:3; column-gap:30px; margin-top:8px; }
.td-hits .tp-row { break-inside:avoid; }
.td-subject { padding:20px 0 2px; border-top:1px solid var(--line); }
.td-subject:first-child { border-top:0; }
/* The count belongs to the name, not to the far edge of a 1100px dialog. */
.td-subject-row { margin-bottom:12px; }
.td-subject-row .tp-text { flex:0 1 auto; }
.td-subject-row .tp-ct { padding-top:6px; }
.td-subject-row .tp-name { font-family:var(--font-display); font-weight:800; font-size:var(--fs-label); letter-spacing:-.01em; }
.td-subject-row .tp-ct { font-size:var(--fs-micro); }
/* Newspaper columns, not cards: the taxonomy is a syllabus to read, and a
   bordered box per розділ would be chrome standing in for structure. */
.td-cols { columns:3; column-gap:30px; }
.td-group { break-inside:avoid; margin:0 0 18px; }
.td-section-row { padding-bottom:4px; margin-bottom:3px; border-bottom:1px solid var(--line); }
.td-section-row .tp-name { font-weight:700; font-size:var(--fs-label); }
.td-group > .tp-row:not(.td-section-row) .tp-name { font-size:var(--fs-micro); color:var(--ink-2); }
.td-group > .tp-row.on .tp-name { color:var(--cobalt-deep); }
.td-empty { padding:36px 0 40px; text-align:center; }
.td-empty p { margin:0 0 10px; font-size:var(--fs-label); color:var(--ink-2); }

.td-foot {
    display:flex; align-items:center; justify-content:space-between; gap:16px;
    padding:13px 26px; border-top:1px solid var(--line); background:var(--ground);
}
.td-sum { margin:0; font-size:var(--fs-micro); color:var(--ink-2); }
.td-acts { display:flex; gap:8px; flex:none; }
.td-btn {
    font:inherit; font-size:var(--fs-micro); font-weight:600; padding:8px 15px;
    border:1.5px solid var(--line); border-radius:var(--radius-sm); background:var(--card);
    color:var(--ink); cursor:pointer; transition:border-color .14s, background .14s, color .14s;
}
.td-btn:hover { border-color:var(--ink-muted); }
.td-btn.primary { background:var(--cobalt); border-color:var(--cobalt); color:var(--card); }
.td-btn.primary:hover { background:var(--cobalt-deep); border-color:var(--cobalt-deep); }

@keyframes td-in { from { opacity:0; transform:translateY(10px) scale(.988); } to { opacity:1; transform:none; } }
@keyframes td-fade { from { opacity:0; } to { opacity:1; } }
.td[open] { animation:td-in .2s cubic-bezier(.16,1,.3,1); }
.td[open]::backdrop { animation:td-fade .2s ease-out; }

@media (max-width:900px){
    .td-cols, .td-hits { columns:2; }
    /* Stacked: title, then the search field, then the lens. `grid-row` is
       explicit because the base rule pins the lens to row 2, which would
       otherwise push an auto-placed search field below it. */
    .td-head { grid-template-columns:1fr auto; align-items:start; }
    .td-titles { grid-column:1; }
    .td-x { grid-column:2; grid-row:1; align-self:start; }
    .td-find { grid-column:1 / -1; grid-row:2; width:auto; }
    .td-lens { grid-row:3; }
}
@media (max-width:620px){
    .td { width:100vw; max-width:100vw; max-height:100vh; border-radius:0; }
    .td-cols, .td-hits { columns:1; }
    .td-head, .td-body, .td-foot { padding-left:18px; padding-right:18px; }
    .td-foot { flex-wrap:wrap; }
    .td-acts { flex:1 0 100%; }
    .td-acts .td-btn { flex:1; }
}
@media (prefers-reduced-motion:reduce){
    .tp-list.in, .tp-list.out, .td[open], .td[open]::backdrop { animation:none; }
    .tp-open:hover { transform:none; }
}

/* Scrollbars belong to the design too — the dialog body is the one long
   scroller this surface owns besides the rail (themed with .search-facets), so
   it is themed here rather than globally (the page's own scrollbar stays the
   platform's). */
.td-body { scrollbar-width:thin; scrollbar-color:var(--color-gray-300) transparent; }
.td-body::-webkit-scrollbar { width:9px; }
.td-body::-webkit-scrollbar-track { background:transparent; }
.td-body::-webkit-scrollbar-thumb {
    background:var(--color-gray-300); border-radius:var(--radius-full);
    border:3px solid var(--card); background-clip:padding-box;
}
.td-body:hover::-webkit-scrollbar-thumb { background:var(--ink-3); background-clip:padding-box; }

.search-results { padding:20px 24px 48px; min-width:0; }
.search-pills { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:16px; align-items:center; }
/* «Показано результати для …»: the query was corrected, re-read or shortened (search.js fallbackNote). */
.search-note { margin:0 0 16px; font-size:var(--fs-label); color:var(--ink-2); }
.search-pill { display:inline-flex; align-items:center; gap:7px; font-size:var(--fs-micro); padding:5px 7px 5px 12px; border-radius:var(--radius-full); background:var(--cobalt-wash); color:var(--cobalt-deep); border:1px solid transparent; max-width:min(38ch, 100%); min-width:0; }
/* Subject prefix on a topic pill — present for disambiguation, never competing
   with the topic's own name. The name is what truncates last. */
.search-pill .pre { flex:none; font-weight:600; opacity:.62; }
.search-pill .pre::after { content:"·"; margin-left:7px; opacity:.7; }
.search-pill .lbl { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
/* A <button> holding the system's own close mark — it was a <span role=button>
   holding a ✕ from the text font, which DESIGN.md rules out twice over. */
.search-pill .x { flex:none; width:24px; height:24px; border-radius:50%; background:rgba(44,70,216,.16); display:grid; place-items:center; cursor:pointer; color:var(--cobalt-deep); padding:0; border:0; }
.search-pill .x:hover { background:rgba(44,70,216,.28); }
.search-pill .x:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }
.search-pill.clear { background:transparent; color:var(--ink-2); border-color:var(--ink); cursor:pointer; font:inherit; font-size:var(--fs-micro); padding:5px 12px; min-height:32px; }
.search-pill.clear:hover { color:var(--ink); border-color:var(--ink-muted); }

.rcard { border:1px solid var(--line); border-radius:var(--radius-md); background:var(--card); padding:var(--space-ms); margin-bottom:var(--space-xs); transition:border-color .14s; display:block; width:100%; text-align:left; text-decoration:none; }
/* Flat at rest — and flat on hover. This carried a 36px cobalt bloom under a
   1px border, which is the exact combination the retired --shadow-lg was
   removed for. Hover changes colour, not height. */
.rcard:hover { border-color:var(--cobalt); }
/* The source line: competition left, public ID right, the featured card's
   head (.fp-head) in the list's voice. The ID is the problem page's locator
   voice at caption size. Stacked by default; the card's own width decides, so
   one column never mixes the forms: from 420px of card a long catalog name
   wraps in the left column and the ID holds the top-right corner; below
   that (phones) the ID sits under the name. */
.rcard { container:rc / inline-size; }
.rcard-top {
    display:flex; flex-direction:column; align-items:flex-start;
    gap:2px; margin-bottom:var(--space-2xs);
}
.rcard-top .atag { min-width:0; }
.rcard-pid {
    flex:none; font-family:var(--font-sans); font-weight:500; font-size:var(--fs-micro); line-height:1.35;
    letter-spacing:.03em; font-variant-numeric:tabular-nums; color:var(--ink-muted); white-space:nowrap;
    transition:color .14s;
}
.rcard:hover .rcard-pid { color:var(--ink-2); }
@container rc (min-width:420px) {
    .rcard-top { flex-direction:row; align-items:baseline; justify-content:space-between; gap:16px; }
}
.rcard h3 { font-family:var(--font-display); font-weight:600; font-size:var(--fs-read); margin:0 0 6px; letter-spacing:-.01em; color:var(--ink); }
/* The statement is the card. It was set at 14.72px in --ink-2 — secondary
   text — under an --ink-2 competition tag and beside a cobalt grade chip, so
   the loudest thing in a result was the chip and the second loudest was the
   one string that is identical on every card. «The mathematics has the most
   contrast on any screen» was true everywhere except the list a student
   actually scans. Full ink, body size; the measure comes from the card. */
.rcard .rbody {
    /* The scanning voice in the math's face: one step under the reading
       20px, because a three-line clamp at 20px held nine words on a phone.
       Its formulas were 16px (1em of Inter's 16) before; 17 now. */
    font-family:var(--font-math); font-size:var(--fs-read); color:var(--ink); line-height:1.5;
    display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3;
    overflow:hidden; overflow-wrap:anywhere;
}
/* Regular weight: Computer Modern has no semibold, and its bold is the wide
   Bold Extended cut, which made every hit a slab. The wash and the pencil
   colour carry the match. */
.rcard .rbody mark { background:var(--red-wash); color:var(--red); padding:0 2px; border-radius:var(--radius-xs); font-weight:400; }
.rcard .rmeta { margin-top:var(--space-xs); display:flex; gap:var(--space-xs); font-size:var(--fs-micro); color:var(--ink-muted); flex-wrap:wrap; }

.search-empty { text-align:center; color:var(--ink-2); padding:56px 20px 64px; }
.search-empty .big { font-family:var(--font-display); font-size:var(--fs-lead); color:var(--ink); margin-bottom:8px; }
/* The «Шукаю…» placeholder: result-card skeletons sized like real cards
   (measured 2026-09-30: 210-237 px wide, 230-284 px on a phone, where titles
   run to 2-3 lines), with the featured cards' blue-ink shimmer. */
.rcard.rcard-skel { pointer-events:none; min-height:210px; display:flex; flex-direction:column; gap:10px; }
.rcard-skel .sk {
    display:block; border-radius:var(--radius-xs);
    background:linear-gradient(90deg, var(--cobalt-wash) 0%, #FBFBFE 45%, var(--cobalt-wash) 90%);
    background-size:220% 100%; animation:fp-shimmer 1.25s linear infinite;
}
.rcard-skel .sk-top { width:42%; height:11px; margin-bottom:4px; }
.rcard-skel .sk-title { width:68%; height:17px; margin-bottom:4px; }
.rcard-skel .sk-title-2 { display:none; width:40%; }
.rcard-skel .sk-line { height:13px; }
.rcard-skel .sk-short { width:55%; }
.rcard-skel .sk-meta { width:28%; height:10px; margin-top:auto; }
@media (max-width:680px){
    .rcard.rcard-skel { min-height:257px; }
    .rcard-skel .sk-title { width:88%; }
    .rcard-skel .sk-title-2 { display:block; width:52%; }
}
@media (prefers-reduced-motion:reduce){ .rcard-skel .sk { animation:none; } }

/* The gap indicator between page numbers inherited --ink-3 (#8A8F9C, 2.98:1
   on paper) from v2 — the Ink-Muted Floor's only remaining escape on /search. */
.search-pager { display:flex; gap:6px; justify-content:center; align-items:center; margin-top:24px; flex-wrap:wrap; color:var(--ink-muted); }
.search-pager .pager-gap { padding:0 2px; }
.search-pager .pg { display:inline-flex; align-items:center; justify-content:center; min-width:40px; height:40px; padding:0 var(--space-2xs); border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--card); color:var(--ink); font-size:var(--fs-label); font-variant-numeric:tabular-nums; text-decoration:none; }
.search-pager a.pg:hover { border-color:var(--cobalt); color:var(--cobalt-deep); }
.search-pager a.pg:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }
.search-pager .pg.cur { background:var(--cobalt); color:var(--card); border-color:var(--cobalt); font-weight:700; }
.search-pager .pg.is-disabled { opacity:.4; cursor:default; }

/* ── The filter sheet (<=760px) ──────────────────────────────────────────────
   Stacked above the results the rail put the first problem card 1,870px down —
   2.3 viewport heights of checkboxes before a phone user sees any mathematics.
   Below 760px it becomes a sheet that is closed until asked for. */
.facet-sheet-toggle { display:none; }
.facet-sheet-scrim { display:none; }

@media (max-width:760px){
    .search-body { grid-template-columns:1fr; }

    .facet-sheet-toggle {
        display:inline-flex; align-items:center; gap:8px; flex:none;
        min-height:44px; padding:10px 16px;
        font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label);
        color:var(--ink); background:var(--card);
        border:1px solid var(--ink); border-radius:var(--radius-sm); cursor:pointer;
        transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
    }
    .facet-sheet-toggle:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
    .facet-sheet-toggle .n {
        display:inline-grid; place-items:center; min-width:20px; height:20px; padding:0 6px;
        font-size:var(--fs-micro); font-variant-numeric:tabular-nums;
        background:var(--cobalt); color:var(--card); border-radius:var(--radius-full);
    }
    .facet-sheet-toggle[hidden] { display:none; }

    /* The count leaves the input's flex row. Sharing it squeezed the field to
       107px on a 375px screen — about seven Cyrillic characters. */
    .search-top-inner { flex-wrap:wrap; gap:10px; }
    .search-box { flex:1 1 100%; order:1; }
    .facet-sheet-toggle { order:2; }
    .search-count, .search-status { order:3; font-size:var(--fs-micro); }

    .facet-sheet-scrim {
        display:block; position:fixed; inset:0; z-index:40;
        background:rgba(25,30,54,.42); opacity:0;
        transition:opacity var(--dur-sheet) var(--ease);
    }
    .facet-sheet-scrim[hidden] { display:none; }
    .facet-sheet-scrim.is-open { opacity:1; }

    .search-facets {
        position:fixed; inset:auto 0 0 0; z-index:41;
        max-height:82vh; width:auto;
        margin:0; padding:8px 20px calc(20px + env(safe-area-inset-bottom));
        background:var(--card); border:0; border-top:1px solid var(--ink);
        border-radius:var(--radius-lg) var(--radius-lg) 0 0;
        box-shadow:var(--shadow-overlay);
        transform:translateY(100%);
        transition:transform var(--dur-sheet) var(--ease);
        overflow-y:auto; overscroll-behavior:contain;
    }
    .search-facets.is-open { transform:translateY(0); }
    .search-facets[hidden] { display:none; }

    /* A grab handle and a real Done button, so the sheet can always be closed
       by touch as well as by Esc. */
    .facet-sheet-head {
        display:flex; align-items:center; justify-content:space-between; gap:12px;
        position:sticky; top:0; z-index:1; margin:0 -20px 12px; padding:10px 20px 12px;
        background:var(--card); border-bottom:1px solid var(--line);
    }
    .facet-sheet-head::before {
        content:""; position:absolute; top:4px; left:50%; translate:-50% 0;
        width:36px; height:4px; border-radius:var(--radius-full); background:var(--line);
    }
    .facet-sheet-done {
        min-height:44px; padding:10px 18px; border:1px solid var(--cobalt);
        background:var(--cobalt); color:var(--card); border-radius:var(--radius-sm);
        font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label); cursor:pointer;
    }
    .facet-sheet-title { font-family:var(--font-display); font-weight:700; font-size:var(--fs-body); color:var(--ink); }

    @media (prefers-reduced-motion: reduce) {
        .search-facets, .facet-sheet-scrim { transition:none; }
    }
}

/* Desktop never shows the sheet chrome. */
@media (min-width:761px){ .facet-sheet-head { display:none; } }

/* ════════ DETAIL (PROBLEM) PAGE — squared-notebook re-skin ════════════════
   The detail page inherited styles.v2.css component idioms (gradient CTA,
   colour-coded chips, gray pills, sans headings, panel-stack dividers) that
   clashed with the landing. Re-skin into the notebook language: cream card,
   Fraunces serif title, Unbounded display eyebrows, flat cobalt accents,
   neutral chips, airy hairline separation. Everything scoped under
   .problem-card-detail so list/search keep their own treatment. */
/* Not a card — the problem is the page's main content, so it sits open on the
   cream ground. Sections align to the .view-inner gutter and separate by
   whitespace + hairlines. */
.problem-card-detail { background:none; border:0; border-radius:0; box-shadow:none; overflow:visible; }
.problem-card-detail > .problem-nav,
.problem-card-detail > .detail-breadcrumb,
.problem-card-detail > .problem-header,
.problem-card-detail > .problem-body,
.problem-card-detail > .topics-section,
.problem-card-detail > .citation-block {
    padding-left:0; padding-right:0; background:none;
}

/* Toolbar: transparent row above the panel. The gap under it: with none, the
   back button's hover box and focus ring sat flush on the panel's top edge. */
.problem-card-detail .problem-nav {
    background:none; border-bottom:0;
    padding-top:0; padding-bottom:0;
    margin-bottom:var(--space-xs);
}
/* Drawn-box buttons (back / share). */
.problem-card-detail .btn-nav,
.problem-card-detail .btn-share {
    background:var(--card); border:1.5px solid var(--line); border-radius:var(--radius-sm);
    color:var(--ink); font-family:var(--font-sans); font-weight:600; font-size:var(--fs-micro);
    transition:border-color .15s, color .15s, background .15s;
}
.problem-card-detail .btn-nav:hover:not(:disabled),
.problem-card-detail .btn-share:hover {
    background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep);
}
.problem-card-detail .btn-nav-back { background:none; border-color:transparent; max-width:32ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Segmented prev / counter / next as one outlined control. */
.problem-card-detail .nav-steps {
    border:1.5px solid var(--line); border-radius:var(--radius-sm); background:var(--card); padding:2px;
}
.problem-card-detail .nav-steps .btn-step { border-color:transparent; background:none; border-radius:var(--radius-sm); }
.problem-card-detail .nav-steps .btn-step:hover:not(:disabled) { background:var(--cobalt-wash); border-color:transparent; color:var(--cobalt-deep); }
.problem-card-detail .problem-counter { color:var(--ink-2); font-weight:600; }

/* Breadcrumb. */
.problem-card-detail .detail-breadcrumb { padding-top:16px; color:var(--ink-muted); }
.problem-card-detail .breadcrumb-link { color:var(--ink-2); }
.problem-card-detail .breadcrumb-link:hover { color:var(--cobalt-deep); }
.problem-card-detail .detail-breadcrumb-sep { color:var(--ink-muted); }

/* Header — top of the panel; flush to the panel padding, no divider. */
/* The header is three things in descending loudness — what the problem is
   called, where it sits, what it came from — and the spacing has to say so.
   It used to run 6px / 16px / 14px: the title-to-subtitle gap was TIGHTER than
   the subtitle's own leading, so the two collided, while the next two gaps were
   near-equal, leaving the subtitle floating between the title and the chips
   instead of belonging to either. Gaps now ascend — 5 / 16 / 20 — so the title
   and its locator read as one block and the chips as the next. */
.problem-card-detail .problem-header { padding-left:0; padding-right:0; padding-top:0; padding-bottom:20px; border-bottom:0; }
.problem-card-detail .problem-title {
    font-family:var(--font-display); font-weight:600; letter-spacing:-.01em;
    font-size:var(--fs-title); line-height:1.08; color:var(--ink); margin-bottom:var(--space-3xs);
}
/* Public ID («UMO-23-24-017») — the subheader: the problem's catalogue number,
   under the title in the muted locator voice, tabular so the digits line up.
   A button (a click copies it), quiet at rest: the copy glyph and the cobalt
   bed appear on hover/focus, the way the ghost Copy-LaTeX does. The negative
   margin keeps the CODE, not the hit box, on the title's left edge. Not a
   .btn-*, so print keeps it (see @media print below). */
.problem-card-detail .problem-pid {
    display:inline-flex; align-items:center; gap:8px;
    margin:0 0 0 -6px; padding:1px 6px;
    background:none; border:1px solid transparent; border-radius:var(--radius-sm);
    font-family:var(--font-sans); font-weight:500; font-size:var(--fs-label);
    letter-spacing:.03em; line-height:1.4; font-variant-numeric:tabular-nums;
    color:var(--ink-muted); cursor:pointer;
    transition:border-color .15s, color .15s, background .15s;
}
.problem-card-detail .problem-pid svg { flex:0 0 auto; opacity:0; transition:opacity .15s; }
.problem-card-detail .problem-pid:hover,
.problem-card-detail .problem-pid:focus-visible { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
.problem-card-detail .problem-pid:hover svg,
.problem-card-detail .problem-pid:focus-visible svg { opacity:1; }
/* No hover on touch: the glyph stays, so the code reads as tappable. */
@media (hover:none) { .problem-card-detail .problem-pid svg { opacity:.7; } }
@media print {
    .problem-card-detail .problem-pid { margin-left:0; padding-left:0; color:#000; }
    .problem-card-detail .problem-pid svg { display:none; }
}
/* Meta — neutral chips (no colour-coding). */
.problem-card-detail .problem-meta { gap:8px; margin-top:16px; }
.problem-card-detail .problem-meta .meta-badge {
    background:var(--ground); border:1px solid var(--line); color:var(--ink-2);
    border-radius:var(--radius-sm); padding:var(--space-3xs) var(--space-xs); font-size:var(--fs-micro); font-weight:500; line-height:1.4;
}
.problem-card-detail .problem-meta a.meta-badge { text-decoration:none; }
.problem-card-detail .problem-meta .meta-badge--link:hover { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
.problem-card-detail .translation-badge.is-translated {
    background:var(--cobalt-wash); color:var(--cobalt-deep);
    border-color:color-mix(in srgb, var(--cobalt) 30%, transparent);
}


/* Body. The whole problem now lives in ONE notebook "paper" card: the
   .problem-panel carries the cream ground + faint клітинка grid; header,
   statement, actions, revealed content and footer sit flush inside it. */
/* The problem page spans the content column — the same 1120px the header
   sits in, so the panel's edges line up with the wordmark and «Усі задачі».
   It was held to a 704px column (a 38em measure, ~71 characters a line) from
   2026-09-20; on a desktop that read as a phone layout with a margin of empty
   ground either side, and the user called it bad UX (2026-09-21). The text
   fills the panel: capping it inside a wide panel left the right third empty. */

.problem-card-detail .problem-panel {
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-lg);
    padding:var(--space-md) 28px; overflow:hidden;
    background-image:
        linear-gradient(rgba(44,70,216,.045) 1px,transparent 1px),
        linear-gradient(90deg,rgba(44,70,216,.045) 1px,transparent 1px);
    background-size:23px 23px; background-position:-1px -1px;
}
.problem-card-detail .problem-body { padding:0; }

/* The mathematics is the largest body text on the site, by intent. It was
   15.68px — smaller than this system's nominal body — for the one thing a
   student reads for an hour, at night, on a cheap screen. */
.problem-card-detail .problem-content,
.problem-card-detail .solution-content-wrap,
.problem-card-detail .solution-inline,
.problem-card-detail .answer-reveal-content {
    font-family:var(--font-math); font-size:var(--fs-math); line-height:1.5; color:var(--ink);
}

/* Editorial caution — the book's own error; heavier red-pencil margin rule. */
.problem-card-detail .editorial-note {
    background:var(--red-wash); border-left:4px solid var(--red);
    color:var(--ink); border-radius:var(--radius-sm);
}
.problem-card-detail .editorial-note-label { font-family:var(--font-display); letter-spacing:.12em; color:var(--red); }
/* The note carries $…$ math, so it is problem text too: one face with it. */
.problem-card-detail .editorial-note-body { color:var(--ink); font-family:var(--font-math); font-size:var(--fs-math); line-height:1.5; }
/* Copy-LaTeX → quiet content utility (ghost), NOT a page-action button.
   Lives on the «Умова задачі» header; sits in the same low tier as the
   Report/Propose .icon-btn — borderless at rest, cobalt wash on hover. */
.problem-card-detail .btn-copy-latex {
    display:inline-flex; align-items:center; gap:6px;
    background:none; border:1px solid transparent; border-radius:var(--radius-sm);
    color:var(--ink-muted); font-family:var(--font-sans); font-weight:600;
    transition:border-color .15s, color .15s, background .15s;
}
.problem-card-detail .btn-copy-latex:hover { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
.problem-card-detail .btn-copy-latex svg { flex:0 0 auto; }
.problem-card-detail .btn-copy-latex-label { line-height:1; }
.problem-card-detail .problem-content-header .btn-copy-latex.compact { flex:0 0 auto; align-self:center; padding:4px 8px; font-size:var(--fs-micro); }

/* Action row — real buttons under the statement. */
.problem-card-detail .detail-actions { display:flex; flex-wrap:wrap; gap:var(--space-xs); margin-top:var(--space-md); }
/* Primary: flat cobalt fill, like the landing's «Пошук». Auto width. */
.problem-card-detail .solutions-toggle {
    width:auto; display:inline-flex; align-items:center; gap:.5rem;
    background:var(--cobalt); color:var(--card); border:1.5px solid var(--cobalt); border-radius:var(--radius-sm);
    padding:12px 22px; font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body);
    letter-spacing:0; box-shadow:none;
    transition:background .15s, border-color .15s, color .15s;
}
.problem-card-detail .solutions-toggle:hover { background:var(--cobalt-deep); border-color:var(--cobalt-deep); transform:none; filter:none; box-shadow:none; }
.problem-card-detail .solutions-toggle.is-open { background:var(--card); color:var(--cobalt-deep); border-color:var(--cobalt); }
.problem-card-detail .solutions-toggle.is-open:hover { background:var(--cobalt-wash); }
/* Secondary: outlined drawn-box. */
.problem-card-detail .answer-reveal-toggle {
    width:auto; display:inline-flex; align-items:center; gap:.5rem;
    background:var(--card); color:var(--ink); border:1.5px solid var(--line); border-radius:var(--radius-sm);
    padding:12px 20px; font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body);
}
.problem-card-detail .answer-reveal-toggle:hover { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
/* Revealed panels — flat sub-sections inside the panel, set off from the
   action row by a hairline (no own card/grid). */
.problem-card-detail .solutions-content { margin-top:var(--space-ms); padding-top:var(--space-ms); border-top:1px solid var(--line); }
.problem-card-detail .answer-reveal-content {
    display:none; margin-top:var(--space-ms); background:var(--cobalt-wash);
    border-radius:var(--radius-sm); padding:var(--space-sm) var(--space-ms); color:var(--ink);
}
.problem-card-detail .answer-reveal-content.visible { display:block; }
.problem-card-detail .solution-content-wrap { background:none; border:0; border-radius:0; padding:0; }
.problem-card-detail .answer-box { background:var(--cobalt-wash); border-radius:var(--radius-sm); padding:var(--space-xs) var(--space-sm); margin-top:var(--space-xs); }
/* Solution tabs — clean pills (drop v2's ::after underline + the row's bottom
   border, which doubled the active indicator). */
.problem-card-detail .solution-tabs { border-bottom:0; padding-bottom:0; margin-bottom:16px; gap:8px; }
.problem-card-detail .solution-tab {
    border:1.5px solid var(--line); background:var(--card); border-radius:var(--radius-full);
    color:var(--ink-2); font-weight:600; font-size:var(--fs-micro); padding:6px 16px;
}
.problem-card-detail .solution-tab:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.problem-card-detail .solution-tab.active { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
.problem-card-detail .solution-tab.active::after { display:none; }
/* Header: title (single-solution only) left, copy button right. */
.problem-card-detail .solution-detail-header { justify-content:flex-end; }
.problem-card-detail .solution-detail-title { margin-right:auto; }

/* Related topics — neutral notebook chips (no per-subject colour).
   No rule above them. The panel ends in a 16px curve, and a full-width hairline
   drawn flush beneath it runs straight past the point where that curve has
   already turned in — so the card read as a box with its bottom corners sawn
   off, and the line separated nothing the panel's own edge had not separated
   already. Whitespace does the job. */
.problem-card-detail .topics-section { border-top:0; padding-top:var(--space-lg); padding-bottom:var(--space-md); }
.problem-card-detail .topics-grid { flex-direction:row; flex-wrap:wrap; gap:8px; }
.problem-card-detail .topic-item { width:auto; text-decoration:none; background:var(--card); border:1px solid var(--line); border-radius:var(--radius-full); padding:6px 14px; }
.problem-card-detail .topic-item:hover { border-color:var(--cobalt); background:var(--cobalt-wash); }
.problem-card-detail .topic-level { background:none !important; color:var(--ink-2) !important; padding:0; font-weight:500; }
.problem-card-detail .topic-level-0 { color:var(--ink) !important; font-weight:700; }
.problem-card-detail .topic-separator { color:var(--ink-muted); }
/* «Схожі задачі» + the neighbouring problems of the same event (site-proxy/ssr.js
   builds it; js/views/detail.js attaches it). Result cards in the list's own
   voice, minus the statement: a title is enough to choose by, and the page
   above is already the problem. Prev/next is one row of two, the direction as
   an eyebrow; the related set is a grid that drops to one column on phones.
   It comes after «Джерело» (which describes THIS problem), so the page's one
   hairline sits on top of it: everything above the line is this problem,
   everything below is other problems. */
.problem-card-detail .problem-more { border-top:1px solid var(--line); padding-top:22px; padding-bottom:var(--space-md); }
.problem-card-detail .problem-seq { display:grid; grid-template-columns:1fr 1fr; gap:var(--space-xs); margin-bottom:var(--space-lg); }
/* Neighbour / related card (DESIGN.md → Cards): the result card's frame and
   source line, title-first. The title is what a reader picks by, so it leads,
   in Inter 600 at body size; six cards of Unbounded titles under a two-line
   source stack read as a wall of text (user, 2026-09-30), and Unbounded is
   for the brand and the headline. The source line keeps the result card's
   form (ID under the name below 420px of card, at its right end above).
   Everything left-aligned, «Наступна» included: the arrow says the direction,
   a mirrored card only gave it a ragged left edge (user, 2026-09-30). */
.problem-card-detail .problem-more .rcard { margin-bottom:0; display:flex; flex-direction:column; }
.problem-card-detail .problem-more .rcard h3 {
    order:-1; margin:0 0 var(--space-2xs);
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body); line-height:1.35;
    letter-spacing:0; color:var(--ink);
}
.problem-card-detail .problem-more .rcard-top { margin:0; }
.problem-card-detail .pm-dir { order:-2; display:block; margin-bottom:var(--space-2xs); font-family:var(--font-sans); font-weight:600; font-size:var(--fs-micro); letter-spacing:.02em; color:var(--cobalt-deep); }
.problem-card-detail .problem-more-h { margin:0 0 var(--space-xs); }
.problem-card-detail .problem-related { display:grid; grid-template-columns:repeat(auto-fill, minmax(260px, 1fr)); gap:var(--space-xs); }
@media (max-width:560px) {
    .problem-card-detail .problem-seq { grid-template-columns:1fr; }
    .problem-card-detail .pm-gap { display:none; }
}
/* The back control is a link now (a real href for crawlers and new tabs). */
.problem-card-detail a.btn-nav { display:inline-block; text-decoration:none; }

/* Flat гурток tag: the topic's own name only, path in the tooltip. */
/* «Інші задачі ↓» (detail.js syncMoreJump) shares the heading's row, at its
   right end: the one place that is on screen exactly when the card ends. */
.problem-card-detail .topics-section { display:grid; grid-template-columns:1fr auto; align-items:baseline; column-gap:var(--space-xs); }
.problem-card-detail .topics-grid { grid-column:1 / -1; }
.problem-card-detail .more-jump {
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label); line-height:1.3; color:var(--cobalt-deep);
    background:none; border:0; border-radius:var(--radius-sm); padding:6px 8px; margin:-6px -8px -6px 0; cursor:pointer; white-space:nowrap;
}
.problem-card-detail .more-jump:hover { background:var(--cobalt-wash); }
.problem-card-detail .problem-more { scroll-margin-top:88px; }
.problem-card-detail .problem-more:focus { outline:none; }
@media (pointer:coarse){ .problem-card-detail .more-jump { padding:13px 8px; margin:-13px -8px -13px 0; } }
@media print { .problem-card-detail .more-jump { display:none; } }
/* The topic tags are the header's «8 клас» chip (user 2026-10-05): same small
   radius, size, weight and hover, at every width. Full pills stacked ragged on
   a phone and one that wrapped swelled into a blob; a chip with an 8px corner
   wraps as a plain box. Filled with --card because it sits on the page ground,
   where the header chip's --ground fill would vanish. The 44px floor for coarse
   pointers is lifted here, as it is for the header chips they now match. */
.problem-card-detail .topic-item.topic-item--flat {
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-sm);
    padding:var(--space-3xs) var(--space-xs); min-height:0; max-width:100%;
}
.problem-card-detail .topic-item--flat .topic-name { font-size:var(--fs-micro); font-weight:500; line-height:1.4; color:var(--ink-2); }
.problem-card-detail .topic-item--flat:hover { background:var(--cobalt-wash); border-color:var(--cobalt); }
.problem-card-detail .topic-item--flat:hover .topic-name { color:var(--cobalt-deep); }

/* Source / citation: right under «Пов'язані теми», no rule between them —
   both describe this problem. The page's one hairline moved to .problem-more
   below, where the page turns from this problem to others. */
.problem-card-detail .citation-block { border-top:0; padding-top:var(--space-lg); padding-bottom:28px; }
.problem-card-detail .topics-section + .citation-block { padding-top:var(--space-2xs); }
.problem-card-detail .citation-source-badge { color:var(--ink); }
/* Was a right-aligned label column — a form idiom that needs many rows of
   similar length to read as a column. With two rows of «Змагання» and «Захід»
   it just looked like two lines that had failed to line up. Labels now start on
   the same left edge as everything else on the page and the values align in
   their own column, which is what a source citation is: a short definition
   list. */
.problem-card-detail .citation-body {
    grid-template-columns:max-content 1fr; gap:7px 20px;
    font-size:var(--fs-label); line-height:1.5;
}
.problem-card-detail .citation-field-label {
    text-align:left; padding-right:0; font-weight:500; color:var(--ink-muted);
}
.problem-card-detail .citation-field-value { color:var(--ink); }
/* «Джерело» stacks on a phone. Two columns cannot work at 320px: the label
   column is sized to the longest label («Задачу запропоновано», ~180px), which
   leaves under 100px for a value like «Українська математична олімпіада» — less
   than its min-content width, so the value pushed past the viewport edge. */
@media (max-width:480px){
    .problem-card-detail .citation-body { grid-template-columns:1fr; gap:0; }
    .problem-card-detail .citation-field-label { font-size:var(--fs-micro); margin-top:12px; }
    .problem-card-detail .citation-field-label:first-child { margin-top:0; }
    .problem-card-detail .citation-field-value { overflow-wrap:anywhere; }
}
.icon-btn-label { font-size:var(--fs-micro); line-height:1.2; }

/* Report / Propose — quiet footer row at the bottom of the panel. No rule above
   it either: it introduced a single small ghost link with a full-width divider,
   which gave the panel a "footer section" it does not have and put a second
   horizontal line into a card that already has an edge. Space alone. */
/* It rides on the reveal buttons' row, right-aligned and centred on them: as a
   row of its own it left a lone link under a lone button, the panel's bottom
   edge ragged on both sides (user, 2026-09-30). Done in CSS — the body wraps,
   and `order` lifts the toolbar above the revealed panels — because the
   server-rendered page (site-proxy/ssr.js) and the SPA both emit it last. */
.problem-card-detail .problem-body { display:flex; flex-wrap:wrap; align-items:stretch; column-gap:var(--space-xs); }
.problem-card-detail .problem-body > * { flex:1 0 100%; min-width:0; }
.problem-card-detail .problem-body > .detail-actions { flex:1 1 auto; order:1; }
.problem-card-detail .problem-body > .answer-reveal-content,
.problem-card-detail .problem-body > .solutions-content { order:3; }
.problem-card-detail .problem-toolbar {
    display:flex; justify-content:flex-end; align-items:center; gap:6px;
    flex:0 0 auto; order:2; margin-left:auto;
    margin-top:var(--space-md); padding-top:0; border-top:0;
}
/* Once an answer or solution is open, the link goes to the bottom, under it:
   what a reader reports is usually in what they just read (user, 2026-09-30). */
.problem-card-detail .problem-body:has(> .answer-reveal-content.visible, > .solutions-content.visible) > .problem-toolbar {
    order:4; margin-top:var(--space-md);
}
.problem-card-detail .icon-btn { color:var(--ink-muted); }
.problem-card-detail .icon-btn:hover { color:var(--cobalt-deep); border-color:var(--cobalt); background:var(--cobalt-wash); }

/* ============================================================================
   «Рекомендації щодо вивчення олімпіадної математики» (/guide)
   A single-column long-read. No data, no components — just measured prose on
   the notebook ground, capped at ~68ch so a 1 400-word article stays readable
   on a wide monitor.
   ============================================================================ */
.guide-inner { padding-top:40px; padding-bottom:72px; }
.guide { max-width:68ch; margin:0 auto; }

.guide-head { padding-bottom:24px; margin-bottom:8px; border-bottom:1px solid var(--line); }
/* Both of this page's standfirsts lost to `.guide p` on specificity — (0,1,1)
   beats (0,1,0) whatever the order — so they had never once rendered at the
   size they declare. They were setting at the BODY size, in CAPS, tracked to
   .14em: 29 characters here and 40 in the promo card
   («Від Apex Talent — засновників Apex Minds»), which is a sentence, and a
   sentence set in Cyrillic caps is unreadable at any size. Sentence case,
   Inter, normal tracking; the words are untouched. */
.guide-head .guide-eyebrow {
    margin:0 0 var(--space-xs); font-family:var(--font-sans); font-weight:600;
    font-size:var(--fs-micro); letter-spacing:.01em; text-transform:none;
    line-height:1.4; color:var(--cobalt-deep);
}
.guide-head h1 {
    margin:0; font-family:var(--font-display); font-weight:700;
    font-size:var(--fs-title); line-height:1.14; letter-spacing:-.018em;
    color:var(--ink); text-wrap:balance;
}
/* Inter, not a serif: Fraunces (retired 2026-09-20) was Latin-only upstream,
   so a Ukrainian lead fell through to whatever serif the OS had. */
.guide-lead { margin:18px 0 0; font-size:var(--fs-read); line-height:1.62; color:var(--ink-2); }

.guide-sec { padding-top:34px; }
.guide h2 {
    margin:0 0 14px; font-family:var(--font-display); font-weight:700;
    font-size:var(--fs-lead); line-height:1.25; letter-spacing:-.012em; color:var(--ink);
}
.guide h3 {
    margin:26px 0 10px; font-family:var(--font-sans); font-weight:700;
    font-size:var(--fs-body); color:var(--cobalt-deep);
}
.guide p { margin:0 0 15px; font-size:var(--fs-body); line-height:1.68; color:var(--ink); }
.guide p:last-child { margin-bottom:0; }
.guide em { font-style:italic; }
.guide strong { font-weight:650; }

.guide-list { margin:0 0 15px; padding-left:1.25em; }
.guide-list li { margin-bottom:11px; font-size:var(--fs-body); line-height:1.62; color:var(--ink); }
.guide-list li::marker { color:var(--ink-muted); }
ol.guide-list li::marker { font-family:var(--font-display); font-weight:700; color:var(--cobalt); }

/* Links inside the article — cobalt, underlined: an unstyled link in body copy
   reads as plain text. The two CTA buttons (search, promo) opt out; they carry
   their own chrome. */
.guide a:not(.btn-all):not(.g-promo-btn) {
    color:var(--cobalt-deep); text-decoration:underline; text-underline-offset:2px;
    text-decoration-thickness:1px; text-decoration-color:var(--color-primary-200);
}
.guide a:not(.btn-all):not(.g-promo-btn):hover { color:var(--cobalt); text-decoration-color:currentColor; }

/* ── /guide figures ──────────────────────────────────────────────────────────
   Four diagrams, all plain HTML + brand tokens — no SVG, no bitmaps. Text in a
   scaled SVG viewBox falls under 8px at phone width, and every one of these is
   boxes, bars and labels, which CSS already does responsively and with real,
   selectable, translatable text. Each figure carries its meaning in the markup
   (an <ol> is a sequence, a <figcaption> is the note); the decorative grids
   and bars are aria-hidden with the whole diagram labelled once.
   ────────────────────────────────────────────────────────────────────────── */
.g-fig { margin:22px 0 4px; }
.g-fig figcaption {
    margin-top:12px; font-size:var(--fs-label); line-height:1.55; color:var(--ink-muted);
}
/* A figure nested inside a rule of the «Шість правил» list. */
.g-fig-inset { margin:16px 0 6px; }

/* — Session flow: numbered steps, chevron-separated ——————————————————————— */
.g-flow {
    list-style:none; margin:0; padding:0;
    display:grid; grid-template-columns:repeat(4, 1fr); gap:10px;
    counter-reset:g-flow;
}
.g-flow li {
    counter-increment:g-flow; position:relative;
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-lg);
    padding:13px 14px 14px;
}
.g-flow li::before {
    content:counter(g-flow);
    display:block; margin-bottom:7px;
    font-family:var(--font-display); font-weight:700; font-size:var(--fs-micro);
    color:var(--cobalt); letter-spacing:.06em;
}
/* Chevron in the gutter between cards; none after the last. */
.g-flow li:not(:last-child)::after {
    content:"›"; position:absolute; top:50%; right:-11px; transform:translateY(-50%);
    width:12px; text-align:center; line-height:1;
    font-size:var(--fs-lead); color:var(--ink-3);
}
.g-flow b { display:block; font-size:var(--fs-label); font-weight:700; color:var(--ink); }
.g-flow span { display:block; margin-top:5px; font-size:var(--fs-micro); line-height:1.45; color:var(--ink-2); }

/* — Five years: three phase bands over a 5-column year scale ——————————————— */
.g-years { background:var(--card); border:1px solid var(--line); border-radius:var(--radius-lg); padding:14px 16px 16px; }
.g-years-scale, .g-years-grid { display:grid; grid-template-columns:repeat(5, 1fr); }
.g-years-scale {
    gap:4px; margin-bottom:9px; padding-bottom:8px; border-bottom:1px solid var(--line);
    font-family:var(--font-display); font-weight:700; font-size:var(--fs-micro); color:var(--ink-muted);
    text-align:center;
}
.g-years-grid { gap:6px 4px; }
.g-band {
    display:flex; align-items:center; justify-content:center; min-height:34px;
    padding:6px 10px; border-radius:var(--radius-sm); text-align:center;
    font-size:var(--fs-micro); font-weight:650; line-height:1.25;
}
/* Ordered phases → one hue, light to dark (never a rainbow). */
.g-band-1 { grid-column:1 / span 2; background:var(--color-primary-100); color:var(--ink); }
.g-band-2 { grid-column:2 / span 2; background:var(--color-primary-200); color:var(--ink); }
.g-band-3 { grid-column:3 / span 3; background:var(--cobalt); color:var(--card); }

/* — Evidence bars: emphasis, one accent + one de-emphasis gray ——————————————
   Two conditions of one experiment, both direct-labelled, so identity never
   rests on colour and the figure needs no legend and no hover layer. Bars run
   from a common zero on a 0–100 % scale: the width IS the percentage. */
.g-chart-title { margin:0 0 10px; font-size:var(--fs-micro); font-weight:700; color:var(--ink); }
.g-bars { display:flex; flex-direction:column; gap:9px; }
.g-bar-row { display:grid; grid-template-columns:minmax(0,10.5em) 1fr; align-items:center; gap:10px; }
.g-bar-label { font-size:var(--fs-micro); color:var(--ink-2); }
/* The track is the 0–100 % scale; the value rides the bar's tip, not the far
   right edge, so the number never detaches from the mark it belongs to. */
/* The track reserves a strip on the right for the value, so the longest bar can
   fill the plot area and its label still fits. Every bar is a percentage of the
   same content box, so the reserve costs nothing in proportionality. */
.g-bar-track {
    display:flex; align-items:center; gap:8px; min-width:0;
    padding-right:3.6em; box-sizing:border-box;
    border-left:1px solid var(--line);
}
.g-bar {
    flex:none; height:14px; background:var(--color-gray-300);
    border-radius:0 var(--radius-xs) var(--radius-xs) 0;   /* rounded data-end, square at the baseline */
}
.g-bar-on { background:var(--cobalt); }
/* Longer condition names (the competition-type mix) need a wider label column.
   Its bars are scaled so the longest one leaves room for its own value label —
   same zero, same proportion, just a scale that fits. */
.g-bars-wide .g-bar-row { grid-template-columns:minmax(0,12em) 1fr; }
.g-bar-val { white-space:nowrap; }
.g-bar-val { font-size:var(--fs-micro); font-weight:700; color:var(--ink); font-variant-numeric:tabular-nums; }

/* — Stage ladder: height carries the order, one wash carries the rest ———————— */
.g-stairs {
    list-style:none; margin:0; padding:0;
    display:grid; grid-template-columns:repeat(5, 1fr); gap:5px;
    align-items:end; height:132px;
}
.g-stairs li {
    height:var(--h); min-height:38px;
    display:flex; align-items:flex-start; justify-content:center;
    padding:7px 4px 0; text-align:center;
    background:var(--cobalt-wash); border-top:2px solid var(--cobalt);
    border-radius:var(--radius-xs) var(--radius-xs) 0 0;
}
/* 13px floor (DESIGN.md §Named Rules). These are functional labels — the five
   stages a student navigates the year by — and they were set at 12.2px,
   shrinking to 10.9px on a phone, which is where they are read most. */
.g-stairs b { font-size:var(--fs-micro); font-weight:650; color:var(--cobalt-deep); line-height:1.18; }
.g-seasons {
    display:grid; grid-template-columns:2fr 1fr 2fr; gap:var(--space-3xs);
    margin-top:var(--space-2xs); padding-top:var(--space-xs); border-top:1px solid var(--line);
    font-size:var(--fs-micro); color:var(--ink-muted); text-align:center;
}

.guide-cta-row { margin-top:24px; }
.guide-cta-row .btn-all { display:inline-flex; }

.guide-note {
    margin:44px 0 0; padding-top:20px; border-top:1px solid var(--line);
    font-size:var(--fs-label); line-height:1.6; color:var(--ink-muted);
}

@media (max-width:620px){
    /* Flow: two columns, chevrons off — a wrapped row of chevrons points the
       wrong way and reads as noise. */
    .g-flow { grid-template-columns:repeat(2, 1fr); gap:8px; }
    .g-flow li::after { display:none; }
    .g-bar-row { grid-template-columns:minmax(0,7.5em) 1fr; gap:8px; }
    .g-bars-wide .g-bar-row { grid-template-columns:minmax(0,8.5em) 1fr; }
    .g-bar-label { font-size:var(--fs-micro); }
    /* The ladder turns on its side. Five Ukrainian stage names cannot be set
       at the 13px floor in five 61px columns — they hyphenate mid-word
       («Обла-сний») or drop below the floor, which is what they did. As rows,
       each label gets the full width and the ladder still climbs. The seasons
       keep their 2fr/1fr/2fr mapping, now as a rail down the right. */
    /* Scoped to the figure that HOLDS the ladder. Unscoped, this caught every
       `.g-fig` on the page: the session-flow figure became a two-column grid
       too, its <figcaption> was placed in the `auto` track, and at 320px that
       track is 88px wide starting at x=266 — the caption ran 35px past the
       viewport and took the whole page's scrollWidth with it. `:has()` degrades
       safely where it is unsupported: the seasons rail simply stacks under the
       rungs instead of sitting beside them. */
    .g-fig:has(.g-stairs) { display:grid; grid-template-columns:1fr auto; gap:0 10px; }
    .g-stairs { height:auto; gap:4px; grid-template-columns:1fr; align-items:stretch; }
    .g-stairs li {
        height:auto; min-height:0; width:var(--h);
        align-items:center; justify-content:flex-start;
        padding:8px 10px; text-align:left;
        border-top:0; border-left:2px solid var(--cobalt);
        border-radius:0 var(--radius-xs) var(--radius-xs) 0;
    }
    .g-seasons {
        grid-template-columns:none; grid-template-rows:2fr 1fr 2fr;
        margin:0; padding:0 0 0 var(--space-xs); gap:var(--space-3xs);
        border-top:0; border-left:1px solid var(--line);
        align-items:center; text-align:left; writing-mode:horizontal-tb;
    }
    .g-band { font-size:var(--fs-micro); padding:6px; }
}
@media (max-width:560px){
    .guide-inner { padding-top:26px; padding-bottom:52px; }
    .guide-sec { padding-top:28px; }
    .guide-lead { font-size:var(--fs-body); }
    .guide-list { padding-left:1.1em; }
}

/* ── /guide → Apex XY promo (secondary CTA) ──────────────────────────────────
   Subordinate to «Перейти до пошуку задач»: it sits after it, reads as a card
   rather than a page action, and carries exactly ONE button. Four lines tall by
   design — eyebrow, name, one sentence, button — so it never competes with the
   article it follows. Tinted with the cobalt wash so it is visibly an offer and
   not part of the article's argument. It states nothing about a cohort (see the
   markup comment in the shell), so nothing here can go stale.
   ────────────────────────────────────────────────────────────────────────── */
.g-promo {
    margin:34px 0 0; padding:18px 20px 20px;
    background:var(--cobalt-wash); border:1px solid var(--color-primary-200);
    border-radius:var(--radius-lg);
}
.g-promo .g-promo-eyebrow {
    margin:0 0 var(--space-2xs);
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-micro);
    letter-spacing:.01em; text-transform:none; line-height:1.4; color:var(--cobalt-deep);
}
.g-promo-title {
    margin:0 0 6px; font-family:var(--font-display); font-weight:700;
    font-size:var(--fs-read); letter-spacing:-.015em; color:var(--ink);
}
.g-promo-lead { margin:0 0 14px; font-size:var(--fs-label); line-height:1.55; color:var(--ink); }
.g-promo-btn {
    display:inline-flex; align-items:center; gap:9px;
    padding:9px 17px; border-radius:var(--radius-sm);
    background:var(--cobalt); color:var(--card); text-decoration:none;
    font-family:var(--font-sans); font-weight:650; font-size:var(--fs-label);
    transition:background .15s, transform .15s;
}
.g-promo-btn:hover { background:var(--cobalt-deep); }
.g-promo-btn:hover svg { transform:translateX(3px); }
.g-promo-btn svg { transition:transform .15s; }
.g-promo-btn:focus-visible { outline:2px solid var(--cobalt-deep); outline-offset:3px; }

@media (max-width:620px){
    .g-promo { padding:16px 15px 18px; }
    .g-promo-btn { width:100%; justify-content:center; }
}


/* ════════ Additions — «The Ruled Page» pass, 2026-09-20 ═══════════════════ */

/* Screen-reader-only, still focusable. /search had no <h1> at all. */
.visually-hidden {
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}

/* ── Empty state: an exit, not a dead end ─────────────────────────────────── */
.search-empty-actions { margin-top:20px; display:flex; justify-content:center; }
.btn-reset-search {
    display:inline-flex; align-items:center; min-height:44px; padding:12px 22px;
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label);
    color:var(--card); background:var(--cobalt);
    border:1px solid var(--cobalt); border-radius:var(--radius-sm); cursor:pointer;
    transition:background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn-reset-search:hover { background:var(--cobalt-hover); border-color:var(--cobalt-hover); }
.btn-reset-search:focus-visible { outline:2px solid var(--cobalt); outline-offset:3px; }

.search-empty-suggest {
    margin-top:26px; display:flex; flex-wrap:wrap; gap:8px;
    align-items:center; justify-content:center;
}
.search-empty-suggest .lbl { font-size:var(--fs-micro); color:var(--ink-muted); margin-right:2px; }
.suggest-chip {
    display:inline-flex; align-items:center; min-height:36px; padding:8px 14px;
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-micro);
    color:var(--cobalt-deep); background:var(--cobalt-wash);
    border:1px solid transparent; border-radius:var(--radius-full); cursor:pointer;
    font-variant-numeric:tabular-nums;
    transition:border-color var(--dur-fast) var(--ease);
}
.suggest-chip:hover { border-color:var(--cobalt); }
.suggest-chip:focus-visible { outline:2px solid var(--cobalt); outline-offset:3px; }

/* The empty state's heading is set in the display face. */
.search-empty .big { font-family:var(--font-display); font-weight:700; }

/* ── Topic-tree rows reach the 44px floor too ───────────────────────────────
   The floor goes on the ROW alone. On `.tp-pick` as well it was counted twice:
   the label is nested inside `.tp-text`, whose own 4px padding and the row's
   margin sit outside it, so a 44px floor produced a 54px row.

   And a floor on a flex-start row only moves the box — the name and count stay
   at the top of the 44px while `.tp-open`, which is align-self:stretch, centres
   its chevron in the full height. That is the 13px drop that made every arrow in
   the rail sit visibly below its own row. Centre the row's items instead, so the
   taller box lifts the content with it: one alignment for name, count, badge,
   checkbox and chevron. The per-item top offsets that used to hand-align each of
   them to the name's first line are removed with it — they are what centring
   replaces, and left in they would re-introduce the same few-pixel drift. */
.tp-row { min-height:44px; align-items:center; }
.tp-pick { align-items:center; }
.tp-ct { padding-top:0; }
.tp-box { margin-top:0; }

/* …except where the row carries a SUB-LINE. A tree-search hit prints the path
   under the name («Геометрія › Кола»), and a theme under a multi-year lens
   prints its years — so `.tp-text` is a two-part stack, and centring the row
   parks the count and the chevron on the gap between the name and its path
   instead of beside the name they belong to. Those rows go back to
   first-line alignment, which is what the nudges below are for: they are the
   offsets that land each item on the name's first line, and they are correct
   here for exactly the reason they were wrong on a single-line row. */
.tp-row:has(.tp-trail), .tp-row:has(.tp-years) { align-items:flex-start; }
.tp-row:has(.tp-trail) .tp-pick, .tp-row:has(.tp-years) .tp-pick { align-items:flex-start; }
/* No nudge on the box: main's old margin-top:3px was tuned when the checkbox
   was 15px against an 18px line. It is 18px now — the same height as the line —
   so it lands on the line's centre with no offset at all, and the inherited 3px
   only pushed it low. */
.tp-row:has(.tp-trail) .tp-ct, .tp-row:has(.tp-years) .tp-ct { padding-top:5px; }
.tp-row:has(.tp-trail) .tp-open, .tp-row:has(.tp-years) .tp-open { align-self:flex-start; min-height:30px; }
.tp-all { min-height:32px; padding:6px 12px; }
.facet-opt .box, .tp-box { width:18px; height:18px; }

.facet-year-row {
    display:flex; align-items:center; gap:var(--space-2xs);
    font-size:var(--fs-label); color:var(--ink-2);
}
.facet-year-dash { flex:none; color:var(--ink-muted); }
.facet-year-sel {
    flex:1 1 0; min-width:0; min-height:44px; padding:0 var(--space-2xs);
    font-family:var(--font-sans); font-size:var(--fs-label); color:var(--ink);
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-sm);
    cursor:pointer; transition:border-color var(--dur-fast) var(--ease);
}
.facet-year-sel:hover { border-color:var(--ink-muted); }
.facet-year-sel:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }

.facet-more { min-height:44px; padding:var(--space-2xs) 0; }

/* At <=760px the strip below the input reflows into a caption and a row.
   The three items want 372px of a 343px phone: «Фільтри» 114, the count 84,
   the sort 154, plus gaps. They used to wrap wherever they landed, which put
   the sort alone on a third row, right-aligned under a left-aligned count —
   a stranded control, and the loudest thing in the strip despite being the
   least important.

   So the count stops competing for the row. It is status text, not a control,
   and it belongs to the query: it becomes a caption on its own line directly
   under the field that produced it, where it reads as the answer to what was
   just typed. That leaves the two actual controls — filter left, sort right —
   sharing one row at 268 of 343px, the arrangement every mobile search uses.
   Nothing is dropped, no word is abbreviated, and the strip is ~26px shorter
   than the three-row wrap it replaces. */
@media (max-width:760px){
    .search-count { order:2; flex:1 0 100%; margin:0; }
    /* «Шукаю…» replaces the count while a search is in flight, so it takes the
       same caption line — given `0 0 auto` it shared the control row instead
       and stranded the sort on a line of its own again, exactly the layout
       this block exists to remove. Empty, it would still open a line box and
       cost a phantom row, hence the :empty guard. */
    .search-status { order:2; flex:1 0 100%; margin:0; }
    .search-status:empty { display:none; }
    .facet-sheet-toggle { order:3; }
}

/* ── The problem page on a phone ──────────────────────────────────────────
   PRODUCT.md names the phone as the primary device, and at 375px this surface
   was spending 112 of its 375 pixels on chrome — a 28px page gutter and a 28px
   panel padding on each side, 30% of the viewport, before a single character
   of mathematics. The gutter now opens with the viewport (--gutter); the panel
   follows it down. The statement gains about 40px of column, which is a line
   and a half of Ukrainian per paragraph. */
@media (max-width:560px){
    .problem-card-detail .problem-panel { padding:var(--space-ms); border-radius:var(--radius-md); }

    /* An equation is the one thing on the page that cannot reflow, so it gets
       the whole panel — padding included — before it has to scroll. Bleeding
       it into the padding buys it another 40px, and the scroll shadow then
       starts exactly at the panel's inner edge. */
    .problem-card-detail .problem-panel .katex-display {
        margin-inline:calc(var(--space-ms) * -1);
        padding-inline:var(--space-ms);
    }

    /* Two disclosures, one decision. Auto-width buttons stacked at two
       different lengths, which reads as two unrelated controls rather than
       one either/or. */
    .problem-card-detail .detail-actions { flex-direction:column; align-items:stretch; }
    .problem-card-detail .detail-actions > * { justify-content:center; width:100%; }
    /* Full-width buttons take the whole row; the report link wraps under them,
       right-aligned, as before. */
    .problem-card-detail .problem-body > .detail-actions { flex-basis:100%; }
    .problem-card-detail .problem-toolbar { margin-top:var(--space-xs); }
}

/* The footer sat at 12.8px, below the 13px floor, and its separator dot took
   its colour from a BORDER token (--at-warm) — 1.11:1, effectively invisible. */
.site-footer { font-size:var(--fs-micro); }
.site-footer-dot { color:var(--ink-muted); }
/* When the footer wraps — which it does at 375px, where these links are the
   only way to «Як вчитися» and «Про проєкт» — a separator <span> is left
   dangling at the end of a line. Below 560px the wrap itself separates them. */
@media (max-width:560px){
    .site-footer { display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-2xs) var(--space-sm); }
    .site-footer-dot { display:none; }
}
/* Below 560px the header drops «Як вчитися» and «Про проєкт» and the footer is
   where they are reached — so these are primary navigation on a phone, and
   they were 18px-tall targets. Scoped to .site-footer-link only: the «Apex
   Talent» credit is a link inside a sentence, and giving that one a 40px flex
   box breaks the sentence onto its own line. */
.site-footer-link {
    display:inline-flex; align-items:center; min-height:40px; padding:var(--space-2xs) 0;
}

/* ── Result-card metadata ─────────────────────────────────────────────────
   Grade leads: it is the one fact that tells a student whether the problem is
   for them, and it was the fact the card withheld. */
.rcard .rmeta { align-items:center; gap:10px; }
.rcard .rmeta > span { display:inline-flex; align-items:center; }

/* The row does not wrap, and that is what keeps the separators honest.
   A dot between wrapped items has nowhere good to go: as a ::before it led
   the next line («· 1 розв'язок» hanging at the left margin), and moving it
   to ::after only sent it to the end of the previous one («6 клас ·», which
   is what a 375px phone actually showed). CSS cannot see a line break, so the
   fix is to remove the break, not to keep relocating the dot.

   That is also the better caption: three short facts under a card are one
   line of context, not a three-line block repeated down the whole result
   list. Grade and the solution note are fixed — they are short and each is
   a reason to open or skip the problem — so the topic is the only
   item that gives up width, and it is the one already ellipsized upstream
   and shown in full on the problem page. */
.rcard .rmeta { flex-wrap:nowrap; min-width:0; }
.rcard .rmeta > span { flex:none; white-space:nowrap; }
/* Matched through `> span` so it outranks the `flex:none` above it: as a bare
   `.rcard .rmeta-topic` it lost on specificity, the topic never gave up a
   pixel, and the row pushed the whole grid column past the viewport. */
.rcard .rmeta > span.rmeta-topic { flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
/* display:block, because text-overflow does nothing on the inline-flex the
   row's items use: the topic was clipped mid-letter, not ellipsized. Until
   2026-09-21 only cards carrying a difficulty had this (a phone-only rule
   keyed on it); with difficulty off the card it belongs to every topic. */
.rcard .rmeta > span.rmeta-topic { display:block; }
/* With the row fixed to one line the dot goes back on the FOLLOWING item,
   where it cannot be eaten: as an ::after it sat inside the topic's clipped
   box and the ellipsis would have swallowed it. */
.rcard .rmeta > span:not(:first-child)::before {
    content:"·"; margin-right:10px; color:var(--line);
}
.rcard .rmeta-grade { font-weight:600; color:var(--cobalt-deep); }
.rcard .rmeta-topic { color:var(--ink-2); }
/* The rare card with nothing to check yourself against — a warning, so it is
   set in the pencil, not the muted grey it would disappear into. */
.rcard .rmeta-nosol { color:var(--red); }

/* ── The badge hides when there are no filters ────────────────────────────
   `display:inline-grid` beat the [hidden] attribute, so the button read
   «Фільтри 0» on a page with nothing filtered. */
.facet-sheet-toggle .n[hidden] { display:none; }

/* ── Mobile header: the wordmark and a menu ───────────────────────────────
   At 375px the brand and three nav items wrapped to three line-boxes and the
   header grew to 90px. Below 560px the row keeps the wordmark and a menu
   button, and «Усі задачі», «Як вчитися» and «Про проєкт» open in a panel
   under the header (user, 2026-09-21). Before that the row kept «Усі задачі»
   and a phone reached the other two only from the footer, a page away. */
.site-brand { white-space:nowrap; }
.site-nav-links a, .site-nav-links button { white-space:nowrap; }
/* Scoped to the nav so it outranks the coarse-pointer 44px rule below, which
   would otherwise put the button on every touch tablet. */
.site-nav-links .nav-menu, .site-nav-links .nav-menu-panel, .site-nav-links .nav-menu-scrim { display:none; }

@media (max-width:560px){
    .site-header-inner { padding:12px 16px; gap:10px; flex-wrap:nowrap; }
    .site-nav-links > #navGuide,
    .site-nav-links > #navAbout,
    .site-nav-links > #navSearch { display:none; }
    /* Phones keep the 14px caps; only the tile steps down. */
    .site-header .site-brand { gap:10px; }
    .site-brand .brand-mark { width:32px; height:32px; }

    /* Quiet: no box and no wash; the bars turning into the ✕ are the open
       state. 44px square, pulled 10px into the gutter so the bars end about
       as far from the right edge as the brand mark starts from the left. */
    .site-nav-links .nav-menu {
        display:inline-grid; place-items:center; flex:none;
        width:44px; height:44px; min-height:44px; padding:0; margin-right:-10px;
        color:var(--ink-2); background:none; cursor:pointer;
    }
    .site-nav-links .nav-menu:hover { background:none; color:var(--ink); }
    .nav-menu .nav-menu-x, .nav-menu[aria-expanded="true"] .nav-menu-bars { display:none; }
    .nav-menu[aria-expanded="true"] .nav-menu-x { display:inline; }

    /* The panel hangs from the header's hairline, full width, and its rows are
       ruled like the page: a hairline between them and one under the last.
       Behind it, the filter sheet's scrim, which also takes the tap that
       closes the menu, so dismissing it can never open a problem underneath. */
    .site-nav-links .nav-menu-panel {
        display:block; position:absolute; left:0; right:0; top:calc(100% + 1px);
        background:var(--card); border-bottom:1px solid var(--line);
        animation:nav-menu-in var(--dur-sheet) var(--ease);
    }
    .site-nav-links .nav-menu-scrim {
        display:block; position:fixed; inset:var(--header-h) 0 0;
        background:rgba(25,30,54,.42);
        animation:nav-scrim-in var(--dur-sheet) var(--ease);
    }
    .site-nav-links .nav-menu-panel[hidden], .site-nav-links .nav-menu-scrim[hidden] { display:none; }
    .site-nav-links .nav-menu-panel > a, .site-nav-links .nav-menu-panel > button {
        display:flex; align-items:center; gap:12px; width:100%; min-height:52px;
        padding:0 16px; border-radius:0; font-size:var(--fs-body); color:var(--ink);
        text-align:left; cursor:pointer;
    }
    .nav-menu-panel .nav-sym { width:20px; height:20px; color:var(--ink-2); }
    .nav-menu-panel > :hover .nav-sym, .nav-menu-panel > :active .nav-sym { color:inherit; }
    .site-nav-links .nav-menu-panel > * + * { border-top:1px solid var(--line); }
    .site-nav-links .nav-menu-panel > :hover,
    .site-nav-links .nav-menu-panel > :active { background:var(--cobalt-wash); color:var(--cobalt-deep); }
    /* Full-bleed rows: an outset ring would run off both edges of the screen. */
    .site-nav-links .nav-menu-panel > :focus-visible { outline-offset:-3px; }
    /* The page you are on: the selected-row wash (as .ws-variant.is-on), cobalt
       text and glyph, a step of weight. */
    .site-nav-links .nav-menu-panel > a[aria-current="page"] { background:var(--cobalt-wash); color:var(--cobalt-deep); font-weight:600; }
    .nav-menu-panel > a[aria-current="page"] .nav-sym { color:var(--cobalt); }
}
@keyframes nav-menu-in { from { opacity:0; transform:translateY(-4px); } }
@keyframes nav-scrim-in { from { opacity:0; } }
@media (prefers-reduced-motion: reduce) {
    .site-nav-links .nav-menu-panel, .site-nav-links .nav-menu-scrim { animation:none; }
}

/* ── Every target reaches 44px on a thumb ─────────────────────────────────
   DESIGN.md's floor is 44px on a coarse pointer, and these are the controls
   that miss it on a mouse-sized build: measured on a 375px phone the nav CTA
   came out 99×43, the sort select 154×42, «Усі теми» 96×40, the search
   field's clear ✕ 26×26 and the topic chevron 34px wide. They are collected
   here rather than bumped one at a time, so the floor is one rule in one
   place and a new control can be checked against the list.

   Only the pager grows visually; the rest gain hit area the eye never sees,
   because each is either transparent until hover or centred in a box that
   already had the room. The one link deliberately left out is the «Apex
   Talent» credit in the footer sentence — it is running text, not a control,
   and giving it a 44px box breaks the sentence onto its own line. */
@media (pointer:coarse){
    .search-pager .pg { min-width:44px; height:44px; }
    .site-footer-link { min-height:44px; }
    .site-nav-links a, .site-nav-links button { min-height:44px; display:inline-flex; align-items:center; }
    .site-header .site-brand { min-height:44px; }
    .search-box .clear-x { width:44px; height:44px; }
    .tp-all { min-height:44px; }
    .tp-open, .tp-open-slot { width:44px; }
    /* The problem page's own three: «← На головну» came in at 115×31,
       «Копіювати LaTeX» at 148×24 and each topic chip at 298×36. */
    .problem-card-detail .btn-nav { min-height:44px; }
    .problem-card-detail .btn-copy-latex { min-height:44px; }
    .problem-card-detail .topic-item { min-height:44px; }
}

/* ── Math too wide for its column ────────────────────────────────────────────
   KaTeX, not MathJax: `.katex-display` is what js/markdown.js and
   tools/export/render_featured.mjs actually emit, and the `mjx-container` rules
   in styles.v2.css have been inert since the move off MathJax. A display
   formula wider than its card was CLIPPED, not scrolled — the featured card and
   the detail panel both carry overflow:hidden for their rounded corners — so
   the right-hand half of an equation simply vanished, with no scrollbar and no
   sign anything was missing.

   Scrolling `.katex-display` alone does not fix it, which is the trap: KaTeX
   ships `.katex-display > .katex { display:block }`, so as a block it takes
   exactly the container's width, its own nowrap contents spill out of it
   without ever growing the scroll container's scrollWidth, and the scrollbar
   has nothing to scroll. Sized to its content, the overflow becomes scrollable
   overflow. Narrower formulas still centre — the parent's text-align applies to
   an inline-block too. */
.fp-statement .katex-display,
.rcard .rbody .katex-display,
.problem-content .katex-display,
.solutions-content .katex-display,
.solution-content-wrap .katex-display,
.answer-reveal-content .katex-display {
    overflow-x:auto; overflow-y:hidden; padding-bottom:2px;
    max-width:100%; -webkit-overflow-scrolling:touch;
}
.katex-display > .katex { display:inline-block; white-space:nowrap; }

/* Inline math is the harder half, and the more common one: a single `$…$` can
   be 60 characters of `(f(x)+f(z))(f(y)+f(t))=f(xy-zt)+…` with no break
   opportunity anywhere inside it, and KaTeX sets it nowrap. The prose around it
   wraps; the formula does not. Scroll the STATEMENT rather than the formula —
   making `.katex` itself an inline-block scroller would move every inline
   formula's baseline to its bottom margin edge (CSS makes that unconditional
   once overflow is not visible), misaligning thousands of short formulas to fix
   the rare long one. Phone-only: at the full-width column the case does not
   arise. */
@media (max-width:760px){
    .fp-statement,
    .problem-card-detail .problem-content,
    .problem-card-detail .solution-content-wrap,
    .problem-card-detail .answer-reveal-content {
        overflow-x:auto; overflow-y:hidden;
        padding-bottom:3px; -webkit-overflow-scrolling:touch; scrollbar-width:thin;
    }
}
/* Prose (not math) can still hold an unbreakable 40-character token. */
.fp-statement, .rcard .rbody, .problem-content { overflow-wrap:anywhere; }

/* 100vh is the TALLEST the viewport ever gets on iOS/Android — it ignores the
   collapsing URL bar, so a 100vh full-screen dialog runs off the bottom of the
   screen and its «Готово» row is unreachable until the bar hides. dvh tracks
   the visible viewport; the plain-vh line stays first as the fallback. */
@media (max-width:620px){
    .td { max-height:100vh; max-height:100dvh; }
}

/* ── «Складність»: the RUBRIC20 rung, 2026-09-21 ──────────────────────────
   Neutral ink throughout. Difficulty is not colour-coded — no traffic-light
   tiers, and never the red pencil, which belongs to editorial corrections. A
   one-hue ramp was tried and taken out the same day: on the cream page it read
   as a palette the site does not have, and a tinted dot said nothing until its
   legend was learned.

   The per-problem mark, on the problem page only (js/views/difficulty-mark.js):
   the rung over its denominator, and a rule inked up to it — 2px a rung, 40px
   for the scale, so where the problem sits on 1–20 is seen before the number
   is read. Not on result cards: there a bare «13/20» needs the axis named, and
   «Складність» on every card is noise. The rule is a ruled line, so it is
   drawn in --line and filled with the text's own colour; the mark radius on a
   4px bar resolves to a round end. */
.diff-mark { display:inline-flex; align-items:center; gap:6px; font-variant-numeric:tabular-nums; }
.diff-n b { font-weight:600; }
.diff-rule {
    position:relative; display:inline-block; flex:none;
    width:40px; height:4px; border-radius:var(--radius-xs); background:var(--line); overflow:hidden;
}
.diff-rule > i {
    position:absolute; left:0; top:0; bottom:0;
    width:calc(var(--rung, 0) * 2px); background:currentColor; border-radius:var(--radius-xs);
}
/* Forced colours drop author backgrounds; keep the rule a rule. */
@media (forced-colors:active) {
    .diff-rule { forced-color-adjust:none; background:GrayText; }
    .diff-rule > i { background:CanvasText; }
}

/* Facet: a histogram of the twenty rungs with a two-handle range on its
   baseline (js/views/difficulty-range.js). At rest nothing is selected, so the
   bars are neutral ink: a picture of the slice, not a control that is «on».
   Narrowed, the bars inside the range take cobalt — the selected-state colour
   every other facet uses — and the rest fall back to a pale neutral that still
   shows the shape. No ramp: the colour says «selected», never «hard». */
.dr-head {
    display:flex; align-items:center; gap:var(--space-2xs);
    min-height:24px; margin:0 0 var(--space-2xs);
}
/* The group heading sits in a row now, so it is not a direct child of
   .facet-group and the rail's h4 rule misses it; restate it exactly. */
.facet-group.dr > .dr-head > h4 {
    margin:0; font-family:var(--font-sans); font-weight:600;
    font-size:var(--fs-micro); line-height:1.3; letter-spacing:.06em;
    text-transform:uppercase; color:var(--ink-2);
}
/* One line, always: a second line here grows the facet and moves everything
   under it. rangeText keeps its copy inside the slot; the ellipsis is the net
   for copy that outgrows it. The line box is 16px, not 1em, because the clip
   would otherwise shave the comma tails of a «3, 7, 9». */
.dr-value {
    margin-left:auto; font-size:var(--fs-label); font-weight:600; line-height:16px;
    color:var(--cobalt-deep); font-variant-numeric:tabular-nums;
    min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.dr-clear {
    flex:none; position:relative; width:24px; height:24px; padding:0; margin:0 -4px 0 0;
    display:inline-flex; align-items:center; justify-content:center;
    border:0; border-radius:var(--radius-xs); background:transparent; color:var(--ink-2); cursor:pointer;
    transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.dr-clear:hover { background:var(--cobalt-wash); color:var(--ink); }
.dr-clear:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }
.dr-clear[hidden] { display:none; }

/* (i) beside the heading: a quiet glyph at the text floor's colour, so it
   reads as part of the label rather than a second control competing with the
   value. Open, it takes the selected-state cobalt. */
.dr-info {
    flex:none; position:relative; width:24px; height:24px; padding:0; margin:0 0 0 -4px;
    display:inline-flex; align-items:center; justify-content:center;
    border:0; border-radius:var(--radius-xs); background:transparent; color:var(--ink-muted); cursor:pointer;
    transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.dr-info:hover { background:var(--cobalt-wash); color:var(--ink); }
.dr-info[aria-expanded="true"] { color:var(--cobalt); }
.dr-info:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }

/* The note it opens sits inline under the heading and pushes the plot down —
   no popover, so no shadow: a raised surface here is the card step and a
   hairline, as everywhere else. The four levels are a run-in glossary, the
   name in ink so the list scans at the 13px floor; the box bleeds 9px past
   the column, like the tier rows' beds, so its text lines up with the
   heading's. */
.dr-about {
    margin:var(--space-3xs) -9px var(--space-xs); padding:9px 8px;
    background:var(--card); border:1px solid var(--line); border-radius:var(--radius-sm);
    font-size:var(--fs-micro); line-height:1.45; color:var(--ink-2);
}
.dr-about[hidden] { display:none; }
.dr-about p { margin:0; }
.dr-levels { margin:6px 0 0; }
.dr-levels > div + div { margin-top:4px; }
.dr-levels dt { display:inline; font-weight:600; color:var(--ink); }
.dr-levels-range { font-weight:400; color:var(--ink-muted); font-variant-numeric:tabular-nums; }
/* The dash rides on the range with a no-break space, so no line opens on it. */
.dr-levels dd { display:inline; margin:0; }
.dr-levels dd::before { content:"\00a0— "; }
.dr-about .dr-about-foot { margin-top:6px; color:var(--ink-muted); }

/* The plot: 64px of bars, the rail on their baseline, the handles centred on
   the rail, the tick numbers under it. The 12px side inset is half a handle,
   so a handle parked at either end of the scale stays inside the rail. */
.dr-plot {
    position:relative; height:100px; padding:0 12px; margin:0 -4px;
    touch-action:pan-y; user-select:none; -webkit-user-select:none; cursor:pointer;
}
.dr-bars { display:flex; align-items:flex-end; gap:3px; height:64px; }
.dr-bar { flex:1 1 0; min-width:0; height:100%; display:flex; align-items:flex-end; }
/* A tier boundary is a slightly wider gap, so the bars read as four groups
   — the same four the rows below name. */
.dr-bar.is-tier-start { margin-left:5px; }
.dr-bar > i {
    display:block; width:100%; height:100%; background:var(--ink-3);
    transform:scaleY(0); transform-origin:50% 100%;
    transition:transform 240ms var(--ease), background-color var(--dur-fast) var(--ease);
}
.dr-bar.is-hover > i { background:var(--ink-2); }
.dr.is-narrowed .dr-bar > i { background:var(--color-gray-300); }
.dr.is-narrowed .dr-bar.is-hover > i { background:var(--ink-3); }
.dr.is-narrowed .dr-bar.is-in > i { background:var(--cobalt); }
.dr.is-narrowed .dr-bar.is-in.is-hover > i { background:var(--cobalt-hover); }

.dr-rail { position:absolute; left:12px; right:12px; top:64px; height:1px; background:var(--ink-3); }
.dr.is-narrowed .dr-rail { background:var(--line); }
.dr-fill { position:absolute; top:-1px; height:3px; background:var(--cobalt); display:none; }
.dr.is-narrowed .dr-fill { display:block; }

/* Handles: drawn, not rendered — card fill and a 1px ink edge, and no shadow
   at rest or in hand. They respond in colour: a cobalt edge on hover, a
   heavier one while held. */
.dr-thumb {
    position:absolute; top:64px; left:0; width:20px; height:20px; margin:-10px 0 0 -10px;
    box-sizing:border-box; border-radius:50%;
    background:var(--card); border:1px solid var(--ink); cursor:grab; touch-action:none;
    transition:left var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
    z-index:1;
}
.dr-thumb::before { content:""; position:absolute; inset:-8px; border-radius:50%; }
.dr-thumb:hover { border-color:var(--cobalt); background:var(--cobalt-wash); }
.dr-thumb.is-active { border:2px solid var(--cobalt); background:var(--card); cursor:grabbing; z-index:2; }
.dr-thumb:focus-visible { outline:2px solid var(--cobalt); outline-offset:3px; z-index:2; }
.dr.is-dragging .dr-thumb, .dr.is-dragging .dr-fill,
.dr.is-measuring .dr-thumb { transition:none; }

.dr-ticks > span {
    position:absolute; top:80px; transform:translateX(-50%);
    font-size:var(--fs-micro); line-height:16px; color:var(--ink-muted);
    font-variant-numeric:tabular-nums; pointer-events:none;
}

/* A peek at one bar under the mouse: its rung and count on the tick row,
   under the bar, while the tick numbers step aside (js hoverTip). One line in
   a row that is always there, so it can never change the facet's height. */
.dr-ticks > .dr-peek { display:none; transform:none; white-space:nowrap; font-weight:500; color:var(--ink-2); }
.dr.is-peeking .dr-ticks > .dr-peek { display:block; }
.dr.is-peeking .dr-ticks > span:not(.dr-peek) { visibility:hidden; }

/* Tier rows are presets, not checkboxes: one press sets the range to the
   band, a second press on the same band clears it. So they carry no box, and
   the pressed one wears the selected-row bed every facet row uses. */
.dr-tiers { display:flex; flex-direction:column; margin-top:var(--space-3xs); }
.dr-tier {
    display:flex; align-items:center; gap:var(--space-2xs);
    width:calc(100% + 16px); min-height:36px; padding:4px 8px; margin:0 -8px;
    border:0; border-radius:var(--radius-sm); background:none; cursor:pointer;
    font-family:var(--font-sans); font-size:var(--fs-label); line-height:1.3; color:var(--ink); text-align:left;
    transition:background var(--dur-fast) var(--ease);
}
.dr-tier:hover { background:var(--cobalt-wash); }
.dr-tier:focus-visible { outline:2px solid var(--cobalt); outline-offset:-2px; }
.dr-tier[aria-pressed="true"] { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.dr-tier[aria-pressed="true"] .dr-tier-name,
.dr-tier.is-covered .dr-tier-name { font-weight:600; }
.dr-tier-range { font-size:var(--fs-micro); color:var(--ink-muted); font-variant-numeric:tabular-nums; }
.dr-tier .ct { margin-left:auto; font-size:var(--fs-micro); color:var(--ink-muted); font-variant-numeric:tabular-nums; }

/* Fingers: bigger handles, a 44px hit on every one of them, 44px rows. */
@media (pointer:coarse) {
    .dr-thumb { width:24px; height:24px; margin:-12px 0 0 -12px; }
    .dr-thumb::before { inset:-10px; }
    .dr-clear::before, .dr-info::before { content:""; position:absolute; inset:-10px; }
    .dr-tier { min-height:44px; }
}

@media (prefers-reduced-motion:reduce) {
    .dr-bar > i, .dr-thumb, .dr-fill, .dr-tier, .dr-clear, .dr-info { transition:none; }
}

/* Problem page: the axis named in the muted voice, the value in the chip's. */
.problem-card-detail .problem-meta .meta-badge--difficulty { font-variant-numeric:tabular-nums; }
.meta-badge--difficulty .meta-badge-pre { color:var(--ink-muted); }
/* The rung leads; the tier word after the rule is the gloss, in the muted voice. */
.meta-badge--difficulty .diff-mark { margin-left:2px; }
.meta-badge--difficulty .diff-n { color:var(--ink-muted); }
.meta-badge--difficulty .diff-n b { color:var(--ink); }
.meta-badge--difficulty .meta-badge-word { margin-left:2px; color:var(--ink-muted); }

/* ════════ 404 — «Сторінку не знайдено» (js/views/notfound.js) ═════════════
   One sheet, deliberately unlike the problem page: no panel, no chips, no
   disclosures. The клітинка ruling covers the view and fades out at the
   edges; the blue pen writes «√404?» on it (the brand's √, its bar over the
   numerals, curling on into a question mark), and the olympiad's driest
   answer sits in plain view. The figure is aria-hidden, like the brand mark,
   so its numerals sit outside the type ramp. Cobalt only where the pen acts;
   no red: the pencil is for the archive's corrections alone. */
#notFoundView { position:relative; overflow:hidden; }
#notFoundView::before {
    content:""; position:absolute; inset:0; pointer-events:none;
    background-image:
        linear-gradient(rgba(44,70,216,.07) 1px,transparent 1px),
        linear-gradient(90deg,rgba(44,70,216,.07) 1px,transparent 1px);
    background-size:23px 23px; background-position:center -1px;
    -webkit-mask-image:radial-gradient(ellipse 70% 60% at 50% 38%, #000 30%, transparent 75%);
    mask-image:radial-gradient(ellipse 70% 60% at 50% 38%, #000 30%, transparent 75%);
}
.nf-inner {
    position:relative; display:flex; justify-content:center;
    min-height:calc(100svh - var(--header-h, 72px) - 80px);
    padding-top:var(--space-2xl); padding-bottom:var(--space-3xl);
}
.nf-sheet { width:100%; max-width:560px; text-align:center; }
/* «√404?»: the figure is an SVG in viewBox units, so the numerals and the
   pen's line scale together. */
.nf-pen { display:block; width:min(100%, 440px); height:auto; margin:0 auto; overflow:visible; }
.nf-pen-404 { font-family:var(--font-math); font-size:150px; fill:var(--ink); letter-spacing:-.02em; }
.nf-pen-line { fill:none; stroke:var(--cobalt); stroke-width:4.5; stroke-linecap:round; stroke-linejoin:round; }
.nf-pen-dot { fill:var(--cobalt); }
.nf-pen-nib { fill:var(--cobalt-deep); }
.nf-title {
    margin:var(--space-lg) 0 0; font-family:var(--font-display); font-weight:600;
    font-size:var(--fs-title); line-height:1.08; letter-spacing:-.01em; color:var(--ink); text-wrap:balance;
}
.nf-answer {
    margin:var(--space-sm) 0 0; font-family:var(--font-math); font-size:var(--fs-math);
    line-height:1.5; color:var(--ink); -webkit-text-stroke-width:.02em;
}
.nf-answer b { font-weight:400; font-style:italic; }
.nf-qed {
    display:inline-block; width:.5em; height:.5em; margin-left:.55em; background:var(--ink);
    vertical-align:-.02em;
}
.nf-why {
    margin:var(--space-md) auto 0; max-width:46ch; min-height:3.1em;
    font-family:var(--font-sans); font-size:var(--fs-body); line-height:1.55; color:var(--ink-2); text-wrap:pretty;
}
.nf-code {
    font-family:var(--font-sans); font-weight:500; font-size:var(--fs-label); letter-spacing:.03em;
    font-variant-numeric:tabular-nums; color:var(--ink); background:var(--card);
    border:1px solid var(--line); border-radius:var(--radius-xs); padding:1px 6px;
    overflow-wrap:anywhere; box-decoration-break:clone; -webkit-box-decoration-break:clone;
}
.nf-search { margin:var(--space-lg) auto 0; text-align:left; }
.nf-actions {
    display:flex; flex-direction:column; align-items:center; gap:var(--space-sm);
    margin-top:var(--space-ms);
}
.nf-near-slot { max-width:100%; min-width:0; }
.nf-near {
    display:inline-flex; align-items:center; gap:var(--space-2xs); max-width:100%; min-height:44px;
    font-family:var(--font-sans); font-size:var(--fs-label); color:var(--ink-2); text-decoration:none;
}
.nf-near-k { flex:none; }
.nf-near .nf-code { flex:none; }
.nf-near-t {
    min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    font-family:var(--font-math); font-size:var(--fs-read); color:var(--ink); -webkit-text-stroke-width:.02em;
}
.nf-near svg { flex:none; color:var(--ink-muted); transition:transform var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
.nf-near:hover .nf-near-t { color:var(--cobalt-deep); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; }
.nf-near:hover svg { color:var(--cobalt-deep); transform:translateX(3px); }

/* The one authored moment: the pen writes √, the bar, the hook (nib riding
   the tip), the dot lands, then the tombstone after the answer. The stroke's
   easing matches the nib's keySplines. Reduced motion gets the finished
   figure and no nib. */
.nf-pen--draw .nf-pen-line { stroke-dasharray:1; stroke-dashoffset:1; animation:nf-write 1.9s cubic-bezier(.45,0,.25,1) .25s forwards; }
.nf-pen--draw .nf-pen-nib { animation:nf-lift 240ms ease-out 2.15s forwards; }
.nf-pen--draw .nf-pen-dot { transform-box:fill-box; transform-origin:center; animation:nf-dot 320ms cubic-bezier(.16,1,.3,1) 2.3s both; }
.nf-anim .nf-qed { animation:nf-qed 380ms cubic-bezier(.16,1,.3,1) 2.6s both; }
@keyframes nf-write { to { stroke-dashoffset:0; } }
@keyframes nf-lift { to { opacity:0; } }
@keyframes nf-dot { from { transform:scale(0); } to { transform:none; } }
@keyframes nf-qed { from { transform:scale(0) rotate(-45deg); } to { transform:none; } }

@media (max-width:560px){
    .nf-inner { padding-top:var(--space-ml); }
    .nf-title { margin-top:var(--space-md); }
    .nf-near-k { display:none; }
}

/* ============================================================================
   «Про проєкт» (/about, 2026-09-30)
   The guide's reading column and voice, for a page that answers «what is this
   and can I trust it» and then hands the reader to the archive. Its one
   own-world move is the contents («Що в архіві»): a notebook's зміст, the
   competition name, a dotted leader and the live count, one ruled row each,
   and every row a link into that competition. No grid (nothing here is
   mathematics), no cards, no red. The figures are live (js/views/about.js):
   a count that has not arrived is simply absent, and its leader with it.
   ============================================================================ */
.about-inner { padding-top:var(--space-ml); padding-bottom:72px; }
.about { max-width:68ch; margin:0 auto; }

.about-head { padding-bottom:var(--space-md); border-bottom:1px solid var(--line); }
.about-head h1 {
    margin:0; font-family:var(--font-display); font-weight:700;
    font-size:var(--fs-title); line-height:1.14; letter-spacing:-.018em;
    color:var(--ink); text-wrap:balance;
}
.about .about-lead { margin:18px 0 0; font-size:var(--fs-read); line-height:1.62; color:var(--ink-2); }
.about .about-facts { margin:var(--space-xs) 0 0; font-size:var(--fs-read); line-height:1.62; color:var(--ink-2); }
.about-facts b, .about-sec b { font-weight:650; color:var(--ink); font-variant-numeric:tabular-nums; }
.about-years { font-variant-numeric:tabular-nums; }
.about .about-cta {
    display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-xs) var(--space-md);
    margin:var(--space-md) 0 0;
}
.about-cta .btn-all { padding:12px 22px; }

.about-sec { padding-top:var(--space-ml); }
.about h2 {
    margin:0 0 14px; font-family:var(--font-display); font-weight:700;
    font-size:var(--fs-lead); line-height:1.25; letter-spacing:-.012em; color:var(--ink);
}
.about p { margin:0 0 15px; font-size:var(--fs-body); line-height:1.68; color:var(--ink); }
.about p:last-child { margin-bottom:0; }

/* In-text links: the guide's treatment, so the two pages read as one family. */
.about a:not(.btn-all) {
    color:var(--cobalt-deep); text-decoration:underline; text-underline-offset:2px;
    text-decoration-thickness:1px; text-decoration-color:var(--color-primary-200);
    transition:color var(--dur-fast) var(--ease), text-decoration-color var(--dur-fast) var(--ease);
}
.about a:not(.btn-all):hover { color:var(--cobalt); text-decoration-color:currentColor; }

/* ── The contents ─────────────────────────────────────────────────────────
   Rows are ruled like the phone menu and the page: a hairline above each,
   one under the last. The name wraps (the Ляшко tournament's is 67
   characters) and gives way first; the leader keeps at least 1.5em and sits
   on the last line; the count never wraps. The whole row is the link and the 44px
   target, so the underline treatment above is switched off here: hover moves
   the name and the dots to cobalt instead. */
.about-toc { margin-top:var(--space-md); }
.about-toc h3 {
    margin:var(--space-md) 0 var(--space-2xs); font-family:var(--font-sans); font-weight:700;
    font-size:var(--fs-body); line-height:1.4; color:var(--cobalt-deep);
}
.about-toc h3:first-child { margin-top:0; }
.about-toc ul { margin:0; padding:0; list-style:none; border-bottom:1px solid var(--line); }
.about-toc li { border-top:1px solid var(--line); }
.about .about-toc li > a {
    display:flex; align-items:flex-end; gap:0 var(--space-2xs);
    min-height:44px; padding:10px 0; color:var(--ink); text-decoration:none;
}
.about-toc-name { flex:0 1 auto; min-width:0; font-size:var(--fs-body); line-height:1.45; }
.about-toc-lead {
    flex:1 0 1.5em; height:2px; margin-bottom:.42em;
    background:radial-gradient(circle, var(--ink-3) 0 .9px, transparent 1.2px) 0 50% / 6px 2px repeat-x;
    transition:opacity var(--dur-fast) var(--ease);
}
.about-toc .about-n {
    flex:none; font-size:var(--fs-label); line-height:1.6; color:var(--ink-2);
    font-variant-numeric:tabular-nums; white-space:nowrap; text-align:right;
}
.about-toc li > a:has(.about-n:empty) .about-toc-lead { visibility:hidden; }
.about .about-toc li > a:hover .about-toc-name { color:var(--cobalt-deep); }
.about .about-toc li > a:hover .about-toc-lead {
    background-image:radial-gradient(circle, var(--cobalt) 0 .9px, transparent 1.2px);
}
.about .about-toc li > a:hover .about-n { color:var(--cobalt-deep); }
.about .about-toc li > a:focus-visible { outline-offset:-2px; border-radius:var(--radius-xs); }
.about .about-toc-more { margin-top:var(--space-xs); font-size:var(--fs-label); color:var(--ink-2); }

/* ── What every problem carries ───────────────────────────────────────────
   Term beside definition on a desktop, each pair ruled; stacked on a phone. */
.about-dl {
    display:grid; grid-template-columns:minmax(0, 13em) minmax(0, 1fr); gap:0 var(--space-md);
    margin:0; border-bottom:1px solid var(--line);
}
.about-dl dt, .about-dl dd { margin:0; padding:12px 0; border-top:1px solid var(--line); }
.about-dl dt { font-weight:650; font-size:var(--fs-body); line-height:1.5; color:var(--ink); }
.about-dl dd { font-size:var(--fs-body); line-height:1.62; color:var(--ink); }
/* The public ID in the locator voice it has under a problem's title. */
.about-code {
    font-weight:500; letter-spacing:.03em; font-variant-numeric:tabular-nums;
    white-space:nowrap; color:var(--ink);
}

.about-links { margin:0 0 15px; padding-left:1.25em; }
.about-links li { margin-bottom:8px; font-size:var(--fs-body); line-height:1.62; color:var(--ink); }
.about-links li::marker { color:var(--ink-muted); }

@media (max-width:620px){
    .about-dl { grid-template-columns:minmax(0, 1fr); }
    .about-dl dd { border-top:0; padding-top:0; }
}
@media (max-width:560px){
    .about-inner { padding-top:26px; padding-bottom:52px; }
    .about-sec { padding-top:var(--space-lg); }
    .about .about-lead, .about .about-facts { font-size:var(--fs-body); }
    .about-links { padding-left:1.1em; }
    .about-cta .btn-all { flex:1 1 auto; justify-content:center; }
}

/* ══ «Добірка»: the teacher's printable set (js/views/worksheet.js) ═══════
   Every class is `ws-`: the legacy print rules hide `[class*="btn-"]` and
   `.solutions-content`, and this sheet must print with its solutions. */

/* The bookmark of a problem, in the top-right corner of a result card and of
   the problem page (user 2026-10-05): outlined = not in the set, filled = in. */
.ws-add {
    display:inline-flex; align-items:center; justify-content:center; cursor:pointer;
    width:32px; height:32px; padding:0; border-radius:var(--radius-sm);
    border:1px solid transparent; background:none; color:var(--ink-muted);
    transition:border-color .15s, color .15s, background .15s;
}
.ws-add:hover { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
.ws-add.is-on { color:var(--cobalt); }
.ws-add .ws-mark { flex:none; }
/* Into the set, the ribbon drops into the bookmark from its top edge and the
   bookmark swings on it, twice, smaller each time, like a tag; a ring of pen
   strokes flicks out round it (util/worksheet-toggle.js burst). Out of the set,
   the ribbon goes back up, quicker, and nothing else. The fill is its own layer
   (MARK), so any repaint, a second tab's included, animates rather than swaps. */
.ws-mark-ink { transform-box:fill-box; transform-origin:50% 0; transform:scaleY(0); transition:transform 140ms cubic-bezier(.4,0,1,1); }
.ws-add.is-on .ws-mark-ink { transform:none; transition:transform 380ms cubic-bezier(.16,1,.3,1); }
.ws-add.ws-bump svg.ws-mark { animation:ws-swing 900ms cubic-bezier(.3,0,.3,1); transform-origin:50% 12%; }
@keyframes ws-swing {
    0%   { transform:none; }
    14%  { transform:translateY(1.5px) scaleY(1.06); }
    32%  { transform:rotate(-11deg); }
    52%  { transform:rotate(7deg); }
    70%  { transform:rotate(-3deg); }
    86%  { transform:rotate(1deg); }
    100% { transform:none; }
}
.ws-burst { position:absolute; inset:-30%; pointer-events:none; color:var(--cobalt); }
.ws-burst svg { display:block; width:100%; height:100%; overflow:visible; }
.ws-burst path { stroke-dasharray:1 1; stroke-dashoffset:1; animation:ws-ray 560ms cubic-bezier(.16,1,.3,1) 90ms forwards; }
@keyframes ws-ray {
    0%   { stroke-dashoffset:1; opacity:1; }
    45%  { stroke-dashoffset:0; opacity:1; }
    100% { stroke-dashoffset:-.98; opacity:0; }
}
#navWorksheet.ws-bump .ws-nav-count, .nav-menu.ws-bump::after { animation:ws-count 440ms cubic-bezier(.16,1,.3,1); }
@keyframes ws-count { 0% { transform:scale(.5); } 35% { transform:scale(1.3); } 100% { transform:none; } }
/* Still: no drop, swing or strokes (the script skips them); the ribbon simply fills. */
@media (prefers-reduced-motion:reduce){
    .ws-mark-ink { transform:none; opacity:0; transition:opacity 120ms linear; }
    .ws-add.is-on .ws-mark-ink { transform:none; opacity:1; transition:opacity 120ms linear; }
    .ws-add.ws-bump svg.ws-mark, #navWorksheet.ws-bump .ws-nav-count, .nav-menu.ws-bump::after { animation:none; }
    .ws-burst { display:none; }
}
/* A card is one link, so the button is its sibling: a zero-height holder before
   the card, the bookmark drawn over the card's top-right corner. The source
   line (and the code at its right end) keeps clear of it. */
.rcard-ws { position:relative; height:0; z-index:1; }
.rcard-ws .ws-add { position:absolute; right:calc(var(--space-ms) - 8px); top:calc(var(--space-ms) - 9px); width:40px; height:40px; color:var(--ink-2); }
.rcard-ws .ws-add .ws-mark { width:24px; height:24px; }
.rcard-ws .ws-add.is-on { color:var(--cobalt); }
.rcard.has-ws-add .rcard-top { padding-right:42px; min-height:24px; }
.rcard.has-ws-add:not(:has(.rcard-top)) h3 { padding-right:42px; }
/* The bookmark has the top-right corner, so on /search the code moves to the
   bottom-right one, on the meta line (user 2026-10-05). Position only: the
   markup is search/listing.js, shared with the proxy's first page. */
/* /search prints the whole statement (user 2026-10-05): no three-line clamp. */
#searchResults > .rcard .rbody { display:block; -webkit-line-clamp:none; line-clamp:none; overflow:visible; }
#searchResults > .rcard:has(> .rmeta) { position:relative; }
#searchResults > .rcard:has(> .rmeta) .rcard-pid { position:absolute; right:var(--space-ms); bottom:var(--space-ms); line-height:1.35; }
#searchResults > .rcard:has(> .rmeta) .rmeta { padding-right:136px; }
/* Problem page: the same bookmark, level with the title. */
.problem-card-detail .problem-header { position:relative; }
.problem-card-detail .problem-header:has(> .ws-add) .problem-title { padding-right:48px; }
.problem-card-detail .problem-header > .ws-add { position:absolute; top:-4px; right:-6px; width:40px; height:40px; }
.problem-card-detail .problem-header > .ws-add .ws-mark { width:22px; height:22px; }
/* Desktop: beside a display-size title the mark is drawn to match it, in ink
   and with a border, not as a faint corner glyph (user 2026-10-05). */
@media (min-width:761px){
    .problem-card-detail .problem-header:has(> .ws-add) .problem-title { padding-right:72px; }
    .problem-card-detail .problem-header > .ws-add { top:0; right:0; width:52px; height:52px; color:var(--ink); border-color:var(--line); background:var(--card); border-radius:var(--radius-md); }
    .problem-card-detail .problem-header > .ws-add .ws-mark { width:28px; height:28px; }
    .problem-card-detail .problem-header > .ws-add:hover { border-color:var(--cobalt); background:var(--cobalt-wash); color:var(--cobalt-deep); }
    .problem-card-detail .problem-header > .ws-add.is-on { color:var(--cobalt); border-color:var(--cobalt); background:var(--cobalt-wash); }
}
/* A finger gets 44px whatever the drawn size. */
@media (hover:none) { .ws-add::after { content:""; position:absolute; inset:-2px; } .problem-card-detail .problem-header > .ws-add::after { inset:-2px; } }
/* Copy-LaTeX and the report link, quiet: on the reveal button's own line on a
   desktop (user 2026-10-05), under an opened solution, and a right-aligned
   column closing the card on a phone. */
.problem-toolbar-quiet { display:inline-flex; align-items:center; flex-wrap:wrap; justify-content:flex-end; gap:6px; margin-left:auto; }
.problem-card-detail .problem-toolbar .btn-copy-latex.compact { padding:4px 8px; font-size:var(--fs-micro); }
@media (max-width:560px){
    .problem-toolbar-quiet { flex-direction:column; align-items:flex-end; gap:0; }
}
/* Phone: the set's link is inside ☰, so the button says the set is not empty. */
.site-nav-links .nav-menu { position:relative; }
.nav-menu.has-ws::after {
    content:""; position:absolute; top:9px; right:7px; width:8px; height:8px; border-radius:50%;
    background:var(--cobalt); box-shadow:0 0 0 2px var(--ground);
}
.nav-menu.has-ws[aria-expanded="true"]::after { display:none; }
.toast a.toast-action { text-decoration:none; }
/* left:50% halves the room a toast may take, so a short one wrapped on a phone. */
.toast-container { width:max-content; max-width:calc(100vw - 2 * var(--gutter)); align-items:center; }

/* «Про задачу»: a value that is a facet is a quiet link into /search. */
.problem-card-detail .about-link {
    color:var(--ink); text-decoration:underline; text-decoration-color:var(--line-strong, var(--line));
    text-decoration-thickness:1px; text-underline-offset:4px; transition:color .15s, text-decoration-color .15s;
}
.problem-card-detail .about-link:hover { color:var(--cobalt-deep); text-decoration-color:var(--cobalt); }
.problem-card-detail .about-link:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; border-radius:2px; }
.problem-card-detail .about-link .cflag { display:inline-block; margin-right:.4em; text-decoration:none; }   /* no rule under the flag */
/* Phone: the title steps down, and the code keeps a 44px tap area without
   taking the height. */
@media (max-width:560px){
    .problem-card-detail .problem-title { font-size:22px; line-height:1.14; }
    .problem-card-detail .problem-pid { position:relative; }
    .problem-card-detail .problem-pid::after { content:""; position:absolute; inset:-10px -6px; }
    .problem-card-detail .problem-meta { margin-top:10px; }
    .problem-card-detail .problem-header { padding-bottom:14px; }
}

/* A toast's one action (undo). The toast itself in the system's own ink and
   card, on the label step (it was v2's slate on pure white at 14.08px), with
   the overlay shadow: it is a response, so it may cast one. */
.toast {
    display:flex; align-items:center; gap:14px;
    background:var(--ink); color:var(--card); font-size:var(--fs-label); line-height:1.4;
    border-radius:var(--radius-sm); box-shadow:var(--shadow-overlay);
}
.toast-action {
    border:0; background:none; cursor:pointer; padding:2px 0; color:var(--card); font:inherit; font-weight:700;
    text-decoration:underline; text-underline-offset:3px;
}
.toast-action:focus-visible { outline:2px solid var(--card); outline-offset:3px; border-radius:2px; }

/* A solution's «- » case list (markdown.js, opts.lists): a hanging en dash,
   as the books set it, and a little air between cases that run to several lines. */
.problem-card-detail .md-list { list-style:none; margin:0 0 var(--space-sm); padding-left:1.25em; }
.problem-card-detail .md-list:last-child { margin-bottom:0; }
.problem-card-detail .md-list > li { position:relative; }
.problem-card-detail .md-list > li + li { margin-top:var(--space-2xs); }
.problem-card-detail .md-list > li::before { content:"–"; position:absolute; left:-1.25em; width:1em; text-align:center; color:var(--ink-2); }
/* A short formula and the stop or comma after it never part (util/math-glue.js). */
.problem-card-detail .math-glue { white-space:nowrap; }
/* «Сховати розв’язок» at the end of a long solution: the open toggle's own
   drawn look, so it reads as the same control. Full width on a phone, as the
   toggle is. */
.problem-card-detail .solutions-collapse {
    display:inline-flex; align-items:center; justify-content:center; margin-top:var(--space-md);
    background:var(--card); color:var(--cobalt-deep); border:1.5px solid var(--cobalt); border-radius:var(--radius-sm);
    padding:12px 22px; font-family:var(--font-sans); font-weight:600; font-size:var(--fs-body); cursor:pointer;
    transition:background .15s;
}
.problem-card-detail .solutions-collapse:hover { background:var(--cobalt-wash); }
.problem-card-detail .solutions-collapse[hidden] { display:none; }
@media (max-width:560px){ .problem-card-detail .solutions-collapse { width:100%; } }
@media print { .problem-card-detail .solutions-collapse { display:none !important; } }

/* Header link, shown while the set holds something. */
.ws-nav-count[hidden] { display:none; }
/* «Добірка» is always in the header (user 2026-10-05): between the phone menu
   and a full-width window the four links fit without their glyphs. */
@media (min-width:561px) and (max-width:720px){
    .site-nav-links > a .nav-sym { display:none; }
    .site-nav-links { gap:0; }
}
@media (min-width:561px) and (max-width:620px){
    .site-nav-links > a:not(.nav-cta) { padding-left:6px; padding-right:6px; }
}
.ws-nav-count {
    display:inline-grid; place-items:center; min-width:20px; height:20px; padding:0 6px;
    border-radius:var(--radius-full); background:var(--cobalt); color:var(--card);
    font-size:12px; font-weight:700; font-variant-numeric:tabular-nums; line-height:1;
}
@media (max-width:560px){
    .site-nav-links > #navWorksheet { display:none; }
    .site-nav-links .nav-menu-panel > a[data-ws-nav] .ws-nav-count { margin-left:auto; }
}

/* The page: a toolbar that stays under the header while the sheet scrolls,
   then the sheet as it will print. The bar starts right under the header, where
   it also sticks (no .view top padding above it). */
#worksheetView:has(> .ws-bar) { padding-top:0; }
.ws-bar {
    position:sticky; top:var(--header-h, 65px); z-index:5;
    background:var(--ground); border-bottom:1px solid var(--line);
}
.ws-bar-inner {
    max-width:calc(820px + 2 * var(--gutter)); margin:0 auto; padding:var(--space-xs) var(--gutter);
    display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-2xs) 8px;
}
.ws-head { display:flex; align-items:baseline; gap:10px; margin-right:auto; min-width:0; }
.ws-page-title svg { flex:none; width:.9em; height:.9em; color:var(--cobalt); }
.ws-empty-mark { display:inline-flex; vertical-align:-3px; color:var(--ink); }
.ws-page-title { display:inline-flex; align-items:center; gap:.4em; font-family:var(--font-display); font-weight:600; font-size:var(--fs-lead); letter-spacing:-.01em; line-height:1.2; margin:0; color:var(--ink); }
.ws-count { font-size:var(--fs-label); color:var(--ink-muted); font-variant-numeric:tabular-nums; white-space:nowrap; }
/* Three states of one sheet. The chosen one is the selected-row wash; the only
   filled cobalt in the bar is the action. */
.ws-variants { display:inline-flex; padding:3px; gap:2px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--card); }
.ws-variant {
    border:0; background:none; cursor:pointer; padding:0 10px; min-height:34px; border-radius:5px;
    font-family:var(--font-sans); font-size:var(--fs-label); font-weight:600; color:var(--ink-2); white-space:nowrap;
    transition:background .15s, color .15s;
}
.ws-variant:hover { color:var(--ink); }
.ws-variant.is-on { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.ws-variant:focus-visible, .ws-print:focus-visible, .ws-word:focus-visible, .ws-clear:focus-visible, .ws-tool:focus-visible, .ws-add:focus-visible, .ws-empty-link:focus-visible { outline:2px solid var(--cobalt); outline-offset:2px; }
.ws-actions { position:relative; display:inline-flex; align-items:center; gap:var(--space-2xs); margin-left:auto; }   /* right edge, also when it wraps */
.ws-print {
    display:inline-flex; align-items:center; gap:8px; cursor:pointer; white-space:nowrap;
    border:1.5px solid var(--cobalt); background:var(--cobalt); color:var(--card);
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label); padding:0 16px; min-height:40px; border-radius:var(--radius-sm);
    transition:background .15s, border-color .15s;
}
.ws-print:hover { background:var(--cobalt-hover); border-color:var(--cobalt-hover); }
/* The Word file is a second way out, so it is quiet, drawn like «Очистити»
   with its glyph: print stays the one filled button. */
.ws-word {
    display:inline-flex; align-items:center; gap:6px; cursor:pointer;
    border:0; background:none; color:var(--ink-2);
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label); padding:0 10px; min-height:40px; border-radius:var(--radius-sm);
    white-space:nowrap; transition:background .15s, color .15s;
}
.ws-word svg { flex:none; }
.ws-word:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.ws-word[disabled], .ws-word[aria-busy="true"] { cursor:progress; opacity:.7; }
/* «Прибрати всі задачі?» lies over the actions, which keep their room hidden:
   the bar keeps its height and the variants stay where they are. */
.ws-actions.is-asking > :not(.ws-ask) { visibility:hidden; }
.ws-ask { position:absolute; inset:0; display:flex; align-items:center; gap:2px; }
.ws-ask .ws-clear { min-width:44px; }
.ws-clear-q { font-size:var(--fs-label); color:var(--ink); margin-right:6px; white-space:nowrap; }
.ws-clear {
    border:0; background:none; cursor:pointer; color:var(--ink-2); white-space:nowrap;
    font-family:var(--font-sans); font-weight:600; font-size:var(--fs-label); padding:0 10px; min-height:40px; border-radius:var(--radius-sm);
    transition:background .15s, color .15s;
}
.ws-clear:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.ws-clear-yes { color:var(--ink); text-decoration:underline; text-underline-offset:3px; }
/* Desktop: ONE row in the sheet's 820px — heading, three variants, «Очистити»,
   «Word», «Друк або PDF» — with about 14px to spare, so the steps below are
   tight on purpose: a wider label or padding wraps the actions to a second row.
   The count is already in the header's «Добірка · N», so it yields its place;
   the heading takes the reading step instead of the lead. */
@media (min-width:761px){
    .ws-head .ws-count { display:none; }
    .ws-bar .ws-page-title { font-size:var(--fs-read); }
    .ws-variants { gap:1px; }
    .ws-variant { padding:0 7px; }
    .ws-actions { gap:2px; }
    .ws-clear, .ws-word { padding:0 8px; }
    .ws-print { padding:0 12px; gap:6px; margin-left:6px; }
}

.ws-inner { padding-top:var(--space-sm); padding-bottom:72px; }
.ws-hint { max-width:820px; margin:0 auto var(--space-sm); font-size:var(--fs-micro); line-height:1.5; color:var(--ink-muted); }

.ws-empty { max-width:620px; padding:var(--space-xl) 0 var(--space-lg); }
.ws-empty .ws-page-title { font-size:var(--fs-title); letter-spacing:-.02em; line-height:1.1; text-wrap:balance; }
.ws-empty p { font-size:var(--fs-body); line-height:1.6; color:var(--ink-2); margin:var(--space-sm) 0 var(--space-md); max-width:60ch; }
.ws-empty-link {
    display:inline-flex; align-items:center; min-height:44px; padding:0 20px; border-radius:var(--radius-sm);
    background:var(--cobalt); color:var(--card); font-weight:600; font-size:var(--fs-label); text-decoration:none;
    transition:background .15s;
}
.ws-empty-link:hover { background:var(--cobalt-hover); }

/* The sheet: white paper in the plain look of a typeset problem list. */
.ws-sheet {
    max-width:820px; margin:0 auto; background:#fff; color:#000;
    border:1px solid var(--line); border-radius:var(--radius-xs);
    padding:56px 64px 48px;
    font-family:var(--font-math); font-size:var(--fs-read); line-height:1.45;
    -webkit-text-stroke-width:.02em;
}
.ws-sheet .katex { font-size:1em; -webkit-text-stroke-width:0; }
/* Display math on paper: no scroll shadows (they are --card, the sheet is
   white), and no blank line from the source's newline after the formula. */
.ws-sheet .katex-display { background:none; margin:.55em 0; padding-block:2px; max-width:100%; }
.ws-sheet span:has(> .katex-display) + br, .ws-sheet .katex-display + br { display:none; }
.ws-sheet .figure-unavailable { display:block; margin:.6em 0; padding:.6em; font-size:.8em; }
.ws-heading { font-family:var(--font-math); font-weight:700; font-size:1.45em; line-height:1.2; margin:0 0 1.1em; letter-spacing:.01em; }
.ws-title {
    display:block; overflow-wrap:anywhere;
    /* Edited in place: a dotted rule says so, the caret is the pen. */
    cursor:text; caret-color:var(--cobalt); border-radius:2px; outline:0;
    text-decoration:underline dotted #B9BDCB 1px; text-underline-offset:.28em;
    transition:background .15s;
}
.ws-title:hover { background:var(--cobalt-wash); box-shadow:0 0 0 6px var(--cobalt-wash); }
.ws-title:focus { background:none; box-shadow:0 0 0 6px #fff, 0 0 0 8px var(--cobalt); text-decoration-color:transparent; }
.ws-title:empty::before { content:attr(data-placeholder); color:#6A6F7E; }
.ws-item { position:relative; margin:0 0 .95em; }
.ws-item p { margin:0 0 .45em; text-align:justify; hyphens:auto; overflow-wrap:break-word; }
.ws-item p:last-child { margin-bottom:0; }
.ws-label { font-weight:700; }
.ws-statement { display:flow-root; }
.ws-code {
    margin:0 0 .1em; font-family:var(--font-sans); font-size:.62em; line-height:1.5; font-weight:500; text-align:left;
    letter-spacing:.03em; font-variant-numeric:tabular-nums; color:#6A6F7E; -webkit-text-stroke-width:0; white-space:nowrap;
    break-after:avoid;
}
.ws-item img { display:block; max-width:min(100%, 420px); max-height:320px; width:auto; height:auto; margin:.6em auto; break-inside:avoid; }
.ws-item .markdown-table, .ws-item .markdown-grid { margin:.6em auto; font-size:.95em; break-inside:avoid; }
.ws-key { margin:.45em 0 0 1.4em; padding-left:.9em; border-left:1px solid #C9CCD6; }
.ws-key > * + * { margin-top:.45em; }
.ws-nosol { font-style:italic; color:#4A5170; }
/* Solutions on their own pages, after the statements. */
.ws-keys { margin-top:2.2em; padding-top:1.4em; border-top:1px dashed #C9CCD6; break-before:page; }
.ws-keys-title { font-family:var(--font-math); font-weight:700; font-size:1.25em; line-height:1.2; margin:0 0 .9em; }
.ws-key-end { margin:0 0 .95em; }
.ws-key-end > * + * { margin-top:.45em; }
.ws-key-end p { margin:0 0 .45em; text-align:justify; hyphens:auto; }
.ws-key-end img { display:block; max-width:min(100%, 420px); max-height:320px; width:auto; height:auto; margin:.6em auto; break-inside:avoid; }
.ws-key-num { font-weight:700; margin-right:.45em; }
.ws-source { margin:1.8em 0 0; padding-top:.7em; border-top:1px solid #C9CCD6; font-size:.78em; color:#4A5170; -webkit-text-stroke-width:0; }
.ws-missing p { font-family:var(--font-sans); font-size:var(--fs-label); color:var(--ink-muted); font-style:italic; -webkit-text-stroke-width:0; }

/* Screen-only row tools: quiet glyphs in the sheet's left margin, waking when
   the problem is pointed at or a tool has the focus. */
.ws-item-tools { position:absolute; left:-46px; top:-3px; display:flex; flex-direction:column; gap:0; color:var(--ink-muted); opacity:.55; transition:opacity .15s; }
.ws-item:hover .ws-item-tools, .ws-item:focus-within .ws-item-tools { opacity:1; }
.ws-tool {
    display:grid; place-items:center; width:30px; height:28px; padding:0; border:0; border-radius:var(--radius-xs);
    background:none; color:inherit; cursor:pointer; transition:background .15s, color .15s;
}
.ws-tool:hover { background:var(--cobalt-wash); color:var(--cobalt-deep); }
.ws-tool[disabled] { opacity:.3; cursor:default; pointer-events:none; }
/* The handle a problem is dragged by. `touch-action:none` so a finger on it
   moves the problem, not the page. */
.ws-grip { cursor:grab; touch-action:none; }
.ws-sheet.is-sorting, .ws-sheet.is-sorting .ws-grip { cursor:grabbing; }
.ws-sheet.is-sorting { user-select:none; -webkit-user-select:none; }
.ws-sheet.is-sorting .ws-item { transition:transform .18s cubic-bezier(.2,.8,.2,1); }
/* Lifted: the one moment the page is not flat. */
.ws-sheet.is-sorting .ws-item.is-dragging {
    transition:none; z-index:2; background:#fff; border-radius:var(--radius-xs);
    box-shadow:0 0 0 10px #fff, 0 0 0 11px var(--cobalt), 0 14px 28px 6px rgba(25,30,54,.16);
}
.ws-item.is-dragging .ws-item-tools { opacity:1; color:var(--cobalt-deep); }
@media (prefers-reduced-motion:reduce){ .ws-sheet.is-sorting .ws-item { transition:none; } }
@media (hover:none) { .ws-item-tools { opacity:1; } }
/* A finger gets 44px on the row tools, the variants and the actions whatever
   their drawn size: the hit area grows, the glyphs and the row stay put. On a
   desktop-width touch screen the margin tools are 30×28 and stacked, so the
   handle reaches up and the ✕ down. */
@media (pointer:coarse){
    /* ::before, because the phone's handle label is its ::after. */
    .ws-tool, .ws-variant, .ws-clear, .ws-word, .ws-print { position:relative; }
    .ws-tool::before, .ws-variant::before, .ws-clear::before, .ws-word::before, .ws-print::before { content:""; position:absolute; inset:-2px 0; }
    .ws-variant::before { inset:-5px 0; }
}
@media (pointer:coarse) and (min-width:761px){
    .ws-grip::before { inset:-16px -7px 0; }
    .ws-tool-remove::before { inset:0 -7px -16px; }
}

@media (max-width:760px){
    .ws-bar-inner { padding-top:10px; padding-bottom:10px; }
    .ws-head { width:100%; }
    .ws-variants { flex:1 1 100%; }
    /* Sized by their words, not in thirds: the long third label keeps its line. */
    .ws-variant { flex:1 1 auto; padding:0 4px; min-height:40px; }
    .ws-actions { flex:1 1 100%; justify-content:space-between; }
    .ws-print { min-height:44px; }
    /* «Очистити» keeps the left edge; the two ways out sit together on the right. */
    .ws-word { min-height:44px; margin-left:auto; }
    .ws-clear { min-height:44px; }
    .ws-inner { padding-bottom:52px; }
    .ws-hint { margin-left:0; margin-right:0; }
    .ws-sheet { padding:24px 16px; border-left:0; border-right:0; border-radius:0; margin:0 calc(-1 * var(--gutter)); }
    .ws-item p { text-align:left; }
    .ws-key { margin-left:.2em; padding-left:.7em; }
    /* No margin to stand in: each problem opens with its own tool row, under
       the rule that parts it from the one before, so the row reads as this
       problem's. The handle is a labelled button here: six dots alone did not
       say «drag me» on a phone (user 2026-10-05). */
    .ws-item { margin-bottom:.7em; padding-top:8px; border-top:1px solid #DCDCD4; }
    .ws-item-tools { position:static; flex-direction:row; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px; opacity:1; }
    .ws-sheet.is-sorting .ws-item.is-dragging { border-top-color:transparent; }
    .ws-tool { width:44px; height:40px; }
    .ws-tool svg { width:18px; height:18px; }
    .ws-grip {
        width:auto; padding:0 14px 0 10px; display:inline-flex; align-items:center; gap:6px;
        border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--card); color:var(--ink-2);
        font-family:var(--font-sans); font-size:var(--fs-label); font-weight:600; -webkit-text-stroke-width:0;
    }
    .ws-grip::after { content:"Перемістити"; }
    .ws-grip:active, .ws-item.is-dragging .ws-grip { background:var(--cobalt-wash); border-color:var(--cobalt); color:var(--cobalt-deep); }
}
/* Three actions in one row at 360px: 12px side padding still wrapped the
   print label, so the two buttons tighten their padding and icon gap. */
@media (max-width:400px){ .ws-print, .ws-word { padding:0 10px; gap:6px; } }
@media (max-width:380px){ .ws-variant { font-size:12px; padding:0 3px; } }
@media (max-width:350px){ .ws-variant { flex:1 1 0; white-space:normal; line-height:1.2; padding:4px 3px; } }

/* A phone in the hand: the bar is too tall to pin. */
@media (max-width:760px) and (max-height:900px){ .ws-bar { position:static; } }

/* Print: the sheet and nothing else. A named page, so the footer line does not
   reach a print of any other page of the site. */
@page worksheet {
    size:A4; margin:20mm 22mm 20mm;
    @bottom-left { content:"apex-minds.org"; font-family:'CMU Serif', serif; font-size:8.5pt; color:#555; }
    @bottom-right { content:counter(page); font-family:'CMU Serif', serif; font-size:10pt; color:#000; }
}
@media print {
    html, body { background:#fff !important; }
    /* No screen min-heights: they push an empty last page. */
    body:has(#worksheetView:not([hidden])) { display:block; min-height:0 !important; }
    body:has(#worksheetView:not([hidden])) .site-footer { display:none !important; }
    .ws-sheet .katex-display { overflow:visible; }
    #worksheetView .ws-bar, #worksheetView .ws-hint, #worksheetView .ws-item-tools, #worksheetView .ws-missing, .toast-container { display:none !important; }
    .ws-title { text-decoration:none; background:none; box-shadow:none; }
    .ws-item { border-top:0; padding-top:0; margin-bottom:.95em; }
    /* No screen paddings either: on a nearly full last page they spill an empty one. */
    #worksheetView, #worksheetView .ws-inner { max-width:none; padding:0 !important; margin:0 !important; }
    .ws-sheet {
        page:worksheet; max-width:none; margin:0; padding:0; border:0; border-radius:0; background:none;
        font-size:11.5pt; line-height:1.38; -webkit-text-stroke-width:0;
    }
    .ws-item p { text-align:justify; orphans:2; widows:2; }
    .ws-statement { break-inside:avoid; }
    .ws-answer, .ws-note { break-inside:avoid; }
    .ws-code { color:#555; }
    .ws-key { border-left-color:#999; }
    .ws-keys { margin-top:0; padding-top:0; border-top:0; }
    .ws-source { break-inside:avoid; color:#333; }
}

/* ── «Автори задач» (/authors) ─────────────────────────────────────────────
   A thank-you page: no counts, no ranking. One featured author on the brand
   tile (the logo's blue on its клітинка ruling), then everyone else by the
   first letter of the surname. Shares .about's column and type. */
.authors-kicker, .authors-hero-kicker {
    margin:0 0 10px; font-family:var(--font-sans); font-size:var(--fs-label); font-weight:650;
    letter-spacing:.08em; text-transform:uppercase; color:var(--brand-blue);
}
.authors-hero {
    position:relative; overflow:hidden; margin-top:var(--space-ml); padding:var(--space-ml) var(--space-lg);
    border-radius:var(--radius-lg); color:#FBFBF2; background-color:var(--brand-blue);
    background-image:linear-gradient(rgba(255,255,255,.13) 2px, transparent 2px),
                     linear-gradient(90deg, rgba(255,255,255,.13) 2px, transparent 2px);
    background-size:46px 46px; background-position:-1px -1px;
}
.authors-hero .authors-hero-kicker { color:#FBFBF2; opacity:.8; }
.about .authors-hero-name {
    margin:0 0 var(--space-sm); font-family:var(--font-display); font-weight:600;
    font-size:clamp(28px, 5vw, 40px); line-height:1.1; letter-spacing:-.01em; color:#FBFBF2;
}
.authors-hero p { margin:0 0 14px; font-size:var(--fs-read); line-height:1.62; color:#FBFBF2; max-width:58ch; }
.authors-abc { margin-top:var(--space-md); }
.authors-letter {
    display:grid; grid-template-columns:2.2em minmax(0, 1fr); gap:var(--space-sm);
    padding:12px 0; border-top:1px solid var(--line);
}
.authors-letter-k {
    font-family:var(--font-display); font-weight:600; font-size:22px; line-height:1.2; color:var(--brand-blue);
}
.authors-letter ul { list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:6px 8px; }
.authors-letter li {
    padding:4px 12px; border-radius:var(--radius-full); background:var(--brand-blue-50);
    font-size:var(--fs-body); line-height:1.5; color:var(--ink);
}
.authors-thanks { margin-top:var(--space-md); }
@media (max-width: 640px) {
    .authors-hero { padding:var(--space-lg) var(--space-ms); background-size:34px 34px; }
    .authors-hero p { font-size:var(--fs-body); }
    .authors-letter { grid-template-columns:1.6em minmax(0, 1fr); gap:var(--space-xs); }
    .authors-letter-k { font-size:19px; }
}

/* /about → /authors: the thank-you, set apart near the top of «Про проєкт». */
.about-authors {
    margin-top:var(--space-ml); padding:var(--space-md) var(--space-md) var(--space-ms);
    border-radius:var(--radius-md); background:var(--brand-blue-50);
    border-left:4px solid var(--brand-blue);
}
.about .about-authors h2 { margin-top:0; }
.about-authors p { margin:0 0 12px; font-size:var(--fs-body); line-height:1.62; color:var(--ink); }
.about-authors-link {
    display:inline-flex; align-items:center; gap:var(--space-2xs); min-height:44px;
    font-family:var(--font-sans); font-size:var(--fs-body); font-weight:650;
    color:var(--brand-blue-700); text-decoration:none;
}
.about-authors-link:hover { text-decoration:underline; text-underline-offset:3px; }
.about-authors-link svg { transition:transform var(--dur-fast) var(--ease); }
.about-authors-link:hover svg { transform:translateX(3px); }
@media (prefers-reduced-motion: reduce) { .about-authors-link svg { transition:none; } .about-authors-link:hover svg { transform:none; } }
