/* GENERATED from design/catalogue/page-v9/Katalog kurzov v9.html by
 * tools/build-catalogue-assets.php — do not edit by hand. Change the design file and
 * re-run the builder; that file is the specification and this is a copy of it.
 * Generated: 2026-09-05T04:52:48+00:00
 */

/* ══════════════════════════════════════════════════════════════════════════
   FILTERS · A2 — ALWAYS VISIBLE, NO SIDEWAYS SCROLL
   B is dropped: every change of mind cost an open. What stays is the
   always-visible band, with the horizontal scroll replaced by a disclosure
   per axis and a fourth line for a programme's sections. The page around it is
   page-v8's, copied rather than mocked.

   FILTERS — TWO ANSWERS, IN THE REAL PAGE
   A and B are two surfaces over one identical filter state: same axes, same
   order, same counts, same union-within and intersection-across, same disabled
   values, same keyboard, same URL. Switch them in the demo bar. The page
   around them — hero, shelves, cards, preview — is page-v8's, copied rather
   than mocked and otherwise untouched.

   THE CATALOGUE PAGE v8 — the filter panel is rebuilt on a navy ground in
   Direction C's register (values as light on dark, teal for the selected one,
   counts dimmed), the filter row gives up its frosting so the hero keeps the
   page's only frosted surface, and the drift is 40s instead of 112s. Nothing
   else changed.

   THE CATALOGUE PAGE v7 — the hero is T2 from hero-v3, taken whole: the glass
   pane, the warm field, the one drifting light at 112s, the letterform sweep
   that greets once and stops, the backdrop-filter fallback and the
   reduced-transparency answer. Nothing else on the page changed.
   Two things the page context forced, both in the note: the hero is BUILT ONCE
   and only its two sentences are updated afterwards — this page repaints on
   every keystroke, and a rebuilt hero would restart the sweep and reset the
   112-second orbit each time a letter is typed — and the sweep's curve is
   pinned to its own token, because this page's --e-out is a different, weaker
   curve than the one hero-v3 wrote it against.

   THE CATALOGUE PAGE v4 — the claim. One constant line that everyone reads,
   and beneath it AT MOST ONE line that depends on who is looking. Never three
   lines. Every one of those strings is edited on this plugin's own settings
   screen, per language, with the current text as the default — an empty field
   is how "render nothing" is said, not a special case in code. The partner
   logos moved up under it, where they are actually seen.

   THE CATALOGUE PAGE v3 — the rail has two forms. At rest, at the top of the
   page, it is exactly what it was: three axes, every value, live counts,
   disabled values visible, and NOT sticky — that is where it teaches the shape
   of the catalogue and it needs the room. Once the reader scrolls past it, it
   condenses to a SINGLE fixed line: the count, the active chips on one
   horizontally scrolling row, and one control that reopens the full rail as a
   panel over the grid. One rail element, two presentations, and the axes exist
   in only one of them at a time.

   THE CATALOGUE PAGE v2 — v1 imported the card's MARKUP but dropped every
   listener that drives its hover preview, so the panel existed in the DOM and
   nothing ever opened it. The card is a finished component and arrives whole:
   the wiring block below is card-v14's, verbatim, and one panel serves the
   whole page in both states. The rail is now a stable element whose contents
   are replaced, rather than an element replaced wholesale — see the note.
   "Časti" became "kroky" in the card's own string table, where it belongs.

   THE CATALOGUE PAGE v1 — built from the settled card, which is imported
   from card-v14 UNCHANGED: geometry, ribbons, price slot, recognition line,
   ISO squares, hover preview and the data-schb button. Nothing below alters
   it. What is new is the page around it: two states with one composed
   transition between them, a three-axis rail assembled per viewer, shelves,
   a results grid, and a partner band that has to look right at 3 logos and
   at 12. The card's own version log follows.

   CARD v14 — two changes:
     · THE FEDERATION CREST IS GONE, and with it the sch_fa.logo_url read. At
       the size the wording allowed it, an emblem was an unreadable smudge
       carrying nothing the words did not already carry. A check mark takes
       its place: it CONFIRMS rather than IDENTIFIES, which is what the line
       was doing all along.
     · THE PREVIEW'S THREE TIMINGS are custom properties in :root, read once
       from the stylesheet by the JS. Tuning the open delay is one number in
       one file.

   CARD v13 — three corrections:
     · ONE CREST SIZE, derived from the cap height of the text beside it and
       used in both places, so the panel's larger wording scales the mark by
       the same rule rather than by a second number.
     · THE CONDITIONS come from this plugin's own webinar CPD block — the
       `note` attribute of sch-course-blocks/webinar-cpd — and hang on a live
       Progressive Coach session, which is the only kind of thing that carries
       them. A recording is not accredited and shows neither badge nor note.
     · NO CPD VALUE IS LOCAL ANY MORE. The unit words and the plural tables
       this file used to own are gone: SCH_Coach_CPD::course_recognitions()
       already returns unit_label "resolved for the current locale and
       plural-correct for this amount, so consumers print it verbatim". That
       is now exactly what happens here.

   CARD v12 — the recognition
   detail is finished. The panel's caption is gone, the pills moved from teal
   into a softened navy so the block stops reading as one green mass, the
   credit the course carries sits under them, and the conditions of the
   accreditation hide behind a disclosure. The badge renders the association's
   own logo, and its wording changes when the accreditation names the licence
   the coach actually holds.
   THE CONDITIONS TEXT HAS NO FIELD IN CIS — see the note. Not one word of it
   is written in this component: it arrives as data or the disclosure does not
   render at all.

   CARD v11 — two additions:
     · FEDERATION RECOGNITION. Not a ribbon: a ribbon carries what is true for
       everyone, and recognition is a relationship between this course and this
       viewer's association. On the card it sits between the price and the meta
       line, in the price's own teal register; in the panel it carries the
       licences it counts toward, as pills.
     · THE PANEL POINTS AT ITS CARD. An arrow on the side facing the card,
       following the flip, tracking the card's vertical centre, clamped inside
       the panel's own corners.
   The data behind the recognition badge DOES NOT EXIST in this plugin. The
   shape below mirrors the real CIS tables — see the note. 
   ══════════════════════════════════════════════════════════════════════════ */
:root{
  --teal:#227F77; --teal-lt:#5dcaa5; --navy:#122B46; --yellow:#F1C40F; --white:#fff;
  --ink:#122B46; --ink-72:rgba(18,43,70,.72); --ink-54:rgba(18,43,70,.54);
  --ink-38:rgba(18,43,70,.38); --ink-22:rgba(18,43,70,.22);
  --rule:rgba(18,43,70,.13); --rule-2:rgba(18,43,70,.07); --wash:rgba(18,43,70,.04);
  --el-rest:0 1px 2px rgba(18,43,70,.05);
  --el-full:0 6px 18px rgba(18,43,70,.13), 0 2px 5px rgba(18,43,70,.07);
  --el-compact:0 3px 10px rgba(18,43,70,.10);
  --el-panel:0 18px 48px rgba(18,43,70,.18), 0 3px 10px rgba(18,43,70,.09);
  --f-head:'Rajdhani',system-ui,sans-serif;
  --f-body:'Source Sans 3','Noto Sans Thai','Noto Sans Arabic',system-ui,sans-serif;
  --r:6px; --t-ui:150ms; --e-out:cubic-bezier(.22,.61,.36,1);
  /* the collapse curve is emil-design-eng's strong ease-out, added as its OWN
     token: --e-out above is the weak built-in easing the skill warns against,
     but it carries every settled transition in this file and is left alone */
  --e-collapse:cubic-bezier(.23,1,.32,1); --t-collapse:200ms;
  /* the recognition tick, as multiples of the CAP HEIGHT of the text beside it */
  --tick:1.1; --tick-drop:-.05;
  /* ── THE PREVIEW'S THREE TIMINGS ────────────────────────────────────────
     THE ONLY PLACE THESE EXIST. The JS reads them from here at start-up with
     getComputedStyle; there is no literal to keep in step. Want the panel
     slower on the live page? Change --pv-open, and nothing else. */
  --pv-open:280ms;      /* hover intent before the panel opens               */
  --pv-close:180ms;     /* grace after leaving, so a diagonal path survives  */
  --pv-suspend:120ms;   /* how long a scroll blocks opening                  */
  /* the ONE composed transition between discovery and results. Two halves of
     one gesture: what leaves goes first and quickly, what arrives takes a
     little longer because it carries something to read. Both are inside
     emil-design-eng's 300ms ceiling; everything else on the page is --t-ui. */
  --t-out:150ms; --t-in:210ms;
  /* ── carried in with the hero ────────────────────────────────────────────
     THE CURVE IS PINNED. hero-v3's --e-out was cubic-bezier(.23,1,.32,1); this
     page's --e-out is cubic-bezier(.22,.61,.36,1), the weaker built-in. Left
     alone the sweep would have silently changed curve on the way in. */
  --e-hero:cubic-bezier(.23,1,.32,1);
  --navy-deep:#0b1c2f; --navy-lift:#17395e;
  /* a dark step of the SAME teal, derived: a teal that darkens by sliding into
     navy rotates hue, and hue rotation is what makes a field read as a generic
     aurora rather than as one brand's colour */
  --teal-deep:color-mix(in oklab,var(--teal) 74%,#000);
  --k:1;                       /* one-shot duration scale */
  --play:running;              /* the one continuous animation on the page */
  /* ── THE HEADER OFFSET ───────────────────────────────────────────────────
     THE SITE'S OWN sticky header is not ours to measure. --site-header is the
     one number the theme sets (an inline style on <html>, or a rule in the
     child theme); everything that has to sit below it derives from this and
     nothing hardcodes a pixel. Default 0 because a theme without a sticky
     header must not be given a gap it did not ask for. */
  --site-header:0px;
  --ba-sticky:1;         /* can the band stick at this width at all? the phone
                            block says no, and autoShed() reads it rather than
                            re-stating the breakpoint in JS                   */
  --pin-w:38px;          /* the trailing space the last row keeps clear for the pin;
                            0 on a phone, where there is no pin — see the phone block */
  --t-pan:190ms;        /* the panel opening: emil's 150–250ms popover band */
  --lift:3px; --reach-up:3px; --reach-down:15px; --track-pad:22px;
  --sq:26px; --sq-gap:10px;
}
:root.rm{--k:.001; --sweep:none; --play:paused}
@media(prefers-reduced-motion:reduce){:root{--k:.001; --sweep:none; --play:paused}}
.schcat-page,.schcat-page *{box-sizing:border-box}
/* ══ [hidden] MUST ACTUALLY HIDE ═══════════════════════════════════════════
   The browser's own rule is `[hidden]{display:none}` in the UA stylesheet, and
   ANY author rule that sets `display` beats it — because author beats UA, at any
   specificity. So `.lic{display:inline-flex}` silently cancelled `hidden` on the
   licence pills: all six were on screen from the start, the "+3" control revealed
   nothing because there was nothing left to reveal, and only its own label moved.
   The markup, the handler and the state were all correct.

   This file already carried `.view[hidden]` and `.ba__more[hidden]` — the same
   trap, found and patched twice, one element at a time. This is the third, so it
   is fixed once for the page instead: anything with `display` set is still caught,
   because 0,2,0 out-ranks the single class those rules use.

   `display:none` and not `visibility` or `clip`: a hidden pill must leave the flex
   flow entirely, or the row keeps its gaps. */
.schcat-page [hidden],.pv [hidden]{display:none}
.schcat-page{color:var(--ink);font-family:var(--f-body);font-size:16px;line-height:1.5;-webkit-font-smoothing:antialiased}
.schcat-page h1,.schcat-page h2,.schcat-page h3,.schcat-page h4{font-family:var(--f-head);font-weight:700;margin:0}
.wrap{max-width:1180px;margin-inline:auto;padding:30px 26px 110px}
.sec{padding-block:42px;border-block-start:1px solid var(--rule)}
.sec:first-of-type{border-block-start:0;padding-block-start:0}
.sec__h{display:flex;align-items:baseline;gap:11px;margin-bottom:6px;flex-wrap:wrap}
.sec__l{font-family:var(--f-head);font-weight:700;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--white);background:var(--navy);padding:4px 8px}
.sec__h h2{font-size:21px}
.cap{margin:0 0 18px;font-size:14.5px;color:var(--ink-72);max-width:84ch;text-wrap:pretty}
.ctx{font-family:var(--f-head);font-size:11px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-38);margin:24px 0 10px}

/* ══ THE CARD — v7, carried over ══════════════════════════════════════════ */
.k{position:relative;display:flex;flex-direction:column;background:var(--white);border:1px solid var(--rule);
   border-radius:var(--r);overflow:hidden;text-align:start;padding:0;box-shadow:var(--el-rest);
   transition:transform var(--t-ui) var(--e-out),box-shadow var(--t-ui) var(--e-out),border-color var(--t-ui) var(--e-out)}
.k__hit{position:absolute;inset:0;z-index:1}
.k__hit:focus-visible{outline:2px solid var(--teal);outline-offset:-3px;border-radius:var(--r)}
.k__im{position:relative;aspect-ratio:16/9;overflow:hidden;background:var(--wash);flex:0 0 auto}
.k__im img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;transform:none}
.ph{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
    background:repeating-linear-gradient(135deg,rgba(18,43,70,.03) 0 10px,rgba(18,43,70,.06) 10px 20px)}
.ph i{font-style:normal;font-family:var(--f-head);font-weight:600;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-38)}
.k__b{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-width:0;padding:11px 12px 12px;gap:6px}
.k__t{font-family:var(--f-head);font-weight:600;font-size:16.5px;line-height:1.2;letter-spacing:.004em;
      display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
      transition:color var(--t-ui) var(--e-out)}
/* price is TEXT, never a control — the card has no buttons at all */
.k__price{display:flex;flex-direction:column;gap:2px;margin-block:6px;position:relative;z-index:1}
.k__amt{font-family:var(--f-head);font-weight:700;font-size:17px;letter-spacing:.01em;color:var(--ink);line-height:1.2}
.k__amt.is-words{font-size:12.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--teal);line-height:1.35;text-wrap:pretty}
.k__note{font-size:11.5px;color:var(--ink-54);line-height:1.35;text-wrap:pretty}
.k__m{display:flex;flex-wrap:wrap;align-items:baseline;gap:5px;margin:0;font-size:12.5px;color:var(--ink-54);line-height:1.35}
.mi{display:inline-flex;align-items:baseline;gap:5px;min-width:0}
.mi+.mi::before{content:"·";color:var(--ink-38)}
.ai{font-size:11px;color:var(--ink-38)}
.k__alt{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sq-gap);margin:0;margin-block-start:auto;
        padding-block-start:9px;border-block-start:1px solid var(--rule-2);position:relative;z-index:2}
.k__altlbl{font-family:var(--f-head);font-weight:600;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-38)}
.sq{position:relative;display:inline-flex;align-items:center;justify-content:center;
    inline-size:var(--sq);block-size:var(--sq);flex:0 0 var(--sq);
    font-family:var(--f-head);font-weight:700;font-size:10.5px;letter-spacing:.03em;
    color:var(--ink-72);text-decoration:none;background:var(--white);
    border:1px solid var(--ink-22);border-radius:4px;
    transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out),border-color var(--t-ui) var(--e-out)}
.sq::after{content:"";position:absolute;inset-block:calc((44px - var(--sq)) / -2);inset-inline:calc(var(--sq-gap) / -2)}
.sq:hover,.sq:focus-visible{background:var(--teal);color:var(--white);border-color:var(--teal)}
.sq:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.sq.is-nocode{border-style:dashed;color:var(--ink-38)}
.rib{position:absolute;inset-block-start:0;z-index:2;display:inline-flex;align-items:center;
     font-family:var(--f-head);font-weight:700;font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
     padding:4px 8px;border-radius:0;border:0;opacity:1}
.rib--start{inset-inline-start:0}
.rib--end{inset-inline-end:0}
/* ══ THE YELLOW RULE, AS IT NOW STANDS ═════════════════════════════════════
   --yellow is the page's one loud colour and it has exactly THREE uses. Nothing
   else on this page may take it, and a fourth use is a decision, not a tweak:

     1. NAŽIVO   — a live session is coming. The one fact on a card that is
                   time-critical, and the only one a coach loses by missing.
     2. NOVÉ     — new since the reader last looked. Added here: it was navy,
                   the colour of the card's own furniture, so it said "new" in
                   the same voice as everything that was not.
     3. THE CALL TO ACTION — the membership band's button, and nothing else.
                   A button that sells is allowed to shout once.

   Both ribbons are yellow, so a course that is both must show only one; ribEnd()
   picks Naživo, because a date passes and "new" does not. */
.rib--prog,.rib--lock{background:var(--navy);color:var(--white)}
.rib--new{background:var(--yellow);color:var(--navy)}
.rib--done,.rib--open{background:var(--teal);color:var(--white)}
.rib--live{background:var(--yellow);color:var(--navy)}
.k:hover{transform:translateY(calc(var(--lift) * -1));box-shadow:var(--el-full);border-color:var(--ink-22);z-index:3}
.k:hover .k__t{color:var(--teal)}
.k:active{transform:translateY(calc(var(--lift) * -1)) scale(.985);box-shadow:var(--el-compact)}
.k:has(.sq:hover),.k:has(.sq:focus-visible){transform:none;box-shadow:var(--el-rest);border-color:var(--rule)}

/* ══ THE PREVIEW PANEL — ONE element, reused ══════════════════════════════
   Never one per card: 174 panels in the DOM is unacceptable, so a single node
   is repositioned and re-filled. Its static styles are logical; only the
   viewport-flip maths is physical, because a flip is computed from rects. */
.pv{position:fixed;z-index:100;inline-size:344px;max-inline-size:calc(100vw - 24px);
    background:var(--white);border:1px solid var(--rule);border-radius:8px;box-shadow:var(--el-panel);
    padding:16px 16px 14px;opacity:0;visibility:hidden;transform:translateY(5px);
    transition:opacity 150ms var(--e-out),transform 150ms var(--e-out),visibility 0s linear 150ms}
.pv.is-open{opacity:1;visibility:visible;transform:none;transition-delay:0s,0s,0s}
.pv h4{font-size:18px;line-height:1.22;margin:0 0 6px}
.pv__desc{margin:0 0 10px;font-size:13.5px;color:var(--ink-72);line-height:1.5;text-wrap:pretty}
.pv__learn{list-style:none;margin:0 0 12px;padding:0;display:flex;flex-direction:column;gap:5px}
.pv__learn li{position:relative;padding-inline-start:19px;font-size:13px;color:var(--ink-72);line-height:1.42}
.pv__learn li::before{content:"";position:absolute;inset-inline-start:0;inset-block-start:6px;
  inline-size:11px;block-size:11px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23227F77' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l5 5L19 7'/%3E%3C/svg%3E");
  background-size:contain;background-repeat:no-repeat}
.pv__who{display:flex;align-items:center;gap:9px;padding-block:9px;border-block:1px solid var(--rule-2);margin-block-end:10px}
.pv__ava{inline-size:30px;block-size:30px;flex:0 0 30px;border-radius:99px;background:var(--wash);
         display:flex;align-items:center;justify-content:center;font-family:var(--f-head);font-weight:700;font-size:12px;color:var(--ink-38)}
.pv__whoname{font-family:var(--f-head);font-weight:600;font-size:13.5px;line-height:1.2}
.pv__whorole{font-size:11.5px;color:var(--ink-54);line-height:1.3}
.pv__break{margin:0 0 4px;font-size:12.5px;color:var(--ink);line-height:1.4}
.pv__break b{font-family:var(--f-head);font-weight:700;font-size:14px}
.pv__breakparts{color:var(--ink-54)}
.pv__meta{margin:0 0 12px;font-size:12px;color:var(--ink-54);line-height:1.4}
.pv__alt{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sq-gap);margin:0 0 13px;
         padding-block-start:10px;border-block-start:1px solid var(--rule-2)}
/* the action — the only place on the whole surface that buys anything */
.pv__act{display:flex;flex-direction:column;gap:6px}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;inline-size:100%;
     min-block-size:42px;padding:10px 16px;border:0;border-radius:var(--r);cursor:pointer;text-decoration:none;
     font-family:var(--f-head);font-weight:700;font-size:13.5px;letter-spacing:.07em;text-transform:uppercase;
     transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out)}
.btn--primary{background:var(--teal);color:var(--white)}
.btn--primary:hover,.btn--primary:focus-visible{background:color-mix(in oklab,var(--teal) 85%,var(--navy))}
.btn--navy{background:var(--navy);color:var(--white)}
.btn--navy:hover,.btn--navy:focus-visible{background:color-mix(in oklab,var(--navy) 88%,var(--white))}
.btn:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.pv__price{display:flex;align-items:baseline;gap:8px;margin:0 0 2px}
.pv__amt{font-family:var(--f-head);font-weight:700;font-size:22px;line-height:1.1}
.pv__sub{font-size:12px;color:var(--ink-54);line-height:1.4;text-wrap:pretty}
@media(prefers-reduced-motion:reduce){.pv{transition:opacity 100ms linear,visibility 0s}.pv.is-open{transform:none}}

/* ══ FEDERATION RECOGNITION ═══════════════════════════════════════════════
   No box, no tint, no chip. The card's body has no boxes in it, and a filled
   chip here would have made recognition LOUDER than the price rather than
   equal to it. The card already owns a register that means "this line matters
   as much as a price" — .k__amt.is-words, the teal uppercase the tier note is
   set in — so recognition borrows it exactly, and the mark supplies the extra
   weight. Same metrics, deliberately. */
.k__acc{display:flex;align-items:baseline;gap:6px;margin:0;position:relative;z-index:1;
  font-family:var(--f-head);font-weight:700;font-size:12.5px;letter-spacing:.06em;
  text-transform:uppercase;line-height:1.35;color:var(--teal);text-wrap:pretty}
/* ── THE TICK: ONE RULE, USED EVERYWHERE ─────────────────────────────────
   The crest is gone. An association emblem rendered beside 12.5px uppercase
   is a grey smudge — it identifies nothing at that size, and the association's
   own short name is right next to it doing the identifying properly. What the
   line actually does is CONFIRM, so it gets the mark for confirming.

   THE SIZE is 1.1× the CAP HEIGHT of the text beside it — the same rule the
   crest used, one declaration, both places, scaling with the panel's larger
   wording by itself. The MULTIPLE changed, and had to: the shield's ink filled
   its box, so 1.55cap of box was 1.55cap of ink. This tick is drawn in a
   viewBox cropped tight to the stroke, so box height IS ink height, and a mark
   confirming uppercase wants to stand about a cap tall. The extra tenth is
   because a two-stroke diagonal carries less mass than a letter of the same
   height and reads timid at exactly 1cap.

   THE OPTICAL CORRECTION, RE-DERIVED — and it changes sign. A shield's ink is
   top-heavy over a thin tapering tail, so it needed pushing DOWN (0.18cap in
   v13). A tick is the other way about: its lowest ink is the vertex, its
   longest stroke rises to the top right, and the weighted centroid of the two
   strokes sits at ≈41% of the box height from the bottom — BELOW centre. With
   the box foot on the baseline that puts the centroid 0.41 × 1.1 = 0.45cap
   above it, where uppercase wants its optical centre at 0.5cap. So the tick is
   LIFTED, not dropped: 0.05cap, a negative --tick-drop.
   Said plainly: at card size that is about 0.4px and you will not see it. The
   honest finding is that a tight-cropped tick needs almost no correction,
   where the shield needed a large one. The value is derived rather than
   dropped, so it stays right if the wording is ever set larger.

   The em fallback is the same pair of rules where `cap` is unsupported, using
   the ≈0.7em cap height typical of a humanist sans. */
.acc__m{flex:none;display:block;inline-size:auto;aspect-ratio:4/3;
  block-size:calc(1em * .7 * var(--tick));
  translate:0 calc(1em * .7 * var(--tick-drop))}
@supports (block-size:1cap){
  .acc__m{block-size:calc(1cap * var(--tick));
    translate:0 calc(1cap * var(--tick-drop))}
}
.k--compact .k__acc{font-size:11px;gap:5px}

/* in the panel the same fact carries its detail */
.pv__acc{margin:0 0 13px;padding-block-start:11px;border-block-start:1px solid var(--rule-2)}
.pv__accb{display:flex;align-items:baseline;gap:7px;margin:0 0 9px;
  font-family:var(--f-head);font-weight:700;font-size:13px;letter-spacing:.06em;
  text-transform:uppercase;line-height:1.3;color:var(--teal)}
.pv__lic{display:flex;flex-wrap:wrap;gap:6px;margin:0}
/* THE PILLS ARE NAVY, NOT TEAL. Teal under a teal badge read as one green mass
   and the two stopped being a heading and its detail. A softened navy tint
   keeps them quiet and unmistakably subordinate; the badge keeps the teal to
   itself. They are FACTS and carry no interaction — the +N is the one control
   here, so it alone borrows the language square's vocabulary, and it does so in
   the pills' own family rather than the badge's. */
.lic{display:inline-flex;align-items:center;min-block-size:24px;padding-inline:8px;
  font-size:11.5px;line-height:1;white-space:nowrap;border-radius:4px;
  border:1px solid color-mix(in oklab,var(--navy) 16%,var(--white));
  background:color-mix(in oklab,var(--navy) 5%,var(--white));color:var(--ink-72)}
.lic--more{font-family:var(--f-head);font-weight:700;font-size:11px;letter-spacing:.04em;
  cursor:pointer;background:var(--white);border-color:var(--ink-22);color:var(--ink-72);
  transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out),border-color var(--t-ui) var(--e-out)}
.lic--more:hover,.lic--more:focus-visible{background:var(--navy);color:var(--white);border-color:var(--navy)}
.lic--more:focus-visible{outline:2px solid var(--navy);outline-offset:2px}

/* the credit the row carries, on its own line under the pills */
.pv__cred{margin:9px 0 0;font-family:var(--f-head);font-weight:700;font-size:13.5px;
  letter-spacing:.01em;line-height:1.25;color:var(--navy)}
/* the conditions. Long, legal in tone, and they must not compete with the three
   bullets above them — so the disclosure is closed by default, its summary is
   the size of the meta line rather than of a heading, and the prose is smaller
   and muted again. */
.pv__cond{margin-block-start:9px}
.pv__cond>summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:6px;
  font-size:11.5px;line-height:1.3;color:var(--ink-54);transition:color var(--t-ui) var(--e-out)}
.pv__cond>summary::-webkit-details-marker{display:none}
/* the chevron is an inline SVG, not two rotated borders: a chevron built from
   border-inline-end + border-block-end mirrors under RTL, and a "down" chevron
   must look the same in both directions. `rotate` is direction-neutral. */
.pv__cond__c{inline-size:9px;block-size:9px;flex:none;
  transition:rotate var(--t-ui) var(--e-out)}
.pv__cond[open] .pv__cond__c{rotate:180deg}
.pv__cond>summary:hover{color:var(--navy)}
.pv__cond>summary:focus-visible{outline:2px solid var(--navy);outline-offset:2px;border-radius:2px}
.pv__cond>p{margin:7px 0 0;font-size:11.5px;line-height:1.5;color:var(--ink-54);text-wrap:pretty}

/* ══ THE PANEL POINTS AT ITS CARD ═════════════════════════════════════════
   One shape, not a shape plus a line: a square with the panel's own fill and
   border, rotated 45°, straddling the panel's edge. The two edges that face
   OUT keep the border and continue the panel's outline; the two that face IN
   are transparent, so the arrow's own fill covers the segment of panel border
   it crosses. That is where the seam would otherwise be.
   Rotation makes logical sides meaningless — a rotated border-inline-start is
   not "the leading edge" of anything — so this one element is written
   physically, which is the licence the brief gives it. Everything else in the
   file stays logical. */
.pv__arw{position:absolute;inline-size:11px;block-size:11px;box-sizing:border-box;
  background:var(--white);border:1px solid var(--rule);
  top:var(--arw-y,50%);translate:0 -50%;rotate:45deg}
/* deliberately NOT pointer-events:none — the arrow bridges most of the 10px
   gap between card and panel, and being part of the panel's hover surface is
   what keeps the panel open while the pointer crosses to it */
/* rotate:45deg is CLOCKWISE, so the bottom-left corner ends up pointing left
   and the top-right corner pointing right — those two corners' adjacent edges
   are the ones that must keep the border. */
.pv__arw--l{left:-5.5px;border-top-color:transparent;border-right-color:transparent}
.pv__arw--r{right:-5.5px;border-bottom-color:transparent;border-left-color:transparent}

/* ══ SCHB — PRESS · COLLAPSE · WORKING · ANSWER · EXPAND ═════════════════
   On activation the control COLLAPSES to a disc the height of the button,
   turns progress inside that disc, answers inside that same disc, and then
   expands back.

   ONE GESTURE, REVERSED — not two effects. The collapse and the expansion
   are a single transition on a single property: clip-path. Collapsing adds
   .is-disc, expanding removes it. Same duration, same curve, opposite
   direction. It is a TRANSITION and not a keyframe animation precisely so
   it can be interrupted and retargeted mid-flight (emil-design-eng: "CSS
   transitions can be interrupted and retargeted mid-animation; keyframes
   restart from zero").

   HOW THE LAYOUT IS HELD STILL — by construction, not by arrangement.
   clip-path is a PAINT operation: it never participates in layout. The
   button's box keeps inline-size:100% and its 42px block-size in every
   frame of every phase, and no width, height, inset, margin or padding is
   animated anywhere in this component. animation-vocabulary names the exact
   failure this avoids — "Layout thrashing: animating properties like width,
   height, top or left that force the browser to recalculate layout every
   frame". The label is hidden and never removed, so the intrinsic width is
   unchanged too, and because clipping cannot touch layout the label cannot
   even rewrap as the disc closes over it.

   Durations, in animation-vocabulary's terms, and where each came from:
     Press / tap feedback  100ms Ease-out ......... apple-design §1, verbatim
     Collapse              200ms strong Ease-out ..... emil, 150–250ms band
     Label Fade out        140ms + Blur 2px ...... v9's 140ms, emil's blur
     Working               Loop of a Rotate, 620ms Linear ...... v9 settled
     Answer                Crossfade 140ms .................... v9 settled
     Hold                  900ms Delay ..... NO skill sets this; v9's value
     Expand                the Collapse, reversed ......... the same 200ms
   Reduced motion keeps all three phases as opacity and colour at full
   width, with no collapse at all — never as no feedback. */
.btn{position:relative;overflow:hidden;
  /* the disc's diameter IS the button's block-size: 42px is .btn's own
     min-block-size above, and the button resolves to exactly that (10px
     padding + a 13.5px uppercase line is 36px, so the minimum wins). A host
     whose button is a different height overrides this one custom property
     and nothing else. */
  --schb-disc:42px; --schb-fold:calc(50% - var(--schb-disc) / 2)}
/* The clip is installed only for the duration of a run, so an IDLE v10 button
   paints exactly what v9 painted — including its focus ring, which an
   always-on clip at inset(0) would cut away, since outline is drawn outside
   the border box and clip-path clips outline too. */
.btn.is-morphing{clip-path:inset(0 0 0 0 round var(--r));
  transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out),
             clip-path var(--t-collapse) var(--e-collapse)}
.btn.is-morphing.is-disc{
  clip-path:inset(0 var(--schb-fold) 0 var(--schb-fold) round calc(var(--schb-disc) / 2))}
/* while it is a disc the outline WOULD be clipped away, so the disc carries
   its own ring, inside its own edge where the clip cannot reach it — focus is
   visible in every phase, which is what keyboard parity actually requires */
.btn.is-disc:focus-visible{outline-color:transparent}
.btn.is-disc:focus-visible::after{content:"";position:absolute;
  inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%;
  inline-size:calc(var(--schb-disc) - 6px);block-size:calc(var(--schb-disc) - 6px);
  border:2px solid var(--white);border-radius:999px;pointer-events:none}
/* the label leads the collapse out: 140ms against the clip's 200ms, so it has
   gone before the disc closes over where it was. The 2px blur is emil's
   recipe for exactly this moment — it bridges the gap between two states so
   the eye reads one object changing instead of two objects swapping. */
.btn__lb{display:inline-flex;align-items:center;gap:8px;
  transition:opacity 140ms var(--e-out),filter 140ms var(--e-out)}
.btn.is-pressed{transform:translateY(1px) scale(.985);transition:transform 100ms var(--e-out)}
.btn.is-working,.btn.is-settled,.btn.is-failed{pointer-events:none}
.btn.is-working .btn__lb,.btn.is-settled .btn__lb,.btn.is-failed .btn__lb{opacity:0;filter:blur(2px)}
.btn__spin,.btn__ok,.btn__no{position:absolute;inset-block-start:50%;inset-inline-start:50%;
  translate:-50% -50%;opacity:0;transition:opacity 140ms var(--e-out);pointer-events:none}
.btn.is-working .btn__spin{opacity:1}
.btn.is-settled .btn__ok{opacity:1}
.btn.is-failed .btn__no{opacity:1}
.btn__spin{inline-size:17px;block-size:17px;border-radius:99px;
  border:2px solid rgba(255,255,255,.34);border-block-start-color:#fff;
  animation:schb-spin 620ms linear infinite}
@keyframes schb-spin{to{rotate:360deg}}
.btn__ok,.btn__no{inline-size:17px;block-size:17px;
  background-size:contain;background-repeat:no-repeat}
.btn__ok{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l5 5L19 7'/%3E%3C/svg%3E")}
/* the failing action answers in the disc too, with a mark of its own — the
   disc does not simply give up and reopen with nothing in it */
.btn__no{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 6.5l11 11M17.5 6.5l-11 11'/%3E%3C/svg%3E")}
.btn.is-failed{background:var(--navy)}
.schb-err{margin:6px 0 0;font-size:12px;line-height:1.4;color:var(--navy);
  background:color-mix(in oklab,var(--yellow) 34%,var(--white));
  border-inline-start:3px solid var(--yellow);padding:6px 9px;border-radius:4px;
  opacity:0;transition:opacity 140ms var(--e-out)}
.schb-err.is-in{opacity:1}
/* The language square is the same component, and it does NOT collapse: a 26px
   square is already the size of its own disc, so the collapse would be the
   identity gesture — and a clip on .sq would cut off the ::after that gives it
   its 44px touch target. It keeps v9's behaviour exactly. */
.sq.is-working .sq__lb,.sq.is-settled .sq__lb{opacity:0}
.sq__lb{transition:opacity 140ms var(--e-out)}
.sq.is-pressed{transform:scale(.94);transition:transform 100ms var(--e-out)}
.sq .btn__spin{inline-size:13px;block-size:13px;border-color:var(--ink-22);border-block-start-color:var(--teal)}
.sq.is-working,.sq.is-settled{pointer-events:none}
.sq .btn__ok,.sq .btn__no{inline-size:13px;block-size:13px}
.sq .btn__ok{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23227F77' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l5 5L19 7'/%3E%3C/svg%3E")}
.sq .btn__no{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23122B46' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 6.5l11 11M17.5 6.5l-11 11'/%3E%3C/svg%3E")}
@media(prefers-reduced-motion:reduce){
  /* all three phases survive at FULL WIDTH, carried by opacity and colour
     instead of motion: no collapse, no disc, no blur. The runtime never adds
     the classes here, and these rules neutralise them anyway if the setting
     is changed while a button is mid-run. */
  .btn.is-morphing,.btn.is-morphing.is-disc{clip-path:none;
    transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out)}
  .btn.is-disc:focus-visible{outline-color:var(--navy)}
  .btn.is-disc:focus-visible::after{content:none}
  .btn__spin{animation:none;border-block-start-color:rgba(255,255,255,.8)}
  .sq .btn__spin{border-block-start-color:var(--teal)}
  .btn.is-pressed,.sq.is-pressed{transform:none;opacity:.78}
  .btn.is-working{opacity:.9}
  .btn.is-settled{opacity:1}
  .btn.is-working .btn__lb,.btn.is-settled .btn__lb,.btn.is-failed .btn__lb{filter:none}
  .btn__lb,.btn__spin,.btn__ok,.btn__no,.sq__lb{transition:opacity 120ms var(--e-out)}
}

/* ══ the bench — the four actions, driveable without opening a preview ════ */
.bench{display:flex;flex-wrap:wrap;gap:18px;align-items:flex-start}
.bench__c{inline-size:260px;max-inline-size:100%}
.bench__l{display:block;font-family:var(--f-head);font-size:10.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--ink-38);margin-block-end:6px}
.bench__ref{block-size:3px;background:var(--rule-2);margin-block-start:10px}

/* ══ the panel's secondary action ═════════════════════════════════════════ */
.btn--ghost{background:var(--white);color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink-22)}
.btn--ghost:hover,.btn--ghost:focus-visible{background:var(--wash);color:var(--ink)}
.btn--ghost .btn__spin{border-color:var(--ink-22);border-block-start-color:var(--teal)}
.btn--ghost .btn__ok{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23227F77' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l5 5L19 7'/%3E%3C/svg%3E")}
.pv__or{margin:2px 0;font-size:11px;letter-spacing:.12em;text-transform:uppercase;
        font-family:var(--f-head);font-weight:600;color:var(--ink-38);text-align:center}

/* ══ STUDY ROW · COMPACT · CONTEXTS · shelf fix — all carried over ════════ */
.k--row{flex-direction:row;align-items:stretch}
.k--row .k__im{flex:0 0 196px;align-self:flex-start}
.k--row .k__b{padding:12px 14px 13px;gap:7px}
.k--row .k__t{font-size:15.5px;-webkit-line-clamp:1}
@media(max-width:620px){.k--row{flex-direction:column}.k--row .k__im{flex-basis:auto;align-self:auto}}
.mi--when{font-family:var(--f-head);font-weight:700;font-size:12.5px;letter-spacing:.03em;color:var(--ink-72)}
.mi--when.is-open{color:var(--teal)}
.k__foot{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin:0;margin-block-start:auto;
         padding-block-start:8px;border-block-start:1px solid var(--rule-2);position:relative;z-index:2}
.need{display:inline-flex;align-items:center;gap:7px;font-family:var(--f-head);font-weight:700;font-size:11px;
      letter-spacing:.09em;text-transform:uppercase;color:var(--ink)}
.need::before{content:"";inline-size:8px;block-size:8px;flex:0 0 8px;border-radius:99px;background:var(--yellow)}
.need.is-done{font-weight:600;letter-spacing:.04em;text-transform:none;font-size:11.5px;color:var(--teal)}
.need.is-done::before{inline-size:11px;block-size:11px;flex-basis:11px;border-radius:0;background:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23227F77' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l5 5L19 7'/%3E%3C/svg%3E");
  background-size:contain;background-repeat:no-repeat}
.need.is-none{font-weight:600;font-size:11.5px;letter-spacing:0;text-transform:none;color:var(--ink-38)}
.need.is-none::before{background:none;box-shadow:inset 0 0 0 1px var(--rule)}
.file{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;color:var(--ink-72);text-decoration:none;
      border-block-end:1px solid var(--rule);position:relative;z-index:2}
.file:hover,.file:focus-visible{color:var(--teal);border-block-end-color:var(--teal)}
.k.is-locked .k__im{opacity:.34}
.k.is-locked .k__t{color:var(--ink-54)}
.k.is-locked:hover{transform:none;box-shadow:var(--el-rest);border-color:var(--rule)}
.k--compact{--sq:22px;--sq-gap:8px}
.k--compact .k__b{padding:8px 9px 9px;gap:5px}
.k--compact .k__t{font-size:14px}
.k--compact .k__m{font-size:11.5px;gap:4px}
.k--compact .mi--lang{display:none}
.k--compact .k__amt{font-size:14px}
.k--compact .k__amt.is-words{font-size:11px}
.k--compact .k__note{display:none}
.k--compact .k__altlbl,.k--compact .sq{font-size:9.5px}
.k--compact:hover{transform:translateY(-2px);box-shadow:var(--el-compact)}
.widget{inline-size:320px;max-inline-size:100%;border:1px solid var(--rule);border-radius:var(--r);padding:12px}
.widget h3{font-family:var(--f-head);font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-38);margin:0 0 10px}
.stack{display:flex;flex-direction:column;gap:8px}
/* ══ THE SHELF'S EDGES ═════════════════════════════════════════════════════
   A native scrollbar sat under every shelf: grey, always drawn, and drawn even
   under a row that already fitted — so it was loudest exactly when it had
   nothing to say. What replaces it says one thing, only when it is true, and
   only at the edge it is true about.

   apple-design, §12: "scroll edge effects, not hard dividers" — where content
   meets floating chrome, fade it, and only where the chrome actually overlaps
   content. So the fade is PER EDGE and appears only on the side that still has
   cards behind it. A shelf that fits carries neither fade nor arrow and looks
   like what it is: complete. §9 is the other half — a hard stop reads as
   frozen; an affordance that simply stops being there reads as "you have
   arrived", which is the truth.

   The scrollbar is hidden, not merely thinned. The track keeps its
   padding-and-negative-margin, which is what lets a card lift on hover without
   being clipped by its own scroller; nothing here narrows that box. */
.track{display:flex;gap:12px;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x proximity;
       scrollbar-width:none;                      /* Firefox */
       padding-block:var(--track-pad);margin-block:calc(var(--track-pad) * -1);
       padding-inline:26px;margin-inline:-26px;scroll-padding-inline:26px}
.track::-webkit-scrollbar{width:0;height:0}       /* Chromium, Safari */
.track>*{flex:0 0 274px;width:274px;scroll-snap-align:start}
/* the track is a scroll container the keyboard can reach; arrow keys then move
   it natively. No key handler is bound on purpose — binding one is how a
   scroller starts swallowing PageUp, Home and the vertical axis. */
.track:focus-visible{outline:2px solid var(--teal);outline-offset:-6px;border-radius:var(--r)}
.trk{position:relative}
/* data-more carries "s", "e", both, or the attribute is absent entirely.
   THE EDGE FADE IS GONE. It was correct — measured at opacity 0 on a shelf that
   fits — but on a shelf that does overflow, which is most of them, a white wash
   lying over the last card reads as a rendering fault rather than as an
   affordance. The arrows already say there is more, only on the side there is
   more on, and they say it without touching the cards. One signal is enough. */
/* The arrows: Scale in + Fade in, ease-out, no overshoot. animation-vocabulary
   separates these from "Pop in", which overshoots — and apple-design §4 is
   explicit that overshoot is earned by a gesture that carried momentum. A
   pointer arriving carries none, so this settles rather than bounces. */
.trk__nav{position:absolute;z-index:3;inset-block-start:50%;translate:0 -50%;
       inline-size:38px;block-size:38px;padding:0;display:grid;place-items:center;cursor:pointer;
       border:1px solid var(--rule);border-radius:50%;color:var(--navy);
       background:color-mix(in oklab,var(--white) 78%,transparent);
       -webkit-backdrop-filter:blur(12px) saturate(160%);backdrop-filter:blur(12px) saturate(160%);
       box-shadow:var(--el-compact);
       opacity:0;scale:.9;pointer-events:none;
       transition:opacity 140ms var(--e-collapse),scale 140ms var(--e-collapse)}
.trk__nav--s{inset-inline-start:-5px}
.trk__nav--e{inset-inline-end:-5px}
.trk__nav svg{inline-size:15px;block-size:15px}
.trk__nav--s svg{scale:-1 1}
[dir=rtl] .trk__nav--s svg{scale:1 1}
[dir=rtl] .trk__nav--e svg{scale:-1 1}
.trk[data-more~="s"]:hover .trk__nav--s,.trk[data-more~="s"]:focus-within .trk__nav--s,
.trk[data-more~="e"]:hover .trk__nav--e,.trk[data-more~="e"]:focus-within .trk__nav--e{
       opacity:1;scale:1;pointer-events:auto}
/* press feedback on the press, not on release — apple-design §1. Written with
   the same attribute and state selectors as the reveal rule above so it OUT-
   specifies it; a plain .trk__nav:active would lose and never be seen. */
.trk[data-more]:hover .trk__nav:active,.trk[data-more]:focus-within .trk__nav:active{
       scale:.94;transition-duration:90ms}
/* no hover to reveal them with, and the finger is the affordance */
@media(hover:none),(pointer:coarse){.trk__nav{display:none}}
@media(prefers-reduced-motion:reduce){
  .trk__nav{scale:1;transition:opacity 120ms linear}
  .trk[data-more~="s"]:hover .trk__nav--s,.trk[data-more~="s"]:focus-within .trk__nav--s,
  .trk[data-more~="e"]:hover .trk__nav--e,.trk[data-more~="e"]:focus-within .trk__nav--e{scale:1}
  .trk[data-more]:hover .trk__nav:active,.trk[data-more]:focus-within .trk__nav:active{scale:1}
  .trk::before,.trk::after{transition:none}
}
@media(prefers-reduced-transparency:reduce){
  .trk__nav{background:var(--white);-webkit-backdrop-filter:none;backdrop-filter:none}
}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(252px,1fr));gap:12px}
.rows{display:flex;flex-direction:column;gap:12px}
.prove{margin-block-start:14px;display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap}
.prove button{font-family:var(--f-head);font-weight:600;font-size:11px;letter-spacing:.12em;text-transform:uppercase;
              background:var(--navy);color:var(--white);border:0;padding:8px 13px;border-radius:var(--r);cursor:pointer}
.ok{color:var(--teal);font-weight:600}
.no{color:var(--white);background:var(--navy);font-weight:600;padding:0 4px}
@media(prefers-reduced-motion:reduce){.k{transition:box-shadow 120ms var(--e-out)}.k:hover,.k:active{transform:none!important}}

/* ══ THE PAGE ═════════════════════════════════════════════════════════════
   It sits in a BuddyBoss content column: .wrap is the column, 1180px, not
   full-bleed. There is no hero, no statistic and no claim anywhere below —
   a coach came here to find a course, and apple-design's sixth principle is
   that every element earns its place. The lede is one line because one line
   is what is needed to say where you are. */
/* ══ THE CLAIM — THREE ATTEMPTS ═══════════════════════════════════════════
   v4 set a paragraph at 30px on a short measure and called the measure an
   idea. It was not: a paragraph at a different size is still a paragraph, and
   it read as something typed into a Gutenberg block because structurally that
   is all it was. Each of the three below has actual STRUCTURE — a second
   element that the line is in a relationship with. Toggle A/B/C in the demo
   strip; all three take the same three strings and work in all four locales
   and all three viewer cases.
   None of them moves. Same reason as v4: first thing on the page, every
   visit — emil-design-eng's frequency table says remove, not reduce. */
.claim{margin:0 0 16px}
.claim__b{margin:0}
.claim__a{margin:0;font-family:var(--f-head);font-weight:700;font-size:12.5px;
  letter-spacing:.06em;text-transform:uppercase;line-height:1.45;color:var(--teal);
  text-wrap:pretty}

/* ── A · THE FACT ─────────────────────────────────────────────────────────
   The line is paired with the live count, so the page opens with a FACT
   rather than a promise: "148 — courses from coaches around the world". The
   figure is the object; the sentence becomes its caption. web-design-engineer
   Step 3a again, but with a different answer to the first question: narrative
   role DATA, not pull-quote, and a figure is a different register from a
   sentence — which is exactly why it stops reading as prose. The hairline
   between the two columns is the structure holding them together. */
.claim--a{display:grid;grid-template-columns:auto minmax(0,1fr);gap:0 20px;align-items:start}
.claim--a .claim__n{font-family:var(--f-head);font-weight:700;font-size:54px;line-height:.84;
  letter-spacing:-.02em;color:var(--navy);font-variant-numeric:tabular-nums;
  padding-block-start:2px}
.claim--a .claim__c{border-inline-start:1px solid var(--rule);padding-inline-start:20px;
  min-inline-size:0}
.claim--a .claim__b{font-size:17px;line-height:1.4;color:var(--ink);max-inline-size:44ch;
  text-wrap:pretty}
.claim--a .claim__a{margin-block-start:8px}/* ── B · THE RULE ─────────────────────────────────────────────────────────
   One hairline runs the full width of the column. The promise hangs off its
   start, the personal note off its far end. The rule is not decoration — it is
   the measure of the page, and the two texts are placed against it like an
   entry in a table of contents. The asymmetry is the object; delete the rule
   and there is nothing left, which is the test web-design-engineer's rubric
   asks ("if I delete this, does the design get worse?"). Logical alignment, so
   RTL mirrors it without a rule of its own. *//* ── C · THE CASCADE ──────────────────────────────────────────────────────
   The base line already has two halves — a promise and a range, split at the
   em-dash the copy is written with. They are set as two steps of a stair, in
   two weights and two inks, and the personal note is the third step. The
   indent is the structure: three things at three depths read as one object
   with a hierarchy, where three flush-left lines read as a paragraph.
   The split is editorial, not magic — the settings screen documents it. */

@media(max-width:640px){
  .claim--a{grid-template-columns:minmax(0,1fr);gap:6px}
  .claim--a .claim__n{font-size:42px}
  .claim--a .claim__c{border-inline-start:0;padding-inline-start:0}
  .claim__a{font-size:11.5px}
}

/* ── the search field ─────────────────────────────────────────────────── */
.q{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-block-end:6px}
.q__w{position:relative;flex:1 1 320px;min-inline-size:0}
.q__i{inline-size:100%;font:inherit;font-size:15px;color:var(--ink);
      padding-block:11px;padding-inline:38px 34px;background:var(--white);
      border:1px solid var(--ink-22);border-radius:var(--r);
      transition:border-color var(--t-ui) var(--e-out),box-shadow var(--t-ui) var(--e-out)}
.q__i:focus{outline:0;border-color:var(--teal);box-shadow:0 0 0 3px color-mix(in oklab,var(--teal) 18%,transparent)}
.q__i::placeholder{color:var(--ink-38)}
/* a 16px svg on a text baseline inside a 15px span: the box was smaller than
   its content in both axes, so it too reported an overflow it could not
   scroll. Grid, and the icon sized to the box, rather than an overflow rule. */
.q__ic{position:absolute;inset-inline-start:12px;inset-block-start:50%;translate:0 -50%;
       inline-size:15px;block-size:15px;display:grid;place-items:center;line-height:0;
       color:var(--ink-38);pointer-events:none}
.q__ic svg{display:block;inline-size:100%;block-size:100%}
.q__x{position:absolute;inset-inline-end:6px;inset-block-start:50%;translate:0 -50%;
      inline-size:26px;block-size:26px;display:none;place-items:center;border:0;background:none;
      color:var(--ink-54);cursor:pointer;border-radius:50%}
.q__x:hover{color:var(--navy);background:var(--wash)}
.q.is-typed .q__x{display:grid}
.q__n{font-family:var(--f-head);font-weight:700;font-size:12.5px;letter-spacing:.07em;
      text-transform:uppercase;color:var(--ink-54);white-space:nowrap;font-variant-numeric:tabular-nums}/* ── the axis rail ────────────────────────────────────────────────────── *//* The rail at rest is NOT sticky any more. It sat here occupying close to half
   the viewport with a site header above it; the condensed line below replaces
   that job. `is-stuck` is gone rather than left dormant. */

/* one value. A chip, not a tab — the tab strip is gone and this replaces it. */
.v{display:inline-flex;align-items:center;gap:6px;min-block-size:28px;padding-inline:10px;
   font-family:var(--f-head);font-weight:600;font-size:12px;letter-spacing:.03em;
   color:var(--ink-72);background:var(--white);border:1px solid var(--ink-22);
   border-radius:4px;cursor:pointer;
   transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out),
              border-color var(--t-ui) var(--e-out),opacity var(--t-ui) var(--e-out)}
.v:hover{border-color:var(--navy);color:var(--navy)}
.v__n{font-size:11px;color:var(--ink-38);font-variant-numeric:tabular-nums;
      transition:color var(--t-ui) var(--e-out)}
.v[aria-pressed=true]{background:var(--navy);color:var(--white);border-color:var(--navy)}
.v[aria-pressed=true] .v__n{color:color-mix(in oklab,var(--white) 62%,var(--navy))}
/* A value with nothing behind it stays VISIBLE and stays FOCUSABLE. It is
   aria-disabled rather than disabled: `disabled` would drop it out of the tab
   order, and a rail whose job is to teach the shape of the catalogue must let
   a keyboard reader hear the empty branches too. */
.v[aria-disabled=true]{opacity:.4;cursor:default;background:var(--white);
  border-color:var(--rule);color:var(--ink-54)}
.v[aria-disabled=true]:hover{border-color:var(--rule);color:var(--ink-54)}
.v:focus-visible{outline:2px solid var(--teal);outline-offset:2px}

/* ── THE RAIL, QUIETER ────────────────────────────────────────────────────
   One alternative, one toggle, no third. Structure untouched: same rows, same
   chips, same counts in the same places. Only colour weight moves — a resting
   chip gives up its border and its fill and is carried by type weight alone,
   the hairline between axes does the dividing, and the selected value is left
   as the only strong colour on the rail. Teal rather than navy: with nothing
   else filled, navy reads as another piece of structure, teal reads as a
   choice someone made. */
.rail--quiet .v{background:transparent;border-color:transparent;color:var(--ink-54);font-weight:700}
.rail--quiet .v:hover{background:var(--wash);border-color:transparent;color:var(--navy)}
.rail--quiet .v__n{color:var(--ink-22)}
.rail--quiet .v[aria-pressed=true]{background:var(--teal);border-color:var(--teal);color:var(--white)}
.rail--quiet .v[aria-pressed=true] .v__n{color:color-mix(in oklab,var(--white) 66%,var(--teal))}
.rail--quiet .v[aria-disabled=true]{opacity:.34;background:transparent;border-color:transparent}
.rail--quiet .v--sub{border-style:none}
.rail--quiet .v--all{border-style:solid;border-color:var(--teal)}
.rail--quiet .chip{background:var(--teal)}
.rail--quiet .chip:hover{background:color-mix(in oklab,var(--teal) 86%,var(--navy))}/* the second level: a programme's own sections, revealed in place.
   grid-template-rows 0fr→1fr is the height animation that needs no measured
   pixel value and therefore cannot be wrong at a different font size. */
.v--sub{font-weight:600;font-size:11.5px;border-style:dashed}
.v--all{border-style:solid;border-color:var(--teal);color:var(--teal)}
.v--all:hover{background:var(--teal);color:var(--white);border-color:var(--teal)}/* the active selection, as removable chips, beside the count */
.chip{display:inline-flex;align-items:center;gap:7px;min-block-size:26px;padding-inline:9px 7px;
      font-size:12px;color:var(--white);background:var(--navy);border:0;border-radius:4px;cursor:pointer;
      transition:background var(--t-ui) var(--e-out)}
.chip:hover{background:color-mix(in oklab,var(--navy) 86%,var(--white))}
.chip svg{inline-size:11px;block-size:11px;opacity:.7}
.chip:focus-visible{outline:2px solid var(--teal);outline-offset:2px}

/* ── the two views, and the one transition between them ───────────────── */
/* Named in animation-vocabulary's terms: a Fade out + Slide out, then a Fade
   in + Slide in, held together by Orchestration and made direction-aware by
   Spatial consistency — the axis of travel says which way you went. It is a
   TRANSITION and not a keyframe animation so it is interruptible and can be
   retargeted mid-flight; reversing is the same two halves with the sign
   flipped, not a second pair of rules. */
.view{transition:opacity var(--t-in) var(--e-collapse),translate var(--t-in) var(--e-collapse)}
.view[hidden]{display:none}
.view.is-out{opacity:0;transition-duration:var(--t-out)}
/* going deeper: what leaves rises, what arrives comes up from below */
.views[data-dir=in]  .view.is-out{translate:0 -10px}
.views[data-dir=in]  .view.is-pre{translate:0 12px}
/* coming back: the same motion, reversed */
.views[data-dir=out] .view.is-out{translate:0 10px}
.views[data-dir=out] .view.is-pre{translate:0 -12px}
.view.is-pre{opacity:0;transition:none}

/* ── shelves ──────────────────────────────────────────────────────────── */
/* ══ THE MEMBERSHIP BAND ═══════════════════════════════════════════════════
   This page's own language, not the card's: the catalogue's palette, Rajdhani
   for the headings, --r for the radius and --el-panel for the depth. It reads
   as a break in the page, like the partner section, not as a card that grew. */
.pmb{margin-block-start:34px;padding:24px 26px 22px;border-radius:var(--r);
     background:linear-gradient(180deg,var(--navy) 0%,var(--navy-deep) 100%);
     color:var(--white);box-shadow:var(--el-panel);position:relative;overflow:hidden}
.pmb__top{display:flex;flex-direction:column;gap:4px;margin-block-end:16px}
.pmb__e{margin:0;font-family:var(--f-head);font-weight:600;font-size:11px;letter-spacing:.16em;
        text-transform:uppercase;color:var(--teal-lt)}
.pmb__h{margin:0;font-family:var(--f-head);font-weight:700;font-size:27px;line-height:1.12;
        letter-spacing:.004em;color:var(--white);max-inline-size:22ch}
/* THE TEN LINES, IN COLUMNS. auto-fit keeps each line roughly the length it had
   in the 320px card; at 1200px that is four columns and three rows, not ten. */
.pmb__bs{list-style:none;margin:0;padding:0;display:grid;gap:9px 26px;
         grid-template-columns:repeat(auto-fit,minmax(15.5rem,1fr))}
.pmb__b{font-size:14px;line-height:1.45;color:rgba(255,255,255,.86);
        padding-inline-start:19px;position:relative}
.pmb__b::before{content:"";position:absolute;inset-inline-start:0;inset-block-start:.62em;
        inline-size:7px;block-size:7px;border-radius:50%;background:var(--teal-lt)}
.pmb__b b{color:var(--white);font-weight:600}
.pmb__foot{margin-block-start:20px}
/* the yellow: a call to action, which is what the yellow is for */
.pmb__cta{display:inline-flex;align-items:center;gap:8px;
     font-family:var(--f-head);font-weight:700;font-size:13px;letter-spacing:.08em;text-transform:uppercase;
     background:var(--yellow);color:var(--navy);text-decoration:none;
     padding:11px 22px;border-radius:var(--r);
     transition:transform var(--t-ui) var(--e-collapse),box-shadow var(--t-ui) var(--e-collapse)}
.pmb__cta:hover{transform:translateY(-1px);box-shadow:0 6px 18px rgba(241,196,15,.28)}
.pmb__cta:active{transform:translateY(0)}
.pmb__cta:focus-visible{outline:2px solid var(--white);outline-offset:3px}
/* THE MEMBER'S BAND: quieter, shorter, and nothing to buy. Two actions, no yellow —
   nothing here is a sale, so the colour that means "buy" has no business in it. */
.pmb--member{padding-block:20px}
.pmb--member .pmb__h{font-size:21px;max-inline-size:34ch}
.pmb__acts{display:flex;flex-wrap:wrap;gap:10px;margin-block-start:14px}
.pmb__act{font-family:var(--f-head);font-weight:600;font-size:12.5px;letter-spacing:.06em;
     text-transform:uppercase;text-decoration:none;padding:10px 18px;border-radius:var(--r);
     background:rgba(255,255,255,.10);color:var(--white);border:1px solid rgba(255,255,255,.26);
     transition:background var(--t-ui) var(--e-out),border-color var(--t-ui) var(--e-out)}
.pmb__act:hover{background:rgba(255,255,255,.18);border-color:rgba(255,255,255,.42)}
.pmb__act:focus-visible{outline:2px solid var(--teal-lt);outline-offset:3px}
@media(max-width:700px){
  .pmb{padding:20px 18px}
  .pmb__h{font-size:22px}
  .pmb__bs{grid-template-columns:1fr}   /* the original list, on the original width */
}
@media(prefers-reduced-motion:reduce){.pmb__cta{transition:none}.pmb__cta:hover{transform:none}}
.sh{margin-block-start:34px}
.sh__h{display:flex;align-items:baseline;justify-content:space-between;gap:16px;
       margin-block-end:11px;flex-wrap:wrap}
.sh__t{font-family:var(--f-head);font-weight:700;font-size:19px;letter-spacing:.01em;
       color:var(--ink);margin:0}
.sh__n{font-family:var(--f-head);font-size:12px;color:var(--ink-38);margin-inline-start:9px;
       font-variant-numeric:tabular-nums}
.sh__all{font-family:var(--f-head);font-weight:600;font-size:11.5px;letter-spacing:.08em;
         text-transform:uppercase;color:var(--teal);background:none;border:0;cursor:pointer;
         text-decoration:none;   /* it is a <button> on most shelves and an <a> on one that links away */
         padding:5px 9px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;
         border-radius:var(--r);
         transition:color var(--t-ui) var(--e-out)}
/* THE THEME FILLS A HOVERED BUTTON TEAL AND LEAVES THE LABEL DARK, so this read
   as a solid block with an unreadable word in it, and drew its own border round
   the text. Every one of these is declared here rather than inherited: the fill,
   the label, the border and the outline. Scoped to the page so the theme's own
   button rules cannot out-specify it — the same fault the filter chips had. */
.schcat-page .sh__all:hover,
.schcat-page .sh__all:focus-visible{background:var(--teal);color:var(--white);border:0;box-shadow:none}
.schcat-page .sh__all:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.schcat-page .sh__all:hover{outline:0}
.sh__all svg{inline-size:11px;block-size:11px}
[dir=rtl] .sh__all svg{scale:-1 1}
.sh__all:focus-visible{outline:2px solid var(--teal);outline-offset:3px;border-radius:2px}

/* ── the results grid ─────────────────────────────────────────────────── */
.more{display:block;margin:26px auto 0;font-family:var(--f-head);font-weight:700;font-size:12.5px;
      letter-spacing:.08em;text-transform:uppercase;color:var(--navy);background:var(--white);
      border:1px solid var(--ink-22);border-radius:var(--r);padding:12px 22px;cursor:pointer;
      transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out),border-color var(--t-ui) var(--e-out)}
.more:hover{background:var(--navy);color:var(--white);border-color:var(--navy)}
.more:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.none{padding-block:52px;text-align:center;color:var(--ink-54);font-size:15px}
.none b{display:block;font-family:var(--f-head);font-size:18px;color:var(--ink);margin-block-end:6px}

/* ── the partner row, directly beneath the claim ──────────────────────────
   No heading, no caption, one line. It shares the claim's inline-start edge,
   which is what attaches it to the claim rather than leaving it floating.
   Beyond what fits it SCROLLS — it never becomes a second row, because a
   second row of grey marks under the claim would start to look like content.
   Any length: right at 3, right at 12, and nothing at all when the list is
   empty. */
.pt{margin:0 0 20px;display:flex;gap:14px;align-items:center;
    overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none}
.pt::-webkit-scrollbar{display:none}
.pt__i{flex:none;display:inline-flex;align-items:center;justify-content:center;
       min-block-size:30px;min-inline-size:58px}
/* REAL CRESTS ARE COLOURFUL AND WILDLY DIFFERENT IN SHAPE. Three things keep
   the row from becoming a rainbow strip of mismatched marks:

   OPTICAL HEIGHT, NOT BOX HEIGHT. A fixed block-size makes a tall shield
   loom and a wide wordmark shrink to nothing. Capping BOTH axes and letting
   object-fit:contain place the mark inside that area gives every crest the
   same apparent SIZE regardless of its aspect — a shield uses the height, a
   wordmark uses the width, and neither dominates.

   MUTED AT REST. grayscale(1) at 52% opacity: these are context, not content,
   and they sit directly under the claim where colour would out-shout it.
   Full colour on hover, so a coach can still find their own federation.

   MULTIPLY. Real federation PNGs often ship on a white plate; multiply drops
   that plate into the page's own white instead of leaving a visible box. */
.pt__l{max-block-size:26px;max-inline-size:86px;inline-size:auto;block-size:auto;
       object-fit:contain;display:block;mix-blend-mode:multiply;
       filter:grayscale(1);opacity:.52;
       transition:filter var(--t-ui) var(--e-out),opacity var(--t-ui) var(--e-out)}
.pt__i:hover .pt__l{filter:grayscale(0);opacity:1}
/* the fallback: exactly how thumbs/ behaves — the lettered chip is the markup,
   the image sits over it, and onerror removes the image */
.pt__t{display:inline-flex;align-items:center;justify-content:center;
       block-size:26px;padding-inline:11px;min-inline-size:58px;
       border:1px solid var(--rule);border-radius:3px;
       font-family:var(--f-head);font-weight:700;font-size:11px;letter-spacing:.09em;
       color:var(--ink-38);white-space:nowrap;
       transition:color var(--t-ui) var(--e-out),border-color var(--t-ui) var(--e-out)}
.pt__i:hover .pt__t{color:var(--ink-72);border-color:var(--ink-22)}
.pt__i.has .pt__t{display:none}
/* an empty admin list renders nothing at all, not an empty gap */
.pt:empty{display:none;margin:0}

@media(prefers-reduced-motion:reduce){
  /* the composed transition becomes a plain cross-fade: apple-design §14 —
     a gentler equivalent, never nothing */
  .view{transition:opacity 120ms linear}
  .views[data-dir=in] .view.is-out,.views[data-dir=out] .view.is-out,
  .views[data-dir=in] .view.is-pre,.views[data-dir=out] .view.is-pre{translate:none}
}/* ══════════════════════════════════════════════════════════════════════════
   v6 — THE HERO, THE COLLAPSED FILTER ROW, THE PARTNER SECTION
   ══════════════════════════════════════════════════════════════════════════ *//* ── THE HERO ─────────────────────────────────────────────────────────────
   web-design-engineer's five dials, set before anything below was chosen:
     visual variance   7  — a real departure is wanted
     motion            1  — seen on every single visit
     information density 2 in this band, against 7 in the page beneath
     asset dependence  1  — FORCED there by the brief: no photograph, no
                            illustration
     brand fidelity    9  — the palette is not negotiable
   Two of its rules then decide the whole thing.

   FIRST: "high variance — preserve a stable navigation and grid spine;
   concentrate experimentation in ONE layer." So the hero is the one place
   that departs. The filter row, the shelves and the cards keep the spine
   exactly as they are.

   SECOND, and this is the one that produced the idea: the skill's asset
   protocol says never to substitute CSS silhouettes or hand-drawn shapes for
   real imagery — "the result is generic tech aesthetic any brand could wear."
   Asset dependence is pinned at 1 by the brief. So decorative geometry is out
   by instruction, and the only honest material left is the catalogue's OWN
   CONTENT. The right column is the real vocabulary — all 21 themes and every
   programme this viewer may see — set tiny and low-contrast as texture. It is
   not a tag cloud: one size, no boxes, no links, no varying weight. It reads
   as a field, and it repays exactly what collapsing the rail costs the
   reader — the sense of how large this catalogue's vocabulary is.

   apple-design supplies the restraint. §6 Simplicity: every element earns its
   place, and "sometimes adding context simplifies" — which is the argument for
   the vocabulary field being information rather than ornament. §14 and the
   frequency table supply the answer on motion: none. *//* one hairline of teal along the leading edge — the only ornament, and it is
   the same teal that means "recognised" everywhere else on the page */
@media(max-width:900px){
}
@media(max-width:640px){          /* the field needs width to read as a field */
}


/* ── THE PARTNER SECTION ──────────────────────────────────────────────────
   Between the second and third shelf, with air above and below, so it reads as
   a break in the page rather than a strip at its foot. Not shown in results.
   Real crests are round, wide, or shield-shaped and none of them agree about
   weight. Capping BOTH axes with object-fit:contain gives them a common
   apparent SIZE; grayscale plus a ceiling on opacity gives them a common
   apparent WEIGHT, which is the harder half — a dense navy shield and a thin
   wordmark otherwise sit at different distances from the reader. */
.ptsec{margin-block:52px;padding-block:40px;border-block:1px solid var(--rule-2);
  text-align:center}
.ptsec__l{margin:0 auto 26px;max-inline-size:52ch;font-size:15.5px;line-height:1.5;
  color:var(--ink-72);text-wrap:pretty}
.ptsec__r{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:26px 44px;max-inline-size:900px;margin-inline:auto}
.ptsec__i{flex:none;display:inline-flex;align-items:center;justify-content:center;
  min-inline-size:96px;min-block-size:52px}
.ptsec__g{max-block-size:52px;max-inline-size:132px;inline-size:auto;block-size:auto;
  object-fit:contain;display:block;mix-blend-mode:multiply;
  filter:grayscale(1);opacity:.46;
  transition:filter var(--t-ui) var(--e-out),opacity var(--t-ui) var(--e-out)}
.ptsec__i:hover .ptsec__g{filter:grayscale(0);opacity:1}
.ptsec__t{display:inline-flex;align-items:center;justify-content:center;block-size:40px;
  padding-inline:18px;border:1px solid var(--rule);border-radius:4px;
  font-family:var(--f-head);font-weight:700;font-size:14px;letter-spacing:.09em;
  color:var(--ink-38)}
.ptsec__i.has .ptsec__t{display:none}
.ptsec:empty{display:none}
@media(max-width:640px){
  .ptsec{margin-block:36px;padding-block:28px}
  .ptsec__l{font-size:14px;margin-block-end:20px}
  .ptsec__r{gap:20px 28px}
  .ptsec__g{max-block-size:38px;max-inline-size:100px}
}


/* ══════════════════════════════════════════════════════════════════════════
   THE HERO — T2 · ÚSVIT, lifted whole from hero-v3
   The glass pane, the warm field, the one drifting light, the letterform
   sweep, the backdrop-filter fallback and the reduced-transparency answer,
   all unchanged. What the move into the page DID force is in the note: the
   hero is built once and only its two sentences are updated, because this
   page repaints on every keystroke and a rebuilt hero would restart the
   sweep and reset the 112-second orbit each time a letter is typed.
   ══════════════════════════════════════════════════════════════════════════ */
.h{position:relative;overflow:hidden;border-radius:10px;padding:30px;isolation:isolate;
   background:var(--white);margin-block-end:16px}
.h__f{position:absolute;inset:0;z-index:0}
.h__f i{position:absolute;display:block;border-radius:50%;filter:blur(8px)}

/* ── T2 · ÚSVIT — warm, the light high on the leading side ────────────────
   ┌──────────────────────────────────────────────────────────────────────┐
   │ YELLOW MAY BE A LIGHT, NEVER A SECOND MARK.                          │
   │ #F1C40F has exactly one other use on this platform — the Naživo      │
   │ ribbon, where it means urgency. It is used here as the warm core of  │
   │ a diffuse, edgeless light that never resolves into a shape, because  │
   │ colour meaning attaches to MARKS, not to light.                      │
   │ If a future element wants yellow as a chip, a border, a rule or      │
   │ text, THIS gives way — not the ribbon.                               │
   └──────────────────────────────────────────────────────────────────────┘
   Where #F1C40F earns its place: not as gold, but as the warm core of a light
   sitting high-left, at 20% opacity over teal, the way an early sun sits over
   a pitch. It is the same idea attempt A had, without committing the page to
   a dark band. Navy grounds the trailing bottom. Warmest of the three. */

.h--t2{background:linear-gradient(152deg,
  color-mix(in oklab,var(--yellow) 12%,var(--white)) 0%,
  color-mix(in oklab,var(--teal-lt) 20%,var(--white)) 44%,
  color-mix(in oklab,var(--navy) 9%,var(--white)) 100%)}


.h--t2 .h__f i:nth-child(1){inline-size:74%;block-size:140%;
  inset-block-start:-30%;inset-inline-start:18%;
  background:radial-gradient(closest-side,var(--teal) 0%,transparent 70%);opacity:.42}

.h--t2 .h__f i:nth-child(2){inline-size:56%;block-size:120%;
  inset-block-end:-72%;inset-inline-end:-12%;
  background:radial-gradient(closest-side,var(--navy) 0%,transparent 66%);opacity:.3}

/* ══════════════════════════════════════════════════════════════════════════
   THE LIGHT THAT MOVES
   One source. It is LIGHT that moves, not colour: no stop changes hue, nothing
   cycles, nothing pulses. The layer is painted once and then only transformed,
   so the field itself never repaints — which is the whole reason it is a
   transform and not an animated gradient. Animating colour stops would mark
   the element dirty every frame and repaint a 1128px-wide gradient at 60Hz,
   and that is what would make a 148-card page stutter.

   THE PATH IS A CIRCLE, AND THAT IS WHY THE TIMING IS LINEAR.
   rotate(θ) translate(r) rotate(-θ) traces a circle while leaving the element
   itself unrotated. A closed path returns exactly to its start, so the loop
   has no seam and needs no easing to hide one — which answers the brief's
   question about the curve: a drift wants no easing at either end, and an
   orbit is the one shape that lets you have none. `alternate` with an
   ease-in-out would have given the light two visible pauses per cycle, and a
   light that hesitates reads as breathing, not as drifting.
   In animation-vocabulary's terms: an Orbit, on a Loop, Linear — the glossary
   reserves linear for continuous marquee-class motion, which this is.

   FORTY SECONDS, and 112 was wrong. The test is a reader who glances at the
   hero twice, ten seconds apart, seeing that the light has moved. The
   arithmetic: the light's centre travels a circle of radius 42px, so in ten
   seconds
       at 112s it turns 32°  →  displacement 2·42·sin(16°) = 23px
       at  40s it turns 90°  →  displacement 42·√2         = 59px
   23px of a 700px-wide diffuse glow is nothing. 59px is plainly a different
   picture. Meanwhile the instantaneous speed only rises from 2.4px/s to
   6.6px/s, which is still below the rate at which a soft, low-contrast form
   announces itself in peripheral vision: noticeable if you look, ignorable if
   you do not, which is the whole of the brief.
   THE COST DOES NOT CHANGE. A transform animation costs the same per frame
   however far it moves — there are 60 frames a second at either speed. Only
   the perception changed.

   IT DOES NOT COMPETE. It is behind the glass and behind the sentence, its
   opacity is unchanged from the static version, and the sweep — which greets
   once and stops — remains the only thing that asks for attention. */
.h__d{position:absolute;border-radius:50%;filter:blur(10px);pointer-events:none;
  will-change:transform;
  animation:drift 40s linear infinite var(--play)}
@keyframes drift{
  from{transform:rotate(0deg) translate(var(--r,40px)) rotate(0deg)}
  to  {transform:rotate(360deg) translate(var(--r,40px)) rotate(-360deg)}}
/* off screen, it stops costing anything at all. An IntersectionObserver, not
   a scroll handler — the hero leaves the viewport after one screen of a very
   long page and there is no reason to keep compositing it. */
.h.is-off .h__d{animation-play-state:paused}

/* T2's moving source is the warm core; its teal and navy stay put. */
.h--t2 .h__d{--r:42px;inline-size:66%;block-size:130%;
  inset-block-start:-64%;inset-inline-start:-10%;
  background:radial-gradient(closest-side,var(--yellow) 0%,
    color-mix(in oklab,var(--yellow) 40%,transparent) 40%,transparent 72%);opacity:.34}
/* ── the pane ─────────────────────────────────────────────────────────────
   apple-design §11: exactly ONE translucent layer — "never stack a light
   translucent surface on another" — a specular hairline on the top edge, and
   a shadow that separates it from the field rather than pretending to be
   depth on its own. */
.h__p{position:relative;z-index:1;border-radius:8px;padding:38px 36px 34px;
  background:color-mix(in oklab,var(--white) 58%,transparent);
  backdrop-filter:blur(44px) saturate(185%);-webkit-backdrop-filter:blur(44px) saturate(185%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 10px 34px rgba(11,28,47,.14)}

/* ── the sweep, carried over from attempt D ───────────────────────────────
   THE ONLY MOTION ON THE PAGE. It runs ONCE, over 4.2s, on --e-out
   (cubic-bezier(.23,1,.32,1)), and LANDS — animation-fill-mode:forwards holds
   it at the position the base declaration also names. In animation-vocabulary
   terms it is a Shimmer, a moving sheen, but run once and brought to rest
   rather than Looped; the glossary has no word for a sheen that lands, so
   here are the parts instead: Shimmer + Ease-out + forwards, no Loop.

   WHY ONCE, AND WHY 4.2s. v1 looped it every 11s. This is now the only thing
   moving on the first screen of a page a coach opens weekly, and
   emil-design-eng's frequency table is unambiguous about what to do with
   motion seen that often: remove it or reduce it drastically. An eternal
   shimmer at the top of a working catalogue is motion the reader can never
   get away from. So it greets once and stops. Ease-out means most of the
   travel happens in the first second and it settles into place, which is what
   a sheen coming to rest should do rather than crossing at constant speed.

   AND THE REST POSITION IS THE DESIGN. Reduced motion switches the animation
   off BY NAME, so the base background-position applies — and that is the
   identical end state, arrived at instantly. Reduced motion here is not a
   degraded version of the effect; it is the effect's destination without the
   journey. */
.h .hb{margin:0;font-family:var(--f-head);font-weight:600;font-size:40px;line-height:1.08;
  letter-spacing:-.02em;max-inline-size:26ch;text-wrap:balance;
  background:linear-gradient(100deg,
    var(--navy) 0%,var(--navy) 33%,
    color-mix(in oklab,var(--teal) 84%,var(--navy)) 47%,
    var(--navy) 61%,var(--navy) 100%);
  background-size:260% 100%;background-position:42% 0;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  animation:var(--sweep,sweep) calc(4200ms * var(--k)) var(--e-hero) forwards}
@keyframes sweep{from{background-position:172% 0}to{background-position:42% 0}}
.h .ha{margin:18px 0 0;inline-size:auto;max-inline-size:none;
  padding-block-start:11px;border-block-start:1px solid color-mix(in oklab,var(--teal) 52%,transparent);
  font-family:var(--f-head);font-weight:700;font-size:12.5px;letter-spacing:.06em;
  text-transform:uppercase;line-height:1.45;color:color-mix(in oklab,var(--teal) 88%,var(--navy))}

/* ── WHERE backdrop-filter IS NOT AVAILABLE ───────────────────────────────
   This is part of the design, not a shrug. The pane becomes an opaque near-
   white surface: the field still surrounds it, the specular hairline and the
   shadow still separate it, and the sweep is untouched — the only thing lost
   is the field showing THROUGH the glass. To keep the join from looking cut
   out, the surface is warmed 4% toward the field rather than pure white, and
   the shadow is softened so the panel sits on the field instead of over it.
   The same rules answer prefers-reduced-transparency, which apple-design §14
   asks for by name: "make translucent surfaces frostier/solid — raise
   background opacity, drop the blur." */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .h__p{background:color-mix(in oklab,var(--white) 96%,var(--teal));
        box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 6px 22px rgba(11,28,47,.10)}
}
@media(prefers-reduced-transparency:reduce){
  .h__p{background:color-mix(in oklab,var(--white) 96%,var(--teal));
        backdrop-filter:none;-webkit-backdrop-filter:none;
        box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 6px 22px rgba(11,28,47,.10)}
}
@media(max-width:640px){
  .h{padding:14px}  .h__p{padding:24px 20px 22px}
  .h .hb{font-size:26px;max-inline-size:24ch}
  .h .ha{font-size:11.5px;margin-block-start:15px}
}/* ══════════════════════════════════════════════════════════════════════════
   THE FILTER BAND
   Always visible. Nothing here opens, nothing overlays, and no change of mind
   costs an open — which is the whole reason the popover was dropped.

   ORDERING. The most populated values come first: with no usage data in
   existence, "where the courses actually are" is the honest stand-in for
   "what matters", and it is arguably the better default anyway. The order is
   computed ONCE over the unfiltered corpus and then held — values that
   reshuffle under the reader's hand as counts change are worse than values in
   a slightly stale order. It is also what makes the disclosure defensible:
   what gets hidden is always the sparse tail, never the useful head.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══ THE BAND ═════════════════════════════════════════════════════════════
   Lines that share one label gutter. The gutter is the spatial idea: axes
   aligned to a common leading edge read as ONE object, where free-floating
   bands read as several.

   THE HEIGHT PROBLEM. The first version solved it by scrolling each line
   sideways, and sideways scrolling on a filter is a way of hiding things
   from a mouse. So the line WRAPS NOTHING and instead ends at the edge, with
   the surplus moved into a disclosure that grows the axis DOWNWARD in place.
   That means the band does change height — which animation-vocabulary's
   "Layout thrashing" entry warns about — so it is animated the one way that
   costs nothing to measure: grid-template-rows 0fr → 1fr on the revealed
   block, never a height in pixels. And the band is capped, so no amount of
   expanding can make it taller than half the screen. */
/* STICKY AGAIN, AND EVERYTHING THAT CAN BE MADE TRUE ABOUT IT, IS.
   The band pins under --site-header, never behind it, and never above it:
   z-index 70 against the header's 90. It keeps its own space in the flow — no
   negative vertical margin anywhere — so the content below still begins beneath
   it, and the flow gap is the same positive number in every state.

   THE ONE THING THAT CANNOT BE MADE TRUE is in the README, with the arithmetic:
   once a bar is pinned, content that was below it in the document moves up past
   it. That is not a bug in this band, it is the definition of sticky, and no
   z-index, margin or padding changes it. What IS controlled here:

     · nothing ever LANDS under the band — :root carries a
       scroll-padding-block-start of header + band height, kept in sync by a
       ResizeObserver, so every anchor, focus and keyboard scroll stops below it;
     · the stuck band reads as chrome rather than as a clipped card, because
       .is-stuck gives it an edge shadow the moment it detaches;
     · an EXPANDED band never sticks at all, so it can never swallow the
       viewport — see .ba.is-free and syncStick();
     · expanding and collapsing do not tear — see syncStick() for which edge is
       held steady in each direction, and why they differ.

   The demo strip carries a switch that turns sticking off, which is exactly the
   a4 behaviour, so the two can be compared rather than argued about. */
.ba{position:sticky;inset-block-start:var(--site-header);z-index:70;
  margin-inline:-26px;padding:8px 26px 10px;margin-block-end:10px}
/* Expanded ⇒ back in the flow. A 224px band pinned over a 900px viewport is a
   quarter of the screen spent on filters the reader is already looking at, and
   the alternative — collapsing it out from under them as they scroll — takes
   away a choice they just made. So the tall form simply is not chrome. */
/* relative, NOT static. The pin is absolutely positioned inside the band, so
   the band has to stay its containing block in every state — measured as
   static, the pin escaped 104px past the band's right edge and 354px below it.
   position:relative with no offsets lays out identically to static. */
.ba.is-free{position:relative}
/* the edge only exists while it is detached; in flow the band is just a block
   and a shadow would be a lie about depth */
.ba.is-stuck{box-shadow:0 10px 18px -14px rgba(11,28,47,.34)}
/* zero-height, so it changes no geometry; it is only a thing to observe */
.ba__sent{block-size:0}

/* ── UNPINNED BY THE READER ───────────────────────────────────────────────
   Not a setting and not a hiding place: the band stops following, and stays
   exactly where it always was, at the top of the page. It is the same rule
   .is-free uses for an expanded band, reached from a different decision, which
   is why they are separate classes — one is the reader's, one is the band's. */
.ba.is-unpinned{position:relative;box-shadow:none}

/* ── THE CONTROL ──────────────────────────────────────────────────────────
   Lower right, as asked. It is absolutely positioned inside the band's own
   padding box, so appearing and disappearing changes NO geometry — and it is
   only ever hidden with visibility, never with display, so even the box stays.

   Overlap is prevented at the other end: the last row reserves --pin-w of
   trailing space unconditionally, whether the control is showing or not, so
   the measuring pass has already kept the chips clear of it. */
.ba__pin{position:absolute;inset-block-end:9px;inset-inline-end:26px;
  inline-size:26px;block-size:26px;display:grid;place-items:center;
  padding:0;border:1px solid transparent;border-radius:5px;background:transparent;
  color:var(--ink-38);cursor:pointer;visibility:hidden;
  transition:color var(--t-ui) var(--e-out),background var(--t-ui) var(--e-out),
             border-color var(--t-ui) var(--e-out)}
.ba.is-stuck .ba__pin,.ba.is-unpinned .ba__pin{visibility:visible}
.ba__pin:hover{color:var(--navy);background:var(--wash);border-color:var(--rule)}
.ba__pin:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.ba__pin svg{display:block;inline-size:15px;block-size:15px}
/* pinned reads as the solid pin; unpinned as the same pin, struck through */
.ba__pin .pin-off{display:none}
.ba.is-unpinned .ba__pin{color:var(--teal)}
.ba.is-unpinned .ba__pin .pin-on{display:none}
.ba.is-unpinned .ba__pin .pin-off{display:block}
.ba__r:last-child .ba__v{padding-inline-end:var(--pin-w)}
.ba__s{display:flex;align-items:center;gap:10px;min-block-size:34px}
.ba__n{flex:none;font-family:var(--f-head);font-weight:700;font-size:12.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--navy);white-space:nowrap;font-variant-numeric:tabular-nums}
.ba__c{display:flex;flex-wrap:wrap;align-items:center;gap:6px;flex:1 1 auto;min-inline-size:0}
/* CLEAR ALL — an action, not a chip.
   It lives among the active chips because that is what it acts on, but it must
   not read as one more of them: the chips are solid navy objects you can take
   away one at a time, and this takes them all away at once. So it is text, in
   the teal the page uses for anything that does something, with no fill and no
   border to make it look removable. It exists only while there is something to
   clear, and it is rendered by the same function as the chips, so it appears
   and disappears with them and can never be left behind. */
.ba__clr{flex:none;display:inline-flex;align-items:center;min-block-size:26px;
  padding-inline:8px;margin-inline-start:2px;
  font-family:var(--f-head);font-weight:700;font-size:11.5px;letter-spacing:.05em;
  color:var(--teal);background:transparent;border:0;border-radius:4px;cursor:pointer;
  text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:color-mix(in oklab,var(--teal) 40%,transparent);
  transition:color var(--t-ui) var(--e-out),background var(--t-ui) var(--e-out),
             text-decoration-color var(--t-ui) var(--e-out)}
.ba__clr:hover{background:color-mix(in oklab,var(--teal) 10%,transparent);
  text-decoration-color:var(--teal)}
.ba__clr:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.ba__c .chip{flex:none}
.ba .q{flex:0 0 224px;margin:0}
.ba .q__i{padding-block:6px;font-size:13.5px}
.ba .q__n{display:none}
.ba__r{display:grid;grid-template-columns:minmax(0,74px) minmax(0,1fr);gap:0 14px;
  align-items:center;min-block-size:31px}
.ba__r+.ba__r{border-block-start:1px solid var(--rule-2)}
.ba__l{font-family:var(--f-head);font-weight:700;font-size:10px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--ink-38);display:flex;align-items:baseline;gap:5px}
.ba__k{font-size:10px;color:var(--ink-22);font-variant-numeric:tabular-nums}
/* the values line. It holds what fits and the disclosure control; the surplus
   is MOVED OUT by the measuring pass, so wrap is not the mechanism — it is the
   safety net for the one case that overrides the budget, a row with more
   selected values than fit. Then the row takes two lines, which is correct:
   hiding something the reader chose would be worse. */
.ba__v{display:flex;flex-wrap:wrap;align-items:center;gap:6px;min-inline-size:0;
  padding-block:3px}
.ba__v .v{flex:none}

/* ── on a phone the two diverge, and they should ──────────────────────── */
@media(max-width:640px){
  /* nothing sticks on a phone, so there is nothing to unpin and the control
     would be a button that visibly does nothing. It goes, and the trailing
     space it reserved goes back to the last row. The reader's choice is kept
     — it simply has no effect at this width, and the control returns if the
     window widens. */
  .ba__pin{display:none}
  :root{--pin-w:0px; --ba-sticky:0}
  /* ON A PHONE NOTHING STICKS, AND SO NOTHING IS EVER COVERED.
     Measured at 375×812 the collapsed band is 274px — a third of the screen,
     and pinned it put 24 cards under itself at scrollY 900. A phone cannot
     afford to spend a third of its screen on chrome AND lose what is behind
     it. Un-pinned, the phone column is the only surface here where every row
     of the table reads 0 covered cards at every scroll position. */
  .ba{margin-inline:-16px;padding:6px 16px 8px;position:relative;box-shadow:none;background:none}
  .ba__r{grid-template-columns:minmax(0,1fr);gap:2px 0;min-block-size:0;padding-block:4px}
  .ba .q{flex:0 0 100%;order:9}
  .ba__s{flex-wrap:wrap}
}
@media(prefers-reduced-motion:reduce){
}

/* ══════════════════════════════════════════════════════════════════════════
   A2 — ALWAYS VISIBLE, WITH A DISCLOSURE PER AXIS
   The sideways scroll is gone. It hid fifteen of twenty-one themes behind a
   gesture a wheel mouse does not have, which is not a compromise, it is a
   dead end. Each axis now shows what fits on one line and a control that
   reveals the rest DOWNWARD, in place: the page below moves, nothing overlays.

   HOW MANY FIT IS MEASURED, NOT GUESSED. After each render one pass reads the
   line's width and every chip's width and decides the split — no per-chip
   layout thrash, and it re-runs on resize because the answer depends on how
   wide the window is. A SELECTED value is always kept, whatever the budget
   says; if enough are selected to need two lines, the row takes two lines,
   because hiding something the reader chose is worse than a row that grew.

   THE BAND NEVER SCROLLS, IN EITHER DIRECTION. The previous version removed
   the sideways scroll and then reintroduced the problem vertically: a
   max-block-size of min(52svh,470px) with overflow-y made the band a scrolling
   box of its own. A filter band exists so that it can be READ. There is no
   cap here, no overflow, no nested scroll container, and no viewport unit
   anywhere in the band's rules. It is as tall as its content, it grows
   downward, it pushes the page, and the reader scrolls the PAGE — which is
   the one scroll that was never the problem.

   NOTHING IS COVERED EITHER. See the .ba rule below: the band is an ordinary
   block, so growing it pushes the page rather than painting over it.
   ══════════════════════════════════════════════════════════════════════════ */
/* THE DISCLOSURE, WITH NO CONTAINER AT ALL.
   The tail chips stay in the values line and are simply not displayed. There is
   no second box, so there is nothing to clip, nothing to scroll, and nothing
   that can report a scrollHeight larger than its clientHeight. display:none
   also takes them out of the tab order outright, which the previous version
   needed a visibility rule to achieve.

   THE PRICE, STATED: display cannot be glided, so the 0fr → 1fr height
   animation is gone with the box that made it possible. It could only have
   come back as a clipping container, which is the thing being removed. What
   replaces it is a one-shot fade on the chips that actually appear — no
   clipping, no height to measure — fired by a class the click handler sets and
   drops, so a repaint from an ordinary selection does not re-run it. 180ms on
   --e-collapse, the same curve the licence overflow uses. */
.v.is-tail{display:none}
.ba__r.is-open .v.is-tail{display:inline-flex}
.ba__r.is-revealing .v.is-tail{animation:vin 180ms var(--e-collapse) both}
@keyframes vin{from{opacity:0;translate:0 -3px}}
.ba.no-anim .v.is-tail{animation:none}
/* the control says HOW MANY, because "+13" tells a coach there is a catalogue
   behind it and "more" tells him nothing */
/* a FIXED width, so the space the measuring pass reserves for it is the space
   it takes — "+9" and "+13" and an empty freshly-rendered button are all the
   same width, and the split cannot drift with the digit count */
.ba__more{flex:none;display:inline-flex;align-items:center;justify-content:center;
  min-inline-size:42px;min-block-size:26px;padding-inline:9px;
  font-family:var(--f-head);font-weight:700;font-size:11.5px;letter-spacing:.03em;
  color:var(--teal);background:transparent;border:1px dashed color-mix(in oklab,var(--teal) 46%,var(--white));
  border-radius:4px;cursor:pointer;font-variant-numeric:tabular-nums;
  transition:background var(--t-ui) var(--e-out),color var(--t-ui) var(--e-out),
             border-color var(--t-ui) var(--e-out)}
.ba__more:hover{background:var(--teal);color:var(--white);border-color:var(--teal)}
.ba__more:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.ba__more[hidden]{display:none}

/* ── the section line ─────────────────────────────────────────────────────
   A programme with children gets its own line beneath the programme row,
   labelled for what it is rather than pretending to be a fourth axis. It is
   indented and marked with the teal edge the sub-row already used, so it reads
   as belonging to the row above it. Nothing about it is specific to
   Progressive Coach: any programme with children behaves this way. */
.ba__r--sec{padding-inline-start:14px;margin-inline-start:60px;
  border-inline-start:2px solid var(--teal-lt);grid-template-columns:minmax(0,1fr)}
.ba__r--sec .ba__l{padding-block-end:2px}
.ba__r--sec .ba__l em{font-style:normal;color:var(--teal);text-transform:none;
  letter-spacing:.01em;font-size:11px;margin-inline-start:5px}

/* ── on a phone: the same structure, and it stops sticking ────────────────
   Measured at 375×812 the collapsed band is 274px. Sticky, that parks a third
   of the phone permanently and leaves room for barely one card — sticky is a
   desktop luxury paid for in a currency a phone does not have. So below 640px
   the band scrolls away with the page.

   That costs nothing the reader was promised. The reason this design exists is
   that a change of mind must never cost an open, and it still does not: the
   filters are always there, in the document, one flick from the top, never
   behind a control. Nothing was hidden — it was un-pinned.

   It also settles the disclosure question on the phone by removing it. Once the
   band is ordinary page content, an axis may grow to whatever height it needs;
   it is not competing for permanent screen, so it needs no cap of its own and
   no nested scroll — a scroll inside a scroll on a touch screen is a trap. The
   label sits above its values rather than in a 74px gutter, which is 20% of a
   375px screen, and every chip gets a 34px target. */
@media(max-width:640px){
  .ba__r--sec{margin-inline-start:0;padding-inline-start:11px}
  .ba__v .v{min-block-size:34px}
  .ba__more{min-block-size:34px}
  .ba__clr{min-block-size:34px}
}
@media(prefers-reduced-motion:reduce){
  .v.is-tail{animation:none}
}

body:has(.schcat-page) .entry-header{display:none !important}

/* every state of a filter value declares its own ground, so no theme can fill it */
.schcat-page .v{background:var(--white);color:var(--ink-72)}
.schcat-page .v:hover{background:var(--wash);color:var(--navy);border-color:var(--navy)}
.schcat-page .v:focus-visible{background:var(--white);color:var(--navy)}
.schcat-page .v[aria-pressed=true],
.schcat-page .v[aria-pressed=true]:hover,
.schcat-page .v[aria-pressed=true]:focus-visible{background:var(--teal);color:var(--white);border-color:var(--teal)}
.schcat-page .v[aria-pressed=true] .v__n{color:color-mix(in oklab,var(--white) 70%,var(--teal))}
.schcat-page .v[aria-disabled=true],
.schcat-page .v[aria-disabled=true]:hover{background:var(--white);color:var(--ink-54);border-color:var(--rule)}
.schcat-page .ba__r--sec .v:hover{background:var(--wash);color:var(--navy)}
.schcat-page .ba__more:hover,.schcat-page .ba__more:focus-visible{background:var(--teal);color:var(--white)}
.schcat-page .chip:hover{background:color-mix(in oklab,var(--navy) 86%,var(--white));color:var(--white)}

.schcb-catalogue__seed{display:none}
.schcb-catalogue__seed--noscript{display:block}

.pv__ava{background-size:cover;background-position:50% 50%}

/* the guest sits under the instructor, quieter — an addition, not a second headline */
.schcat-page .pv__who--guest{margin-block-start:8px}
.schcat-page .pv__ava--guest{background:color-mix(in oklab,var(--teal) 16%,var(--white));color:var(--teal)}
