/**
 * Works Facet Filter — ArtistFolio
 * ================================
 * Visitor-facing facet filters (medium / series / collections) that sit ABOVE
 * the works gallery and combine with the year sub-navigation.
 *
 * Rendered by jp-works-filter.js; only present when works_nav produced ≥1 facet.
 * Reuses the same CSS custom-props as 15-year-nav.css (--bg / --fg / --muted /
 * --border / --accent / --max-width / --t-fast) so it inherits the active theme.
 *
 * DESIGN LANGUAGE — text-links, not pills.
 *   The chips deliberately mirror the year sub-nav (.year-nav__item): plain
 *   editorial TEXT items, no resting border or fill, hover → --fg, and an ACTIVE
 *   state that is --fg + weight 600 + a thin underline indicator (the same
 *   treatment .year-nav__item.is-active uses). This keeps the two navigation
 *   rows in one quiet, museum-style visual system instead of stacking a heavy
 *   "control panel" of filled/bordered capsules under the airy year links.
 *   Type matches the year-nav exactly: 10px · uppercase · letter-spacing .1em.
 *
 * Markup:
 *   .works-filter                       (BAR wrapper; --below-year-bar when the
 *                                        year nav runs as a fixed top bar)
 *     .works-filter__inner              (max-width track, h-scroll — mirrors
 *                                        .year-nav__inner)
 *       .works-filter__group            (one per facet, inline run)
 *         .works-filter__legend         (facet label)
 *         .works-filter__chips          (chips style → row of <button>)
 *           button.works-filter__chip[aria-pressed]
 *             span.works-filter__chip-label
 *             span.works-filter__count
 *         select.works-filter__select   (dropdown style)
 *       .works-filter__status           (aria-live count, right end of bar)
 */

/* ── Wrapper — the BAR (USER-049) ─────────────────────────────────────────── */
/* Exactly the year-nav top-bar optics: full-bleed hairline bar with the same
   height, background blur and border. Sticky, so it stays available while the
   visitor scrolls the gallery; when the year nav runs as a fixed top bar the
   filter docks directly beneath it (modifier set by jp-works-filter.js). */

.works-filter {
    position: sticky;
    top: var(--header-height, 112px);
    z-index: 80;                          /* beneath the year-nav's 90 */
    /* Full-bleed out of the content column, like the fixed year bar */
    width: 100vw;
    margin: 0 calc(50% - 50vw) 28px;
    padding: 0;
    background: var(--bg);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    border-bottom: var(--hairline-w) solid var(--border);
}

.works-filter--below-year-bar {
    /* -1px: dieselbe Überlappung, die jp-year-nav.js schon gegen den Header fährt
       ("We overlap by 1px to prevent sub-pixel rendering gaps between two fixed
       elements", positionTopBar()). Dort wird die Jahres-Leiste auf h-1 gesetzt,
       --header-height aber auf h — der Trick war also nur nach OBEN angewandt und
       nach unten nicht weitergereicht. Folge, gemessen an der Live-Seite:
       Jahres-Leiste endet bei 141, Filter beginnt bei 142 → 1px Spalt, durch den
       der Seitenhintergrund scheint. Das ist die "Haarlinie zwischen den beiden
       Leisten" (Jan, 2026-08-12) — kein Rahmen: beide Kanten sind nachweislich
       `0px none`, es war die Lücke selbst. */
    top: calc(var(--header-height, 112px) + var(--year-nav-height, 36px) - 1px);
}

/* UI-NAV-1 (Jan): die Medien-Leiste klebt IMMER bündig als zweite Zeile direkt unter
   der Jahres-Nav — null Luft, in JEDEM Scroll-Zustand. (sticky pinnt sonst erst nach
   Scrollen; am Seitenanfang klaffte ~133px Luft, weil die Jahres-Nav fixed oben
   schwebt und der Filter im Fluss unter der „Werke"-Überschrift sitzt.) Greift exakt,
   wenn die Jahres-Nav als Top-Bar sichtbar ist: jp-year-nav.js setzt body.has-year-nav,
   sobald #works im Viewport ist. Aus dem Fluss genommen schwebt der Filter im leeren
   Padding ÜBER der „Werke"-Überschrift → kein Überlapp (Header bleibt sticky/z1000
   oben, Überschrift liegt darunter). --header-height/--year-nav-height setzt
   jp-year-nav.js je Viewport → Offset stimmt auch mobil. */
body.has-year-nav .works-filter {
    position: fixed;
    /* -1px wie oben — dies ist der Zwilling derselben Regel (greift im
       gescrollten Zustand). Beide Stellen müssen denselben Versatz tragen,
       sonst öffnet sich der Spalt genau in einem der beiden Zustände. */
    top: calc(var(--header-height, 112px) + var(--year-nav-height, 36px) - 1px);
    left: 0;
    margin: 0;
}
/* Klick-auf-Jahr: Scrollziel zusätzlich um die fixe Filterzeile (≈ year-nav-height)
   versetzen, damit der Jahresanfang nicht unter dem Filter verschwindet.
   SWEEP-css-scrollmargin-rule-collision: this is now the SOLE declaration for
   this selector (15-year-nav.css used to carry a second, identically-specific
   rule — same 2 classes + 1 element, so the cascade silently picked whichever
   file loaded last instead of merging the intent of either). Formula: header +
   year-nav-height (year bar) + year-nav-height (this filter bar, ≈ same height)
   + the 32px comfort padding the year-nav-only rule used to add. */
body.has-year-nav .works-year {
    scroll-margin-top: calc(var(--header-height, 112px) + (2 * var(--year-nav-height, 36px)) + 32px);
}

/* USER-FB 2026-07-25: Sticky-Pin der „Werke"-Überschrift ist zurückgenommen
   (15-year-nav.css). Damit die Überschrift im RUHEZUSTAND trotzdem unter dem
   Doppel-Turm (Jahres-Nav + gedockte Filterzeile) hervorkommt, braucht die
   Filter-Variante mehr Kopf-Luft als die Basis-Regel (die nur EINE Bar
   einrechnet). :has = gleiche Support-Annahme wie die übrigen Regeln hier. */
body.has-year-nav:has(.works-filter) #works {
    padding-top: calc((2 * var(--year-nav-height, 36px)) + 24px);
}

/* Inner track mirrors .year-nav__inner: centered max-width column, one
   horizontally scrollable row, hidden scrollbars. */
.works-filter__inner {
    display: flex;
    align-items: center;
    width: 100%;
    max-width: var(--max-width, 1100px);
    margin: 0 auto;
    min-height: var(--year-nav-height, 36px);
    gap: 0 28px;
    /* Leading/trailing inset of 14px so the first chip/legend left-aligns with the
       year-nav labels directly above (.year-nav__item padding: 0 14px) AND the
       works content column gutter (main padding-inline ~--space-md). Without it the
       filter row hung ~14px further left than the year bar it docks beneath
       (USER-FB-111). max() merges it with the iOS notch safe-area so neither is
       lost on a landscape notched device — the same shape the ≤900px rule already
       used. 0px off-notch stays byte-identical for the safe-area part.
       DES-P13B-SAFEBAR. */
    padding-inline: max(14px, var(--safe-left, 0px)) max(14px, var(--safe-right, 0px));
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;

    /* FILTER-DISCOVER-1 — Scroll-Affordanz.
       Der Streifen scrollt waagerecht UND hat die Scrollleiste versteckt: auf
       411px lagen von 9 Chips 500px ausserhalb (gemessen 2026-08-14, Greenfield
       :8602: scrollWidth 911 / clientWidth 411; auf 768px noch 143px). Nichts
       zeigte an, dass rechts weitergeht — der Streifen las sich als lose Wortreihe.
       Genau daher kam Jans Frage „wo ist die Medien-Navigation?".
       Mechanik ohne JS, ohne Layout- und ohne z-index-Wechsel (Komarov-Prinzip):
         · zwei Schatten kleben am Rand des Tracks   (background-attachment: scroll)
         · zwei Deckel in Bar-Hintergrundfarbe fahren MIT dem Inhalt (… : local)
       Am Anfang liegt der linke Deckel exakt auf dem linken Schatten → nichts zu
       sehen; sobald gescrollt wird, wandert er weg und der Schatten erscheint. Passt
       alles hinein, decken beide Deckel dauerhaft ab: kein Zeichen, wo es nichts zu
       holen gibt. Deckel 14px = exakt der Innenabstand, damit er nie auf Chip-TEXT
       faellt, Schatten 12px darunter. Erste Ebene (.works-primary-nav) traegt
       denselben Track und bekommt es damit automatisch.
       Warum nicht stattdessen umbrechen: gemessen — mit umbrechenden Chips waechst
       die GEDOCKTE Leiste auf 83px (411px) bzw. 111px (375px), waehrend die Offsets
       darunter (#works padding-top, scroll-margin) mit EINER Zeile ≈
       --year-nav-height rechnen. Der Umbruch waere ein Verdeck-Fehler an anderer
       Stelle statt einer Loesung. */
    background:
        linear-gradient(var(--bg), var(--bg)) left center / 14px 100% no-repeat local,
        linear-gradient(var(--bg), var(--bg)) right center / 14px 100% no-repeat local,
        linear-gradient(to right, color-mix(in srgb, var(--fg) 16%, transparent), transparent)
            left center / 12px 100% no-repeat scroll,
        linear-gradient(to left, color-mix(in srgb, var(--fg) 16%, transparent), transparent)
            right center / 12px 100% no-repeat scroll;
}

.works-filter__inner::-webkit-scrollbar {
    display: none;
}

/* ── One facet group — an inline run inside the bar ───────────────────────── */

.works-filter__group {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 14px;
}

/* Hairline divider between facet groups (quiet, year-nav rhythm) */
.works-filter__group + .works-filter__group {
    border-left: var(--hairline-w) solid var(--border);
    padding-left: 28px;
}

/* Jan-Design: das sichtbare "Technik/Medium"-Label ist redundant — die Chips
   erklären sich selbst. Visually-hidden statt entfernt: die Chip-Gruppe behält
   ihren accessible name (aria-labelledby zeigt hierher, WCAG 1.3.1). */
.works-filter__legend {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.works-filter__chips {
    display: flex;
    flex-wrap: nowrap;                    /* one row — the bar scrolls instead */
    align-items: center;
    gap: 0 14px;
    border: 0;
    margin: 0;
    padding: 0;
    min-width: 0;
}

/* ── Chip (toggle) — text-link language mirroring .year-nav__item ──────────── */

.works-filter__chip {
    /* Inline text item with NO visible box. A min-height floor keeps the tap
       target ≥24px (the contract the e2e a11y check asserts) even though the
       chip reads as plain text; the label centers within that height. */
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-height: 26px;
    padding: 4px 1px;
    font: inherit;
    font-size: 10px;
    letter-spacing: var(--af-track-caps-lg);
    text-transform: uppercase;
    line-height: 1;
    color: var(--muted);
    background: transparent;
    border: 0;
    border-radius: 0;
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--t-fast, 0.15s ease);
    -webkit-appearance: none;
    appearance: none;
}

.works-filter__chip:hover {
    color: var(--fg);
}

.works-filter__chip:focus-visible {
    /* --accent matches the site-wide focus ring (00-base.css). index.php now emits
       --accent with a 3:1 contrast guard, so it stays visible in both themes. */
    outline: 2px solid var(--accent, var(--fg));
    outline-offset: 3px;
    border-radius: 2px;
}

/* The label carries the underline indicator so it hugs the TEXT (not the
   centered tap-target box) — the same way .year-nav__item's underline tracks
   its label width. */
.works-filter__chip-label {
    position: relative;
}

/* Pressed / selected — --fg text + weight 600 + thin underline indicator,
   exactly like .year-nav__item.is-active (no fill, no pill). */
.works-filter__chip[aria-pressed="true"] {
    color: var(--fg);
    font-weight: 600;
}

.works-filter__chip[aria-pressed="true"] .works-filter__chip-label::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -4px;              /* a hair below the text, like the year-nav line */
    height: 1.5px;
    /* --accent is the SAME token the year-nav active underline uses, so both nav
       rows share one indicator colour (the artist's brand accent). index.php emits
       it with a 3:1 contrast guard, so it stays visible on every preset incl. dark
       — where the old static #111 accent used to vanish. */
    background: var(--accent, var(--fg));
    border-radius: 1px;
}

/* Wert, den es im gerade führenden Jahr (bzw. im gewählten Wert der ersten
   Ebene) nicht gibt. Bewusst dieselbe Sprache wie die zurückgenommenen Jahre
   in 15-year-nav.css: gleiche Bedeutung, gleiche Optik.

   Zurückgenommen, aber BEDIENBAR — kein pointer-events:none. Ein Klick filtert
   ganz normal über den vollen Bestand; das ist derselbe Ausweg, den die
   hellgrauen Jahreszahlen oben bieten (FE-YEARNAV-DEADEND-1). Wer die Auswahl
   verkleinert, darf nicht in einer Ecke landen, aus der kein Klick mehr
   herausführt. */
.works-filter__chip--out-of-context {
    opacity: .38;
}

.works-filter__chip--out-of-context:hover,
.works-filter__chip--out-of-context:focus-visible {
    opacity: 1;
}

.works-filter__count {
    opacity: 0.5;
    font-weight: 400;
    letter-spacing: var(--af-track-snug);    /* numerals don't need the wide tracking */
    font-variant-numeric: tabular-nums;
}

.works-filter__chip[aria-pressed="true"] .works-filter__count {
    opacity: 0.6;
}

/* ── Dropdown (select style) ──────────────────────────────────────────────── */
/* The <select> keeps a light affordance (it genuinely needs to read as a
   control), but trimmed to the minimal palette: hairline border, no fill. */

.works-filter__select {
    font: inherit;
    font-size: 11px;
    letter-spacing: var(--af-track-snug);
    color: var(--fg);
    background: transparent;
    border: 0;
    border-bottom: var(--hairline-w) solid var(--border);
    border-radius: 0;
    padding: 5px 22px 5px 2px;
    min-height: 28px;          /* comfortable tap target */
    cursor: pointer;
    max-width: 100%;
    -webkit-appearance: none;
    appearance: none;
    /* Caret */
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: calc(100% - 8px) center, calc(100% - 4px) center;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
    transition: border-color var(--t-fast, 0.15s ease);
}

.works-filter__select:hover {
    border-bottom-color: var(--fg);
}

.works-filter__select:focus-visible {
    outline: 2px solid var(--accent, var(--fg));
    outline-offset: 3px;
    border-radius: 2px;
}

/* ── Live status / count — rides at the end of the bar ────────────────────── */

.works-filter__status {
    font-size: 10px;
    letter-spacing: var(--af-track-wide);
    color: var(--muted);
    margin: 0 0 0 auto;       /* push to the right end of the bar */
    padding-left: 16px;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Hide the status when it carries no message (avoids an empty gap) */
.works-filter__status:empty {
    display: none;
}

/* Year groups the filter emptied out are hidden via [hidden]; nothing to style,
   but make sure forced display rules elsewhere don't override the attribute. */
.works-year[hidden] {
    display: none !important;
}

/* ── Responsive ───────────────────────────────────────────────────────────── */

@media (max-width: 900px) {
    .works-filter {
        margin-bottom: 22px;
    }

    .works-filter__inner {
        gap: 0 18px;
        /* breathing room at the bleed edges, merged with the notch safe-area so
           neither is lost (logical padding — keeps the base rule's padding-inline
           semantics instead of the physical shorthand). DES-P13B-SAFEBAR. */
        padding-block: 0;
        padding-inline: max(14px, var(--safe-left, 0px)) max(14px, var(--safe-right, 0px));
    }

    .works-filter__group {
        gap: 10px;
    }

    .works-filter__group + .works-filter__group {
        padding-left: 18px;
    }

    .works-filter__chips {
        gap: 0 12px;
    }

    .works-filter__chip {
        font-size: 9px;
    }
}

@media (max-width: 600px) {
    /* Chips WRAP onto multiple lines on narrow screens so every chip stays inside
       the viewport. A previous horizontal-scroll strip (flex-wrap:nowrap;
       overflow-x:auto) pushed later chips off-screen — unreachable without
       sideways scrolling and flagged as a critical offscreen defect. Wrapping
       keeps the whole facet visible and tappable.
       ACHTUNG, gemessen 2026-08-14 (FILTER-DISCOVER-1): dieses flex-wrap greift
       derzeit NICHT. .works-filter__group steht auf `flex: 0 0 auto` in einem
       nicht umbrechenden Track, behaelt also seine max-content-Breite — die
       Chip-Zeile bekommt nie zu wenig Platz und bricht folglich nie um (411px:
       eine einzige Chip-Zeile, 500px ausserhalb der Sicht). Erzwingt man den
       Umbruch (`flex: 0 1 auto; min-width: 0`), waechst die GEDOCKTE Leiste auf
       83px/411px bzw. 111px/375px, waehrend #works padding-top und scroll-margin
       mit EINER Zeile ≈ --year-nav-height rechnen — der Umbruch verschiebt den
       Fehler also nur. Bis die Leistenhoehe gemessen und veroeffentlicht wird
       (wie --af-cookie-h), traegt die Scroll-Affordanz oben die Entdeckbarkeit. */
    .works-filter__chips {
        flex-wrap: wrap;
        gap: 2px 12px;
    }
    .works-filter__chip { flex-shrink: 0; }

    /* Stack the legend above its chips so the row reads like a labelled list. */
    .works-filter__group {
        flex-direction: column;
        align-items: stretch;
        gap: 3px;
    }
    .works-filter__select { width: 100%; }
}

/* ── Dark mode ────────────────────────────────────────────────────────────── */
/* All chip styling is token-driven (--muted / --fg / --accent), so it flips with
   the theme automatically — no per-mode overrides needed for the text links.
   These only firm up the <select> option list, which some browsers render with
   the UA palette regardless of the control colors. */

[data-theme="dark"] .works-filter__select option,
.dark-mode .works-filter__select option {
    color: var(--fg);
    background: var(--bg);
}

/* ── Reduced motion ───────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .works-filter__chip,
    .works-filter__select {
        transition: none !important;
    }
}

/* ── Erste Ebene: frei wählbare primäre Navigations-Achse ──────────────────
   Jan 2026-08-12: „der User soll auswählen können, ob er z. B. Jahre oder
   Medium als erste Instanz hat — diese bleiben dann immer vollständig und es
   passt sich nur die untere an."

   .works-primary-nav trägt zusätzlich .year-nav--top-bar und erbt damit die
   komplette Optik und Position der Jahres-Leiste: für den Besucher ist es
   DIESELBE erste Ebene, nur mit anderem Inhalt. Deshalb steht hier auch nur
   das, was sich wirklich unterscheidet — jede Regel, die man hier
   wiederholte, wäre eine zweite Wahrheit, die auseinanderlaufen kann.

   Ohne Filterzeile darunter behält die Leiste ihre Unterkante zum Inhalt
   (dieselbe Regel, die die Jahres-Leiste in 15-year-nav.css trägt). */
body:has(.works-filter) .works-primary-nav {
    border-bottom: none;
}

/* Die Filterzeile dockt an die erste Ebene an — mit demselben 1px-Überlapp,
   der zwischen Header und Jahres-Leiste schon gefahren wird. Ohne ihn klafft
   genau die Haarlinie, die als FE-NAV-HAIRLINE-GAP-1 gemessen wurde: kein
   Rahmen, sondern ein Pixel Seitenhintergrund zwischen zwei fixierten
   Elementen. */
body.has-primary-nav .works-filter--below-year-bar,
body.has-primary-nav.has-year-nav .works-filter {
    /* --primary-nav-height misst jp-works-filter.js an der Leiste selbst; der
       Rückfallwert ist die Höhe der Jahres-Leiste, weil beide dieselbe Optik
       tragen. Vorher stand hier --year-nav-height: die setzt aber jp-year-nav.js,
       und genau das läuft in diesem Fall NICHT als Top-Bar — der Wert war
       geraten statt gemessen. */
    top: calc(var(--header-height, 112px) + var(--primary-nav-height, var(--year-nav-height, 36px)) - 1px);

    /* Ohne position:fixed klebt die Zeile nicht an der Leiste darüber, sondern
       bleibt im Fluss stehen: gemessen 67,8 px Spalt bei getauschter Priorität
       (FE-NAV-PRIMARY-GAP-1). Die Zwillingsregel für die Jahres-Führung
       (body.has-year-nav .works-filter) trägt dasselbe seit jeher — hier fehlte
       es, weil dieser Fall bis 2026-08-12 nicht existierte. */
    position: fixed;
    left: 0;
    margin: 0;
}

/* Die Chips der ersten Ebene tragen etwas mehr Gewicht als die der Filterzeile
   — sie sind Navigation, nicht Verfeinerung. Bewusst über die Schriftstärke
   und nicht über Farbe: die Farbe gehört dem aktiven Zustand. */
.works-primary-nav .works-filter__chip {
    font-weight: 500;
}

.works-primary-nav .works-filter__legend {
    /* Die Achsen-Bezeichnung ist in der ersten Ebene redundant — der Inhalt
       sagt bereits, worum es geht (Jahreszahlen, Techniken). Für Screenreader
       bleibt sie über aria-label am Container erhalten. */
    display: none;
}
