/* ==========================================================================
   Design tokens — Frisson palette
   First applied: M10A side-experiment 2026-05-12. Beta pass — M12 will polish.
   ========================================================================== */

:root {
  /* ==========================================================================
     SURFACES — Frisson Noir (deep night sky) — M12 ADR-169 palette pivot.
     Replaces the cream-* lineage. Token NAMES kept identical so every var()
     reference across the codebase keeps resolving — only VALUES change.
     cream-base → ink (deepest), cream-surface → midnight (cards),
     cream-mist/sunken → navy (section headers / wells).
     Navy stays for emphasis; visual drama now comes from PLUM accents. */
  --cream-base: #0a0d1e;       /* App bg — deepest ink (was #f4f1ea) */
  --cream-base-rgb: 10, 13, 30;      /* RGB triplet for translucent dark surfaces (was Frost cream) */
  --cream-surface: #13182f;    /* Cards / modals / popovers (was #fbfaf6) */
  --cream-surface-rgb: 19, 24, 47;   /* RGB triplet — panel/modal/popup fills w/ opacity (was Frost #fbfaf6) */
  --cream-mist: #1c2547;       /* Section header bg — admin table headers (was #eef0ec) */
  --cream-sunken: #1c2547;     /* Inputs / wells (was #ecebe2) */
  --cream-lifted: #191f3c;     /* Lifted card on a modal surface — between surface and sunken (designer echo-modal handoff, ADR-236) */
  --navy-deep: #0a0d1e;        /* Was #0d2238 — emphasis now via plum accents */
  --navy-soft: #13182f;        /* Was #1f2e44 — subtle elevated dark surface */

  /* NEW — plum surface family. BG ONLY, never use as text/icon color. */
  --surface-plum: #2a1f4d;
  --surface-plum-lift: #3d2b5e;

  /* Text — same role names; values flip for dark surface */
  --text-dark: #eef0fa;        /* Primary text (~17:1 on midnight) (was #1c2532) */
  --text-muted: #bcc4dc;       /* Secondary text (~9:1) (was #5a6478) */
  /* Field labels — soft teal (muted+teal mix) so caps labels get a quiet
     identity instead of blending into grey (Petřin pokyn 2026-05-27; this
     REVERSES the old HARD RULE 9 "labels never teal"). One token → every
     .field__label across forms/dialogs/auth changes from one place.
     2026-06-03: a brief app-wide gold retone was rolled back — gold belongs to
     LOCATIONS only, via the dedicated --location-color token below. */
  --label-color: color-mix(in srgb, var(--accent-turquoise) 50%, var(--text-muted));
  /* Locations — SOFT GOLD (muted+copper mix, 50 %, Petřin výběr 2026-06-03):
     the location caps (feed where / popup place + meta-place / concert
     location / mobile concert subtitle + gathering header location) get their
     OWN token, split from --label-color (Petřin pokyn — gold on form field
     labels was an accident). Gold ties places to the copper landmark family
     and stops fighting the teal song tag (--song-name). 2026-06-03 live tune:
     copper-bright, postupně 45 % → 35 % — lighter + progressively less
     saturated per Petřin pokyn („odbarvit a zesvětlit", pak „ještě odbarvit"). */
  --location-color: color-mix(in srgb, var(--accent-copper-bright) 35%, var(--text-muted));
  --continent-label: #7b7f86;  /* Muted warm grey for world-view continent
                                  labels on the LIGHT Voyager map (continentLabels.js).
                                  Own token because UI text tokens are tuned for the
                                  dark chrome; this echoes Voyager's native label grey. */
  --continent-label-halo: #ffffff; /* White lift behind continent labels so they
                                      stay legible over land or sea. */
  --map-ocean-tint: #cdeaf2;   /* Voyager ocean tint — fills any 1px gap the tile
                                  layer leaves so it blends with the sea (see
                                  .leaflet-container). */
  --text-light: #eef0fa;       /* Text on dark / on copper button (was #f4f1ea) */
  --text-light-rgb: 238, 240, 250; /* RGB triplet for translucent light rims/rings */
  --text-light-muted: #aab3d2; /* Tertiary text (~6.5:1) — lightened from #8c95b8 (Petra 2026-06-04:
                                  the token kept reading "on the edge"; tertiary may step back but
                                  must never be borderline-legible). NOT for placeholders. */
  --text-ai-note: #8c95b8;     /* M14 — deliberate low-contrast "whisper" EXCLUSIVELY for the
                                  always-visible AI provenance line (Petřin pokyn 2026-06-05).
                                  NEVER for content/labels/meta — ui-standards HARD RULE 14. */
  --text-tombstone: #ccd3e6;   /* M18.X-ECHO-DELETE-RULES — the founding-echo tombstone notice
                                  ("Original echo was deleted"). Muted-but-READABLE by design
                                  (scope: vlastní token, ne --text-muted; one step brighter than
                                  secondary so the italic line never reads borderline). */
  /* Single source of truth for ALL input/textarea placeholder text (2026-06-04
     placeholder audit). One token, one global rule — no per-component
     placeholder colors. #b0b8d2 ≈ 7.6:1 on input wells (--cream-sunken) —
     Petra 2026-06-04: stepped down from #bcc4dc (8.6:1), variant B of the
     rendered comparison (_preview/_placeholder_variants.html). */
  --text-placeholder: #b0b8d2;
  /* Text ON full bright-accent buttons (violet/turquoise/copper/mine/songs).
     Dark ink — white (--text-light) disappears on Noir's luminous accents.
     Use ONLY on solid bright fills; rgba washes + dark/deep accents keep white. */
  --text-on-accent: var(--surface-ink);
  /* Read-only / non-editable VALUE text (e.g. fixed location in Add a Moment).
     Clearly greyer than --text-dark so it reads as "display, not a field you
     type into" — --text-muted was too close to white (Petřin pokyn 2026-05-25,
     reads white next to the white title). Semantic alias — retune here, never
     hardcode the grey at call sites. */
  --text-readonly: var(--text-light-muted);

  /* Brand accents — Frisson Noir DNA */
  /* Teal (was turquoise #0d7575) — Save CTA, concerts, links, focus, primary brand */
  --accent-turquoise: #4dd5cf;
  --accent-turquoise-bright: #7af0e8;                  /* Vivid splash / hover (was #3dd6c8) */
  --accent-turquoise-deep: #1f8b83;                    /* Gradient bottom stop / pressed-bg (Noir deep teal, was Frost #095e5e) */
  --accent-turquoise-tint: rgba(77, 213, 207, 0.14);   /* Selected-state bg tint */
  --accent-turquoise-rgb: 77, 213, 207;                /* RGB triplet for rgba() — recomputed for Noir */
  /* Song name — the ONE token for a song TITLE + its tag chrome (left rule,
     note glyph, song filter pill + result line, profile recent-echo eyebrow)
     across the feed / desktop popup / mobile sheet. Petra picked the luminous
     teal-bright (2026-06-03) after the original pale azure read flat („fádní")
     as small text on noir. The soft-gold feed LOCATION label (--location-color)
     stays far quieter, so the two don't collide. Retune song colour HERE only. */
  --song-name: var(--accent-turquoise-bright);
  --song-name-rgb: var(--accent-turquoise-rgb);
  /* Gold (was copper #a06a2a) — landmarks/POI, system actions, "Mark as test" warn */
  --accent-copper: #d4a04a;
  --accent-copper-bright: #efbe6d;     /* Hover / warm highlight (was #c98b4b) */
  --accent-copper-deep: #8a6a2a;       /* M12 Noir PR5 — landmark teardrop body-shade bottom + cluster inner */
  --accent-copper-hover: #8b5a20;      /* Darkened copper for solid .auth-btn / auth submit :hover (was a duplicated hardcoded hex) */
  --accent-copper-rgb: 212, 160, 74;   /* RGB triplet for rgba() — recomputed for Noir */
  /* M11.X-MARKERS (ADR-121): --accent-rust decommissioned — concert markers
     use --accent-turquoise (mapa); concert titles use --accent-violet (text). */
  /* Iris (was violet #5a3a78) — Add/Create CTA, tour chips, gathering glyph */
  --accent-violet: #ad77f2;            /* Saturated iris — S~83% (was dusty #b89bdf → #a87fe8 still washed across chips/glyphs, Petřin pokyn 2026-05-27). Chip fill (ADR-140), marker fill (fanMomentSvg), aurora gradient stops. */
  --accent-violet-soft: #cba5fb;       /* Label / text foreground variant — between base and -bright. Used as `color:` on noir surfaces so labels/icons don't read dim (Petřin pokyn 2026-05-28). */
  --accent-violet-soft-rgb: 203, 165, 251; /* RGB triplet for rgba() */
  --accent-violet-bright: #d8b8ff;     /* Highlight, badge (was #7d5fb8) */
  --accent-violet-deep: #5a3f7a;       /* NEW — gradient bottom stop */
  /* Foreign-fan nick — ONE token for every foreign-nick surface: feed title,
     popup nick / by-nick / preview author, composer gathering author, confirm
     dialog author, moments-nearby eyebrow, mobile gathering header / row /
     detail authors. The viewer's OWN nick stays --accent-mine (magenta) via
     the --mine overrides. THIS token is the one safe experiment knob for the
     nick tone — every consumer below is verified nick TEXT only (event/concert
     feed titles override to --text-dark, so they never inherit this).
     Petřin pick 2026-06-03: light luminous lavender, a step above
     --accent-violet-bright. Earlier same-day tries: grey-violet mix (dusty),
     bright iris (loud), moon-muted + mist neutrals (weird). Retune here only. */
  --nick-color: #e4c9ff;
  --accent-violet-rgb: 173, 119, 242;  /* RGB triplet for rgba() — synced w/ #ad77f2 (Concert chip per ADR-140) */

  /* M12 ADR-169 — NEW magenta family. "Your moment" identity (--accent-mine).
     Frost flipped this from rust → Noir magenta. Distinct from the
     two reds: cihla --danger (delete/ban + cancelled) reads pinkish-red,
     magenta --accent-mine reads pink. */
  --accent-mine: #eb8aae;              /* Magenta base — own-moment SVG/cluster fill flip */
  --accent-mine-bright: #f0a5c3;       /* Brighter — gradient top stop + hover (was #c41a0e) */
  --accent-mine-deep: #7d2050;         /* Deeper — gradient bottom stop (was #6b0700) */
  --accent-mine-wash: rgba(235, 138, 174, 0.14); /* List item bg tint */
  --accent-mine-rgb: 235, 138, 174;    /* RGB triplet for rgba() with variable alpha */
  /* Designer's canonical "clean" magenta (frisson-palette.html `--magenta`).
     Deeper than the light --accent-mine pink. Already used raw by the marker
     sprite dots (assets/markers-sprite.svg) — promoted to a first-class token
     so icons + the favourited heart share it (Petřin pokyn 2026-05-25). */
  --accent-magenta: #c44a78;
  --accent-magenta-rgb: 196, 74, 120;
  /* M13Q — "lifted" magenta for like-count TEXT on the Noir surface. The base
     --accent-magenta reads dim as small text on dark (same problem
     --accent-violet-soft solves for violet text). Used ONLY for the like
     counter digits; the heart glyph keeps --accent-magenta + the #heart-shade
     fill. Shared across the event like (.popup__save / .rsvp__heart /
     .fm-rsvp__like) and the per-Echo like (echoLike.js) so both like counters
     read alike (Petřin pokyn 2026-06-01). Name + value = the designer's canonical
     palette (docs/handoff/frisson-handoff/frisson-palette.html). Mirrors the
     --accent-mine pink value but is a DISTINCT semantic token (like affinity,
     not own-moment identity). */
  --magentaLift: #eb8aae;

  /* Online entity — azure (M12.X-DETAIL-POPUP, Petřin pokyn 2026-05-27).
     Online events shared violet with moments → confusing. Azure is the one
     genuinely free, distinct hue (broadcast / screen semantic), clearly apart
     from concert teal + moment violet + LIVE mint, so users always recognise
     what they're clicking. Sits at ~205° between teal + violet on the wheel =
     harmonises with both cool accents while owning its identity. */
  --accent-online: #5fb0e8;            /* Azure base — chip / RSVP / watch / bubble */
  --accent-online-bright: #82c2f2;     /* Hover / highlight */
  /* (-soft #a3d2f7 text variant removed 2026-06-03 — its only consumers were the
     song-tag surfaces, which moved to --song-name (teal-bright, Petřin výběr);
     no azure-soft call sites remain.) */
  --accent-online-deep: #2f6fa8;       /* Gradient bottom stop / pressed-bg */
  --accent-online-rgb: 95, 176, 232;   /* RGB triplet for rgba() */

  /* Facebook platform tone — Community hub row tint (ADR-252, designer
     handoff value adopted verbatim per Petřino rozhodnutí 2026-06-04).
     Deliberately NOT the azure --accent-online family: azure = online
     events world; this is a platform identity tint only. */
  --platform-facebook: #8fb4f0;

  /* M13.X-PLATFORM-UNIFY (ADR-255) — ONE colour per platform, app-wide.
     Source of truth = the Community hub handoff tones (community-hub.css
     §"platform tones"), promoted from hub-local rules to tokens so every
     surface (hub, popup + sheet discussion rows, online detail/create,
     profile + Settings chips, list CTA) reads the same identity. Default =
     nearest Noir tone, but NOT palette-at-all-costs (Petřin pokyn
     2026-06-04 večer „nehodí se na všechno"): where the brand identity
     needs it, a tuned brand value is fine (facebook sky blue; instagram =
     pink→blue gradient on the glyph itself, see platformIcons.js). Blue
     brands (telegram/zoom) share the facebook sky blue. */
  --platform-reddit: var(--accent-turquoise);
  --platform-discord: var(--accent-violet-soft);
  --platform-forum: var(--accent-copper-bright);
  --platform-youtube: var(--accent-mine-bright);
  --platform-telegram: var(--platform-facebook);
  --platform-zoom: var(--platform-facebook);
  --platform-spotify: var(--success);
  --platform-twitch: var(--accent-violet);
  /* Instagram glyph = brand pink→blue gradient (platformIcons instagramSvg,
     mine→violet→online tones). This token only tints the glyph's
     SURROUNDINGS (tile wash, border, CAPS label) — light pink so the small
     label stays readable on noir (flat --accent-magenta read „moc tmavý"). */
  --platform-instagram: var(--magentaLift);
  --platform-x: var(--text-light);
  --platform-tiktok: var(--accent-turquoise-bright);
  --platform-web: var(--text-muted);

  /* Status — outside brand palette, softened for dark-bg readability */
  --success: #4dd5a8;          /* Mint, teal-family green (was #2d8a5c) */
  --success-rgb: 77, 213, 168; /* RGB triplet — status-dot glow */
  --danger: #d96b6b;           /* Softened brick — delete / ban FILL / border / button bg (was #b83838) */
  --danger-rgb: 217, 107, 107; /* RGB triplet for rgba() with variable alpha */
  /* Danger TEXT/icon foreground on dark (HARD RULE 13, Petřin pokyn 2026-05-31).
     Base --danger is a button-FILL hue -> as TEXT on noir it reads dim/disabled
     (~4.4:1 on plum). Lighter tint of the SAME brick hue, AA 5.1-7.3:1 on every
     noir surface. Mirrors --accent-violet-soft. Use as `color:` only — NEVER as
     background (buttons/fills/borders keep --danger). */
  --danger-text: #f08a8a;
  --danger-deep: #a73030;      /* LEGACY Frost text-on-light — do NOT use as text on noir (~2.2:1, vanishes). Kept for any light-surface remnant. */
  --danger-bright: #e88888;    /* Gradient end stop for LIVE banner (was #a73030) */
  --danger-soft: rgba(217, 107, 107, 0.10); /* On-brand danger wash — was an undefined token silently falling back to off-brand rgba(180,35,35,…) at every callsite (M12D.X-REPORT-INDICATORS) */
  --warning: #d4a04a;          /* Gold — same family as copper (was #b8862d) */
  --warning-rgb: 212, 160, 74; /* RGB triplet — status-dot glow */

  /* Event status palette (M12 Noir PR4) — extracted from hardcoded hex in
     the feed-list status dots so the orange/green/grey reads as tokens, not
     magic numbers. Orange = scheduled, green = next/live, grey = past. */
  --status-upcoming: #e8821f;        --status-upcoming-rgb: 232, 130, 31;
  --status-next: #2e8a4f;            --status-next-rgb: 46, 138, 79;
  --status-past: #9aa3b5;            --status-past-rgb: 154, 163, 181;
  /* Test-data flag — standard full gold (#d4a04a = --accent-copper) when ON,
     grey (--text-muted) when OFF (Petřin pokyn 2026-05-24, flask in kebab). */
  --test-data: var(--accent-copper);

  /* M11L (ADR-151) cancellation red — M12 ADR-169: UNIFIED with --danger
     (Petřin pokyn 2026-05-24). Zrušený koncert = stejná cihlová červená jako
     smazat/ban; rozliší se TREATMENTEM ne odstínem: výraznější strikethrough
     + danger halo = "NEKONALO se / pozor", musí zůstat dobře vidět. Token name
     ponechán jako sémantický alias (~50 odkazů + čitelnost záměru, lze později
     znovu rozdělit). Used pro CANCELLED badge / banner / row tint / chip. */
  --cancelled-red: var(--danger);
  --cancelled-red-rgb: var(--danger-rgb);

  /* Borders — inverted alpha to read on dark */
  --border-on-light: rgba(238, 240, 250, 0.16);
  --border-strong-light: rgba(238, 240, 250, 0.32);
  --border-on-light-strong-brand: rgba(77, 213, 207, 0.42); /* Teal-tinted divider for writing-mode kontexty (vertical rail labels) */
  --border-on-dark: rgba(238, 240, 250, 0.16);

  /* Shape scale — unchanged */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 18px;
  --radius-pill: 999px;

  /* Elevation — deeper shadows for dark surface */
  --shadow-modal: 0 24px 60px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(238, 240, 250, 0.06);
  --shadow-card: 0 12px 30px rgba(0, 0, 0, 0.40), inset 0 1px 0 rgba(238, 240, 250, 0.04);
  --shadow-dropdown: 0 8px 24px rgba(0, 0, 0, 0.50);     /* Photon results, suggestion popups */
  --shadow-overlay-bar: 0 4px 14px rgba(0, 0, 0, 0.40);  /* Fixed overlay action bars */

  /* M10B Stage 2 review (STYLE-010) — vermilion used by the GPS pin preview
     rectangle on the map. Semantic name so map.js can read it via
     getComputedStyle. Bumped lighter (vermilion) for dark-bg readability. */
  --preview-boundary: #f08570;

  /* ====== Frisson Noir atmospheric tokens (M12 ADR-169) ======
     Replaces Frost mesh. Same token NAMES so radial-gradient rules that
     reference these continue to work — only values change to Noir. */

  /* Atmospheric mesh washes — radial-gradient stops, ~7-10% opacity */
  --frost-wash-teal: rgba(77, 213, 207, 0.08);
  --frost-wash-plum: rgba(var(--accent-violet-rgb), 0.10);
  --frost-wash-copper: rgba(212, 160, 74, 0.07);

  /* Halo glows — single layer perf-friendly (žádný 4-stack) */
  --halo-teal:  0 0 24px rgba(77, 213, 207, 0.30);
  --halo-plum:  0 0 24px rgba(var(--accent-violet-rgb), 0.32);
  --halo-multi: 0 0 32px rgba(var(--accent-violet-rgb), 0.20), 0 0 16px rgba(77, 213, 207, 0.18);
  /* NEW — danger halo for cancelled / warning surfaces (Petřin pokyn 2026-05-24:
     zrušený koncert = strike + danger halo, "NEKONALO se", musí být dobře vidět). */
  --halo-danger: 0 0 24px rgba(217, 107, 107, 0.42);

  /* NEW — live indicator (mint, distinct from --success where both appear).
     Used for LIVE banner pill on online streams. */
  --live: #4dd5a8;
  --live-rgb: 77, 213, 168;
  --live-bright: #6deec0;
  --live-wash: rgba(77, 213, 168, 0.16);

  /* NEW — glass tokens for floating panels on Noir surfaces.
     Used by hero strip, app footer pill, side panel chrome. */
  --glass-strong: rgba(19, 24, 47, 0.85);
  --glass-soft:   rgba(19, 24, 47, 0.66);

  /* `--opacity-self-declared` token removed (Petřin pokyn 2026-05-25):
     the 60 % dim on anonymous self-declared flags was never wanted — on the
     dark Noir surface it read as a "grayed / dead" flag rather than a soft
     signal. Anon flags now render at full opacity like registered ones; the
     "self-declared, anonymous" tooltip stays as the only disclosure cue.
     Reverses ADR-070 Rozhodnutí 2 (visual distinction). */

  /* Typography — unified system (Cormorant Garamond display + Manrope body).
     Pairing works on dark; M14 launch = self-host with latin-only subset. */
  --font-display: "Cormorant Garamond", "Cormorant", Georgia, serif;
  --font-body: "Manrope", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Song "voice" — JetBrains Mono 600 (Petřin výběr 2026-06-03 — „tracklist"
     mono: maximum contrast against both the Caveat handwriting and the
     Cormorant nick, so nothing competes; reads like a terminal track listing
     in the teal --song-name). Earlier same-day tries: Forum (weak/jagged),
     Cardo (fought the handwriting), Domine (good but mono won). ONLY the 600
     cut is loaded; used solely via .echo-song (the song tag). */
  --font-song: "JetBrains Mono", "Cascadia Code", Consolas, monospace;
  /* ====== MOMENT CONTENT typography (user-WRITTEN moments/pins) — single
     source of truth. Retune the moment "voice" from HERE; call sites never
     hardcode size/weight/line-height. (M12 experiment 2026-05-27, Petřin nápad.)
     Two voices, length-driven (threshold = MOMENT_HAND_MAX_CHARS in
     textExpandable.js): short = handwriting (Caveat, reads as "a fan wrote
     this"); long = calm roman serif (Lora, comfortable for a long block).
     Author/date/More chrome always stays Manrope. ====== */
  --font-moment:      "Caveat", "Segoe Print", "Comic Sans MS", cursive;
  --font-moment-long: "Lora", Georgia, serif;
  /* Handwriting has a small x-height → sized up vs body; same size in popup
     AND side-panel list (Petřin pokyn 2026-05-27 „dej to na stejnou jako popup"). */
  --fs-moment-hand:   1.35rem;
  --lh-moment-hand:   1.25;
  --fw-moment-hand:   500;
  /* Roman fallback — calm long-form reading, deliberately quieter/smaller than
     the handwriting (Petřin pokyn 2026-05-27 „ten roman je velký"). */
  --fs-moment-roman:  0.82rem;
  --lh-moment-roman:  1.45;

  /* ====== ALIASES — for asset CSS (icons.css, markers, components.css) ======
     Map asset-CSS token names onto canonical tokens. Single source of truth =
     the canonical token on the right; asset CSS keeps working without renaming. */
  --surface-ink:      var(--cream-base);
  --surface-midnight: var(--cream-surface);
  --surface-navy:     var(--cream-mist);
  --text-moon:        var(--text-dark);
  --text-moon-muted:  var(--text-muted);
  --text-mist:        var(--text-light-muted);
  --hairline-dark:    var(--border-on-light);
  --hairline-dark-lo: rgba(238, 240, 250, 0.08);
  /* M13.X-MEDALLION — the faint card-on-panel surface tint already used raw
     across M12 noir cards (e.g. POI/profile cards ~.04). Tokenised so the
     medallion + future cards share one value. */
  --surface-tint:     rgba(238, 240, 250, 0.03);
  --surface-tint-hi:  rgba(238, 240, 250, 0.05);
  --accent-violet-mid: var(--accent-violet);   /* iris mid is the default in Noir */

  /* ====== Songs accent — Noir PR3 (Petřin výběr B2 2026-05-24) ======
     Dedicated amethyst hue for the 🎵 Songs pebble (music / future M13
     community-ranked Tarja tracks). Distinct from every role color: teal=Save,
     iris=Add/community, copper=landmark/system, magenta=mine, mint=live.
     Was weakly teal (read as navy) — now its own luminous purple. */
  --accent-songs: #b588ff;
  --accent-songs-rgb: 181, 136, 255;

  /* ==========================================================================
     TYPE & SPACING SCALE — Noir PR3 (per handoff tokens-type-spacing.css).
     Atomic tokens + 3 composite text-role tokens. New primitive classes use
     these; legacy inline font-size migration is an opportunistic follow-up.
     ⚠️ Composite `font:` shorthand RESETS font-style → never use a composite
     where italic Cormorant is wanted; expand longhand instead.
     ========================================================================== */

  /* Font size — 8 atomic tokens (consolidates ~20 inline values, drift ≤1px) */
  --fs-3xs:  0.625rem;   /* 10px — feed location caps (toned below the nick, M13.X-PLACE-NAME) */
  --fs-2xs:  0.6875rem;  /* 11px — BETA pill, micro badge */
  --fs-xs:   0.75rem;    /* 12px — uppercase chip labels, meta */
  --fs-sm:   0.8125rem;  /* 13px — caption, secondary label */
  --fs-md:   0.875rem;   /* 14px — DEFAULT small UI body */
  --fs-base: 1rem;       /* 16px — body paragraph */
  --fs-lg:   1.125rem;   /* 18px — card title, sub-heading */
  --fs-xl:   1.375rem;   /* 22px — modal title (h3) */
  /* NO --fs-2xl / --fs-3xl — display/hero sizes stay responsive (clamp/media). */

  /* Title tiers (M13.X-TITLE-TIERS, Petřin pokyn 2026-06-03 — supersedes the
     "one size for every dialog" note in ADR-179). Content modals keep the big
     display title; quick confirm/reason/ban/delete prompts drop one step. */
  --fs-modal-title:  2.1rem;         /* ~33.6px — content modals & forms; restores the
                                        pre-ADR-179 Add-moment/Echo title size that the
                                        M12 PR6 title-unify shrank to 1.55rem (M13.X-TITLE-TIERS) */
  --fs-dialog-title: var(--fs-xl);   /* 22px — quick confirm/reason/ban/delete dialogs */

  /* Spacing — 4px base */
  --space-0:    0;
  --space-px:   1px;
  --space-0-5:  0.125rem;   /* 2px  */
  --space-1:    0.25rem;    /* 4px  */
  --space-1-5:  0.375rem;   /* 6px  */
  --space-2:    0.5rem;     /* 8px  */
  --space-3:    0.75rem;    /* 12px */
  --space-4:    1rem;       /* 16px */
  --space-5:    1.25rem;    /* 20px */
  --space-6:    1.5rem;     /* 24px */
  --space-8:    2rem;       /* 32px */
  --space-10:   2.5rem;     /* 40px */
  --space-12:   3rem;       /* 48px */
  --space-16:   4rem;       /* 64px */

  /* Line-height */
  --lh-tight:   1.15;   /* display */
  --lh-snug:    1.3;    /* headings */
  --lh-normal:  1.5;    /* body (default) */
  --lh-relaxed: 1.7;    /* long-form */

  /* Weight */
  --fw-regular:  400;
  --fw-medium:   500;
  --fw-semibold: 600;
  --fw-bold:     700;
  --fw-black:    800;

  /* Letter-spacing */
  --ls-tight:   -0.01em;   /* Cormorant display */
  --ls-normal:  0;         /* body */
  --ls-wide:    0.02em;    /* small caps body */
  --ls-wider:   0.08em;    /* label */
  --ls-loose:   0.18em;    /* uppercase chip */
  --ls-widest:  0.22em;    /* micro labels */

  /* Composite text-role tokens — 3 most stable patterns only. All Manrope
     (no italic) so the `font:` shorthand is safe. Italic Cormorant = longhand. */
  --text-eyebrow:   var(--fw-bold)    var(--fs-2xs)/var(--lh-tight)  var(--font-body);
  --text-body:      var(--fw-regular) var(--fs-md)/var(--lh-normal)  var(--font-body);
  --text-h-section: var(--fw-bold)    var(--fs-sm)/var(--lh-snug)    var(--font-body);
}

/* ==========================================================================
   Frisson Map — base styles (mobile-first)
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* M-UX-AUDIT-004 (M10 closure audit 2026-05-15) per nový P-032 (mobile):
   eliminate iOS Safari 300ms tap delay across ALL interactive elements.
   `touch-action: manipulation` disables double-tap zoom + delay without
   disabling pinch-zoom (which user-scalable=yes preserves). Map gestures
   live on the Leaflet container which sets its own touch-action, so this
   global rule doesn't interfere with pan/zoom on the map itself. */
button,
[role="button"],
a,
summary,
.leaflet-control a,
input[type="button"],
input[type="submit"],
input[type="reset"] {
  touch-action: manipulation;
}

html,
body {
  height: 100dvh;
  font-family: var(--font-body);
  background: var(--cream-base);
  color: var(--text-dark);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
  position: relative;
}

/* M12 rollout — Cormorant Garamond pro headlines (h1-h3) + identification
   labels v komponentách. Specificky komponenty mohou override (např. small
   UI labels) přes class. Light weight (300) pro hero ≥ 24px size minimum,
   medium (500) pro section h2, semibold (600) pro identifier labels. */
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: 0.02em;
}

/* Mapa = celý viewport pozadí. Map-first UX per Petřin pokyn 2026-05-14
   — grid layout (dashboard styl) bylo "vykreslené velké panely nad mapou"
   a Petra to vrátila zpět na floating overlay pattern. Controls jsou
   floating absolute, s jasně oddělenými zónami (top row clusters +
   sidebar pod nimi). */
#map {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Paint Voyager ocean tint as the first frame before Leaflet's
     `.leaflet-container` class lands. Matches .leaflet-container below
     so any pre-tile flash blends with the rendered map. */
  background: #cdeaf2;
}

/* Leaflet container background fills any gap the rendered tile layer
   leaves behind:
     – the off-tile band above ~85° N (Web Mercator can't render the
       poles), and
     – the brief window during a zoom transition where the new tile
       set hasn't landed yet.
   M11D ADR-081 Phase 1 originally switched this to atmospheric navy
   (#1a2a3a) for Frost DNA. Petřin pokyn 2026-05-19 wieczór reversed
   that priority: the dynamic-minZoom exact-fit (map.js
   `_computeFillMinZoom`) tries to leave zero band, but subpixel
   rounding can expose a 1px strip — that strip MUST blend with the
   ocean ("musí splývat s mapou, tudíž ta modra barva mapy"). The
   Voyager ocean tint #cdeaf2 makes any residual gap invisible. */
.leaflet-container {
  background: var(--map-ocean-tint);
}

/* World-view continent labels (continentLabels.js) — English names drawn over
   the label-free Voyager base, styled to echo Voyager's own continent type:
   muted grey, letter-spaced uppercase Cormorant with a soft white halo so they
   stay legible over land or sea. Override Leaflet's default div-icon chrome
   (white box + border) and center the text on its lat/lng anchor. */
.continent-label.leaflet-div-icon,
.continent-label {
  width: auto;
  height: auto;
  background: transparent;
  border: 0;
  box-shadow: none;
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--continent-label);
  text-shadow: 0 0 3px var(--continent-label-halo), 0 0 3px var(--continent-label-halo),
    0 0 6px var(--continent-label-halo);
  pointer-events: none;
  transform: translate(-50%, -50%);
}

/* Backend health indicator — colored traffic-light DOT
   (Petřin pokyn 2026-05-14: "dej to backend is alive úplně pryč,
   jenom puntík — zelený když je online, oranžový když se načítá,
   červený když failed"). No text, no pill chrome — just a 16 px
   dot with a soft cream halo so it reads against any tile color.
   The aria-label (set via JS or data-i18n-aria-label) carries the
   text for screen readers and as a native title tooltip. */
.status {
  display: inline-block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--warning);
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24 — "černý border pryč u všech
     indikátorů") — coloured glow per state, NO cream ring. */
  box-shadow: 0 0 8px 2px rgba(var(--warning-rgb), 0.55);
  /* The element holds no visible text — kill any inherited typography
     so accidental text doesn't expand the box. */
  font-size: 0;
  color: transparent;
  text-indent: -9999px;
  overflow: hidden;
  transition: background 0.25s ease, box-shadow 0.25s ease;
  cursor: help;
}

.status--checking {
  background: var(--warning);
  box-shadow: 0 0 8px 2px rgba(var(--warning-rgb), 0.55);
  animation: status-dot-pulse 1.4s ease-in-out infinite;
}

.status--ok {
  background: var(--success);
  /* Glow must follow the dot colour — base .status sets a warning-orange
     glow that otherwise lingered behind the green/red dots (Petřin postřeh
     2026-05-24: "zelený puntík, ale oranžový glow"). */
  box-shadow: 0 0 8px 2px rgba(var(--success-rgb), 0.55);
  animation: none;
}

.status--error {
  background: var(--danger);
  box-shadow: 0 0 8px 2px rgba(var(--danger-rgb), 0.55);
  animation: status-dot-pulse 0.9s ease-in-out infinite;
}

@keyframes status-dot-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* Floating placement: top-left, vertically centered against the
   search bar (top: 0.9 rem + ~13 px = vertical middle of the 42 px
   tall input). Horizontally right of the 320 px search bar with a
   0.75 rem gap. */
.status--floating {
  position: absolute;
  top: calc(max(0.9rem, env(safe-area-inset-top)) + 13px);
  left: calc(max(0.75rem, env(safe-area-inset-left)) + 320px + 0.75rem);
  z-index: 1000;
  pointer-events: auto;
}

@media (max-width: 767px) {
  /* Mobile (~360 px): tuck the dot to the right edge of the viewport,
     vertically aligned with the search bar — no room next to it. */
  .status--floating {
    top: calc(max(0.9rem, env(safe-area-inset-top)) + 13px);
    left: auto;
    right: max(0.9rem, env(safe-area-inset-right));
  }

  /* Petřin postřeh 2026-05-16 odp. — Leaflet zoom +/− tlačítka jsou
     na mobile redundantní (pinch zoom funguje nativně) a minus byl
     navíc zakrýtý footer chromem. Skrýt celý zoom control < 768 px. */
  .leaflet-control-zoom {
    display: none !important;
  }
}

/* ==========================================================================
   M11.X-MODAL-CENTERING — master rule pro VŠECHNY native <dialog> elementy.

   Per P-060: native <dialog>.showModal() polozí dialog do top-layer + UA stylesheet
   ho automaticky centruje. ALE fallback path (setAttribute("open","") pro starší
   WebKit) nebo dialog v non-modal módu UA centering ztratí → render jako block na
   top-left. Tahle belt-and-suspenders rule explicitně vynutí center positioning
   bez ohledu na open path. Per-dialog rules dál override width/height/styling.

   Specificity: low (0,0,1) aby každý class-level override (např.
   .pin-privacy-chooser-dialog s vlastní záměrnou bottom-left pozicí) wins.
   ========================================================================== */

dialog {
  position: fixed;
  inset: 0;
  margin: auto;
}

/* ==========================================================================
   Pin form modal — uses the native <dialog> element.
   ========================================================================== */

dialog#pin-form-dialog {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  /* M12 PR6 Fáze-0 shell parity — kanonický dialog radius (ADR-179), byl 20px literál. */
  border-radius: var(--radius-lg);
  padding: 0;
  max-width: 95vw;
  /* M13.X-ECHO-FLOW Fáze 2 (Petřin pokyn 2026-06-03) — the composer now holds
     the song rating, grouping cards and the location picker + mini-map; the
     440px small-form default read needlessly narrow on desktop. Bumped to the
     560px form tier (= .online-event-create-dialog, ui-standards §2). Mobile
     unaffected (95vw cap / fullscreen sheet). */
  width: 560px;
  max-height: 90dvh;
  /* UA default `dialog { overflow: auto }` rendered a second scrollbar
     next to `.pin-form__body`'s own overflow-y:auto in anonymous mode
     (= longer form variant — extra .pin-form__anon-nationality block).
     Petřin postřeh 2026-05-19 odp. „dva scrollbary v Add Pin". Dialog
     is a flex-column shell delegated to #pin-form which manages its
     own internal scrolling region; the outer overflow is redundant
     and visually noisy. `hidden` also makes the 20 px border-radius
     correctly clip the form's rounded corners. */
  overflow: hidden;
  /* Atmosphere: cream surface with subtle turquoise + violet radial tints
     in opposite corners — evokes the painted "splash" feel of Frisson art. */
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

dialog#pin-form-dialog::backdrop {
  /* Night-sky drama: dark navy with a faint turquoise vignette at center. */
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

#pin-form {
  /* AI-S1-M11D-006 per P-045 + new P-054 (mobile sticky-footer mandate):
     form acts as a flex column bound by the dialog's max-height so the
     inner .pin-form__body scrolls while header + .form-actions remain
     pinned. Padding moved to per-region so the sticky footer can bleed
     edge-to-edge with its cream gradient. */
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  max-height: 90dvh;
}

.pin-form__body {
  /* Scrollable middle region — the original .9rem flex gap moved here. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0.9rem 1.4rem 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* Header row — title + close button. The title gradient stays
   (Frisson brand) but the size drops since "Add a pin" is redundant
   after the user just clicked the Add Pin CTA. */
.pin-form__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin: 0;
  /* AI-S1-M11D-006: padding moved from #pin-form so the header stays
     pinned at the top of the flex column while body scrolls. */
  padding: 1.25rem 1.4rem 0.4rem;
  flex-shrink: 0;
}

/* M12 PR6 Fáze-0 — pin-form title typography now lives in the shared
   canonical title selector above (ADR-179); duplicate removed. The header
   is a center-aligned flex row, so the only pin-form-specific need is to
   drop the shared 0.8rem bottom margin (keeps the h2 vertically centered
   next to the close button). Single-property override, not a typography
   duplicate. */
#pin-form h2 {
  margin: 0;
}

/* M12 gathering title fix (Petřin pokyn 2026-05-31) — the title states the
   ACTION only ("Leave another Echo" / "Join with your Echo"). The header is a
   plain title + close button; the gathering identity it lands in lives in its own
   card at the top of the body (below), NOT floating in the header. */
.pin-form__header-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* M12 proposal A (Petřin pokyn 2026-05-31) — the gathering identity is its OWN
   tinted card in the body, same chrome as .pin-form__location (so it reads as a
   sibling of the Location box, "same product"), under a quiet "Adding to this
   gathering" eyebrow. This anchors the name + founder instead of leaving them as
   two floating lines under the title. */
.pin-form__gathering-context {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  padding: 0.7rem 0.85rem;
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
}
/* Eyebrow — canonical small tracked label (same token family as .field__label). */
.pin-form__gathering-eyebrow {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--label-color);
}
/* Gathering name = Cormorant display italic (bright --text-dark), --fs-lg — reads
   as the gathering's own title inside the card, mirroring .pin-popup__kotva-name. */
.pin-form__gathering-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-lg);
  line-height: 1.15;
  color: var(--text-dark);
  word-break: break-word;
}
/* Founder identity = avatar + ✦nick, soft violet — same author treatment as the
   gathering popup header (.pin-popup__by-nick), so the modal echoes the gathering's
   own identity. */
.pin-form__gathering-author {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--nick-color);
}
.pin-form__gathering-avatar {
  flex: 0 0 auto;
  display: inline-flex;
}
.pin-form__gathering-avatar svg {
  display: block;
  border-radius: 50%;
}
.pin-form__gathering-nick {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  color: inherit;
}
.pin-form__gathering-nick::before {
  content: "\2726";   /* ✦ owner mark, same as .pin-popup__by-nick */
  margin-right: 0.3em;
}

/* #pin-form .pin-form__close — unified styling shared with .dialog-close
   et al. (Petřin pokyn 2026-05-16 odp.). Visual rules live in the
   unified block below. The generic `#pin-form button:not(...)` chrome rule
   (~ř.1281) carries higher specificity than `#pin-form .pin-form__close`,
   so it MUST exclude `.pin-form__close` (`:not(.pin-form__close)`) — without
   that exclusion it clobbered the canonical 48×48 circle into a 10px-radius
   ghost rectangle (Petřin postřeh M12 Dávka-A 2026-05-25). */

/* Location preview card — compact one-line summary with optional
   GPS-privacy disclaimer (hidden for click/search paths where storage
   is exact). The card sits as the first content block so the user
   immediately sees WHERE the pin will land. */
.pin-form__location {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.7rem 0.85rem;
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 12px;
}

/* M12 Noir A② (mockup 08) — location is a flag + region chip row.
   The flag replaces the old "📍" emoji prefix per the FlagImg primitive. */
.pin-form__location-chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.pin-form__location-flag {
  flex-shrink: 0;
  border-radius: 2px;
}
.pin-form__location-value {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 600;
  /* Read-only — location is fixed by the Add-a-Moment flow, not typed here.
     Dimmer than primary so it doesn't read as an editable field value. */
  color: var(--text-readonly);
  font-variant-numeric: tabular-nums;
}

.pin-form__location-hint {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  line-height: 1.35;
}

.pin-form__location-actions {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
}
/* Add-mode hides the Move button, leaving this row empty — collapse it so the
   location box doesn't carry dead space below the chip (ADR-181 gap cleanup). */
.pin-form__location-actions:not(:has(> :not([hidden]))) {
  display: none;
}

.pin-form__location-actions .btn-link {
  font-size: 0.82rem;
}

/* Avatar collapse trigger — single-row preview that expands the grid
   underneath on click. Looks like a pill row, not a button-with-border
   so it sits softly within the form rather than competing with the
   primary submit CTA. */
#pin-form .avatar-toggle {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0.8rem;
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 12px;
  color: var(--text-dark);
  cursor: pointer;
  text-align: left;
  font: inherit;
  font-weight: 500;
  transition: border-color 180ms ease, background 180ms ease;
}

#pin-form .avatar-toggle:hover,
#pin-form .avatar-toggle:focus-visible {
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  transform: none;
  outline: none;
}

.avatar-toggle__preview {
  /* ADR-192 (Petřin pokyn 2026-05-28) — bare avatar disc, no cream
     backdrop and no teal inset ring. The sprite is its own opaque
     gradient circle, so the avatar fills the slot directly. Inline
     (Settings) variant adds the per-avatar glow via the shared
     "Avatar unification" rule (search ADR-192 in this file). */
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: transparent;
  border: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

.avatar-toggle__preview .avatar-tile-icon {
  /* The sprite fills the disc; the previous 60% inset only made sense
     when the slot had its own cream plate + ring (ADR-192 removed it). */
  width: 100%;
  height: 100%;
}

.avatar-toggle__preview.is-empty {
  background: repeating-linear-gradient(
    -45deg,
    var(--cream-base) 0 6px,
    var(--cream-sunken) 6px 12px
  );
}

.avatar-toggle__label {
  flex: 1 1 auto;
  font-size: 0.9rem;
  color: var(--text-dark);
}

.avatar-toggle__chevron {
  color: var(--text-muted);
  font-size: 0.85rem;
  transition: transform 200ms ease;
}

#pin-form .avatar-toggle[aria-expanded="true"] .avatar-toggle__chevron {
  transform: rotate(180deg);
}

/* When the grid is expanded, give it a little breathing room above. */
#pin-form-avatar-fieldset .avatar-grid:not([hidden]) {
  margin-top: 0.6rem;
}

#auth-profile-form .avatar-grid:not([hidden]) {
  margin-top: 0.6rem;
}

/* Identity row — avatar + username/nick na stejném řádku per Petřin
   pokyn 2026-05-15 odp. Used v Profile dialog (interactive avatar
   chooser) + Pin form (read-only random preview). */
.profile-identity-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  /* M12.X-PROFILE-FIDELITY — no ad-hoc margin; the form's canonical 0.9rem
     section gap (ADR-179) governs all field-to-field spacing now. */
  margin-bottom: 0;
}
/* Logged-in users have no nick input + no avatar preview — form.js hides both
   children, leaving an empty row that still reserved a double form-gap +
   margin (a visible hole, esp. after the category field was removed per
   ADR-181). Collapse it when no child is visible; anonymous users keep it. */
.profile-identity-row:not(:has(> :not([hidden]))) {
  display: none;
}
.profile-identity-row__username {
  flex: 1 1 auto;
  min-width: 0;
  /* Override default .field margin pro tight integration v row. */
  margin: 0;
}

/* Account-modals redesign (designer handoff 2026-06-04) — Profile dialog:
   avatar vertically centered with the nickname field (label + input);
   the rule hint moved OUT of the row, below it (see .profile-identity-hint).
   Scoped to the auth profile form — the pin-form identity row keeps its
   top alignment. */
#auth-profile-form .profile-identity-row {
  align-items: center;
}

/* The nickname rule hint spans below the avatar+field row. Negative-free:
   the form's flex gap supplies spacing above; pull is not needed because
   the hint follows the row directly. */
.profile-identity-hint {
  display: block;
}

/* Eyebrow section rule — tiny uppercase label + hairline running right
   (Profile ACCOUNT / PUBLIC PROFILE split, account-modals handoff). Uses
   the canonical eyebrow type token; hairline fades out like .auth-divider. */
.group-rule {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
/* A rule that OPENS a later section gets extra air above so the groups
   read as separate blocks (the form's 0.9rem flex gap alone was too
   uniform — caught on the render pass). */
.group-rule:not(:first-child) {
  margin-top: 0.6rem;
}
.group-rule > span {
  font: var(--text-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-light-muted);
  white-space: nowrap;
}
.group-rule::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(to right, var(--border-on-light), transparent);
}

/* Email privacy note — hint with a small lock glyph (was a parenthetical
   note on the label; account-modals handoff moved it under the value). */
.field__hint--lock {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.field__hint--lock svg {
  flex-shrink: 0;
}

/* Inline avatar-toggle variant — Petřin pokyn 2026-05-15 odp. iterace
   3 "to fakt ne. Budeme mit proste kruh s hooverem": ŽÁDNÝ button box,
   ŽÁDNÝ outer border, ŽÁDNÝ outer bg. Jen kulatý preview avatar +
   chevron pod ním. Use #ID selector pro maximum specificity vs UA. */
#pref-avatar-toggle.avatar-toggle--inline,
#pref-avatar-toggle {
  appearance: none;
  -webkit-appearance: none;
  background: transparent !important;
  border: 0 !important;
  padding: 0 !important;
  box-shadow: none !important;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  flex: 0 0 auto;
  cursor: pointer;
  margin-top: 1.35rem;
}
.avatar-toggle--inline .avatar-toggle__preview {
  width: 56px;
  height: 56px;
  /* Subtle hover-able circle — Petřin "kruh s hooverem". */
  transition: background 160ms ease, transform 160ms ease;
}
.avatar-toggle--inline .avatar-toggle__preview .avatar-tile-icon {
  /* ADR-192 — sprite fills the disc (was 70%; base rule is now 100%, the
     variant doesn't need its own override). */
  width: 100%;
  height: 100%;
}
.avatar-toggle--inline:hover .avatar-toggle__preview {
  background: var(--accent-turquoise-tint);
  transform: scale(1.04);
}
.avatar-toggle--inline:focus-visible .avatar-toggle__preview {
  background: var(--accent-turquoise-tint);
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
.avatar-toggle--inline:focus {
  outline: none;
}
.avatar-toggle--inline .avatar-toggle__label {
  display: none;
}
.avatar-toggle--inline .avatar-toggle__chevron {
  font-size: 1.1rem;
  line-height: 1;
  color: var(--text-dark);
}

/* Read-only preview variant — bez chevron, bez interactivity. Used
   v pin form pro anonymous users (avatar je random-generated, ne
   chooser). Žádné border, žádné box — plain circle. */
.avatar-toggle--preview-only {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  padding: 0;
  flex: 0 0 auto;
  margin-top: 1.35rem;
  cursor: default;
  background: transparent;
  border: 0;
}
.avatar-toggle--preview-only .avatar-toggle__preview {
  width: 48px;
  height: 48px;
}
.avatar-toggle--preview-only .avatar-toggle__preview .avatar-tile-icon {
  width: 65%;
  height: 65%;
}

/* M12 Noir — shrink the pin-form anon avatar preview (Petřin pokyn
   2026-05-25 "zmenšit tlačítko s avatarem"). The generic `#pin-form
   .avatar-toggle` box chrome (padding + 12px frame) was making it tower
   over the nickname input; these same-specificity rules (later in source)
   tighten the box and the circle so it reads as a small avatar, not a slab. */
#pin-form .avatar-toggle--preview-only {
  /* M12 Noir — circle avatar matching the moments-feed avatar (Petřin pokyn
     2026-05-25). Strip the #pin-form .avatar-toggle box chrome so it reads as
     a plain avatar token, not a slab. */
  padding: 0;
  margin-top: 1.35rem;
  background: transparent;
  border: 0;
}
#pin-form .avatar-toggle--preview-only .avatar-toggle__preview {
  /* ADR-192 (Petřin pokyn 2026-05-28) — the registered Add Moment
     identity preview now uses the same per-avatar glow as the moments
     feed (applied by the shared "Avatar unification" rule). The
     previous teal halo + cream plate + border read as a separate
     "settings chip" instead of an avatar. Diameter = nickname field
     height (~44px, border-box). */
  width: 44px;
  height: 44px;
}
#pin-form .avatar-toggle--preview-only .avatar-toggle__preview .avatar-tile-icon {
  /* Glyph fills the disc (matches feed avatar fullness). */
  width: 100%;
  height: 100%;
}

/* Avatar picker modal grid — scrollable (Petřin pokyn 2026-05-15 odp.
   "v budoucnu jich tam bude mnohem vice, musi mit ta nabidka vlastni
   modalni okno ve kterem se bude dat scrollovat"). */
/* ==========================================================================
   Avatar picker modal — Frost design rollout 2026-05-15 (Petřin postřeh
   „ikony jsou strašně velké a ten design je strašný"). Panel + backdrop
   + h2 styling analog s public-profile-modal (shared modal pattern),
   plus tile size + atmospheric splash override pro picker context.
   ========================================================================== */

.avatar-picker-dialog {
  max-width: 420px;
  width: min(92vw, 420px);
  padding: 1.4rem 1.3rem 1.5rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  /* Cream-surface + 2-corner atmospheric mesh (teal + plum) per shared
     „Custom div-based modal" pattern v design-system.md. */
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    var(--halo-multi),
    0 24px 60px rgba(0, 0, 0, 0.22);
}

.avatar-picker-dialog::backdrop {
  /* Shared 2-layer night-sky atmospheric backdrop (per design-system.md
     sekce „Custom div-based modal" → „Sdílený overlay backdrop"). */
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.avatar-picker-dialog h2 {
  /* M13.X-TITLE-TIERS — quick avatar chooser = small dialog tier (Petřin výběr
     2026-06-03). Self-contained again: pulled out of the unified big-modal
     selector list (which used to override the old 1.35rem turquoise look with
     the big white italic title). Keeps the white-italic canon + center align. */
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-dialog-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  color: var(--text-dark);
  margin: 0 0 1rem;
  text-align: center;
}

.avatar-picker-dialog .avatar-grid--scrollable {
  /* 5-col tighter grid pro picker context (= 12-24 tiles ve scroll
     okenku). Default .avatar-grid je 4-col pro form fieldsets. */
  grid-template-columns: repeat(5, 1fr);
  /* M-UX-AUDIT-043 (M10 closure audit) per nový P-034 (mobile):
     dvh prevents iOS Safari URL bar collapse cutting modal height. */
  max-height: 60dvh;
  overflow-y: auto;
  padding: 0.4rem;
  margin-top: 0;
  gap: 0.5rem;
}

.avatar-picker-dialog .avatar-tile {
  /* Natural cell-width from 5-col grid 1fr distribuce (~67px tile).
     PŮVODNÍ pokus s `max-width/height: 64px; justify-self: center`
     způsobil bug: justify-self: center stripne grid cell auto-fill →
     tile collapse na content size (empty span s CSS mask) → 0×0 → ikony
     invisible. Atmospheric splash mesh per Frost system. */
  background:
    radial-gradient(circle at 100% 0%, var(--frost-wash-teal), transparent 65%),
    radial-gradient(circle at 0% 100%, var(--frost-wash-plum), transparent 65%),
    var(--cream-base);
}

.avatar-picker-dialog .avatar-tile-icon {
  /* 60% → 50% — menší icon v picker context, větší breathing room. */
  width: 50%;
  height: 50%;
}

/* ==========================================================================
   ADR-189 Stage 4 — sprite avatars (full-colour <use> circles) replace the
   legacy single-colour CSS-mask glyphs. Two surfaces: the identity-row
   preview (Add Moment + Profile) and the new anonymous avatar picker.
   ========================================================================== */

/* Identity-row preview: the sprite is its own circle with a baked gradient, so
   neutralise the mask/currentColor treatment and let the <svg> fill the disc. */
.avatar-toggle__preview .avatar-tile-icon:has(svg),
.avatar-toggle__preview .avatar-tile-icon:has(img.fm-avatar-photo) {
  width: 100%;
  height: 100%;
  background-color: transparent;
  -webkit-mask-image: none;
  mask-image: none;
  border-radius: 50%;
  overflow: hidden;
}
.avatar-toggle__preview .avatar-tile-icon svg,
.avatar-toggle__preview .avatar-tile-icon img.fm-avatar-photo {
  width: 100%;
  height: 100%;
  display: block;
}


/* Anon Add Moment preview = picker trigger. Petřin pokyn 2026-05-27: NO boxy
   button frame — just the bare avatar disc with a pencil hint in the middle
   signalling it's editable. Strip the outer #pin-form .avatar-toggle button
   chrome too (was only stripping the inner preview), which left a tmavý
   zaoblený obdélník kolem avataru — Petřin postřeh 2026-05-28: "ja jsem
   řikala, ze tam nechci žádný čtverec kolem těch avatarů. avatar je
   tlačítko". */
#pin-form .avatar-toggle--clickable {
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 0;
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  gap: 0;
}
#pin-form .avatar-toggle--clickable:hover,
#pin-form .avatar-toggle--clickable:focus-visible {
  background: transparent;
  border-color: transparent;
}
#pin-form .avatar-toggle--clickable .avatar-toggle__preview {
  background: none;
  border: 0;
  padding: 0;
  box-shadow: none;
  position: relative;
  overflow: visible;
  transition: transform 160ms ease;
}
/* Pencil hint — bare outlined pencil pinned to the avatar's bottom-right
   edge (Petřin pokyn 2026-05-28 round 3: "nejde poznat že je to tužka …
   proč je tam to kolečko? chci normální rozeznatelnou tužku"). The teal
   coin around it crushed a 10px filled icon down to an unrecognisable
   diagonal stroke. Now: canonical outlined ICON_PENCIL (stroke-based,
   legible at 18px), turquoise stroke for hue parity with the avatar
   trigger, dark drop-shadow so the strokes stay readable on any avatar
   tone underneath. No coin, no ring.
   Round 4 (Petřin pokyn 2026-05-28): same affordance on the registered
   Profile dialog avatar — selector dropped #pin-form so it covers the
   inline pref-avatar-toggle too. The .avatar-toggle__preview span is
   already position:relative everywhere via the base rule, so the
   absolute hint anchors correctly on both surfaces. */
.avatar-toggle--clickable .avatar-toggle__preview {
  position: relative;
}
.avatar-toggle--clickable .avatar-toggle__edit-hint {
  position: absolute;
  bottom: -4px;
  right: -4px;
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  color: var(--accent-turquoise);
  filter:
    drop-shadow(0 0 2px rgba(0, 0, 0, 0.85))
    drop-shadow(0 1px 1px rgba(0, 0, 0, 0.65));
  transition: transform 160ms ease;
  pointer-events: none;
}
.avatar-toggle--clickable .avatar-toggle__edit-hint svg {
  width: 18px;
  height: 18px;
  display: block;
}
@media (hover: hover) and (pointer: fine) {
  #pin-form .avatar-toggle--clickable:hover .avatar-toggle__preview {
    transform: scale(1.05);
  }
  #pin-form .avatar-toggle--clickable:hover .avatar-toggle__edit-hint {
    transform: scale(1.1);
  }
}
#pin-form .avatar-toggle--clickable:focus-visible .avatar-toggle__edit-hint {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
#pin-form .avatar-toggle--clickable:focus {
  outline: none;
}

/* Reg avatar toggle is non-interactive while the reg picker is deferred
   (ADR-189 Stage 4 — sprite reg picker ships later). */
.avatar-toggle--static {
  cursor: default !important;
  pointer-events: none;
}

/* Anonymous avatar picker — reuses the .avatar-picker-dialog Frost shell; this
   modifier governs the two-set body (Mood + Tarja · Metal split by a divider).
   Grid layout derived from avatar-handoff/example.html + mockup 04. */
.avatar-picker-dialog--sets {
  width: min(94vw, 460px);
  max-width: 460px;
}
.avatar-picker__hint {
  margin: -0.4rem 0 1rem;
  text-align: center;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.avatar-picker__body {
  max-height: 56dvh;
  overflow-y: auto;
  padding: 0.2rem 0.3rem;
}
.avatar-set__divider {
  height: 1px;
  margin: 1rem 0.2rem;
  background: linear-gradient(90deg, transparent, var(--border-on-light), transparent);
}

/* Sprite tile grid — full-colour avatars, no cream-base fill (the sprite is its
   own circle). Selection = teal ring + halo around the disc (mirrors the
   .avatar-tile.selected cyan halo so the two pickers read as one system). */
/* .avatar-grid.avatar-grid--sprite (0,2,0) — must out-rank the base
   .avatar-grid repeat(4,1fr) that sits later in source. 5-col matches the
   existing reg avatar-picker grid for cross-picker consistency. */
.avatar-grid.avatar-grid--sprite {
  grid-template-columns: repeat(5, 1fr);
  gap: 0.55rem;
}
.avatar-tile--sprite {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3px;
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease;
}
.avatar-tile--sprite svg,
.avatar-tile--sprite img.fm-avatar-photo {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 50%;
}
/* M13N — picker tiles glow with each avatar's own --av-glow, same as in-app.
   The picker is dark Noir now, so the old cream-Frost "no glow" exemption is
   stale; sprite <svg> and photo <img> get the identical halo treatment. */
.avatar-tile--sprite svg,
.avatar-tile--sprite img.fm-avatar-photo {
  filter: drop-shadow(0 0 4px rgba(var(--av-glow, 221, 227, 244), 0.8))
    drop-shadow(0 0 7px rgba(var(--av-glow, 221, 227, 244), 0.42));
}
@media (hover: hover) and (pointer: fine) {
  .avatar-tile--sprite:hover {
    transform: translateY(-2px) scale(1.05);
    box-shadow: 0 0 0 2px rgba(var(--accent-turquoise-rgb), 0.35),
      0 6px 14px rgba(var(--accent-turquoise-rgb), 0.18);
  }
}
.avatar-tile--sprite:focus {
  outline: none;
}
.avatar-tile--sprite:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-turquoise),
    0 0 0 5px rgba(var(--accent-turquoise-rgb), 0.22);
}
.avatar-tile--sprite.selected,
.avatar-tile--sprite.selected:hover {
  box-shadow: 0 0 0 2px var(--accent-turquoise),
    0 0 0 5px rgba(var(--accent-turquoise-rgb), 0.20),
    0 8px 22px rgba(var(--accent-turquoise-rgb), 0.28);
  transform: translateY(-2px);
}

/* Category pills — replaces stacked radio with a tighter 2-button row.
   Visually closer to the Frisson design system's "Tab switcher" pattern
   (design-system.md, M10B addition). Native <input> stays for a11y. */
/* Add-moment "What kind of moment?" switch moved to the canonical FSegment
   primitive in M12 Noir A② (was .category-pills/.category-pill — now dead,
   removed). Radiogroup styling lives with .segment (see FSegment block). */

/* "More details" disclosure — uses native <details> for zero JS.
   Mirrors the .mfa-manual pattern (auth dialog) so we don't drift
   between two disclosure looks. */
.pin-form__more {
  /* No border-top — the summary itself carries enough visual weight
     after Petřin 2026-05-14 feedback (= "miniaturní, prehledla"). */
}

.pin-form__more > summary {
  cursor: pointer;
  /* M-UX-AUDIT-024 per P-030 (mobile): disclosure summary ≥44px tap
     target. Matches .pin-form__anon-nationality > summary pattern. */
  padding: 0.75rem 0.85rem;
  min-height: 44px;
  color: var(--accent-turquoise);
  font-weight: 700;
  font-size: 0.92rem;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.35rem;
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.22);
  border-radius: var(--radius-sm);
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
}

.pin-form__more > summary::-webkit-details-marker {
  display: none;
}

.pin-form__more > summary::after {
  /* Petřin postřeh 2026-05-16 odp. — šipka 0.85rem byla "hrozně malá"
     v rozevíratelném seznamu. Bumped to 1.15rem (~18 px) + bold weight
     pro solid visual presence. Apply app-wide na všechny disclosure
     markers. */
  content: "▾";
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1;
  transition: transform 200ms ease;
}

.pin-form__more[open] > summary::after {
  transform: rotate(180deg);
}

.pin-form__more > summary:hover,
.pin-form__more > summary:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  border-color: var(--accent-turquoise);
  outline: none;
}

.pin-form__more-body {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin-top: 0.7rem;
}

/* M10F per ADR-070 — anonymous-only opt-in nationality block. Mirrors
   the .pin-form__more disclosure pattern so the two collapsibles read
   as siblings inside the same form. JS keeps the whole element hidden
   for signed-in users. */
.pin-form__anon-nationality > summary {
  cursor: pointer;
  /* M10F Stage 1 (AI-S1-M10F-007 per P-030 mobile tap target ≥ 44 px):
     padding 0.75rem + font 0.9rem yields computed ~46 px height, above
     iOS HIG 44 + Material 48 dp guideline.
     M10F Stage 2 (AI-S2-M10F-003 per STYLE-003/009): border alpha +
     radius aligned with `.pin-form__more > summary` sibling so the two
     disclosure togglers in the same form read as visual peers, not as
     different control types. Muted border `rgba(var(--accent-turquoise-rgb),0.22)`
     replaces full `var(--accent-turquoise)` (which "křičelo" oproti
     subtle modal mesh); shared `var(--radius-sm)` replaces 6px. */
  padding: 0.75rem 0.85rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.22);
  border-radius: var(--radius-sm);
  background: rgba(var(--accent-turquoise-rgb), 0.06);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--accent-turquoise);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  transition: background 160ms ease, border-color 160ms ease;
}

.pin-form__anon-nationality > summary::-webkit-details-marker {
  display: none;
}

.pin-form__anon-nationality > summary::after {
  /* Petřin postřeh 2026-05-16 odp. — viz .pin-form__more sibling. */
  content: "▾";
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1;
  transition: transform 160ms ease;
}

.pin-form__anon-nationality[open] > summary::after {
  transform: rotate(180deg);
}

.pin-form__anon-nationality > summary:hover,
.pin-form__anon-nationality > summary:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  border-color: var(--accent-turquoise);
  outline: none;
}

.pin-form__anon-nationality-body {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin-top: 0.7rem;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  border: none;
  position: relative;
}

/* fieldset.field (category segment, report reasons) keeps UA <fieldset>
   insets (padding 0.35em/0.75em + margin 0 2px + min-inline-size: min-content)
   that push its content inward and misalign it from sibling .field rows.
   Reset so a fieldset row lines up flush with text/input rows — the segment
   "What kind of moment?" now shares the same left edge + breathing room as
   every other field (M12 Dávka-A 2026-05-25). */
fieldset.field {
  padding: 0;
  margin: 0;
  min-inline-size: 0;
}

[hidden] {
  display: none !important;
}

/* M12 Noir PR6-A — field labels follow the locked primitives spec
   (m12-primitives.md §2): muted, medium weight, wide tracking. The
   Frost-era loud teal/700 label pulled the eye to the caption instead
   of the input; every dialog mockup (07–14) shows the quiet muted
   treatment. Canonical primitive → propagates app-wide. */
.field__label {
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--label-color);
}

/* Canonical SECTION LABEL — a small heading that titles a block (e.g. the
   popup "Where fans are talking" discussion section). GLOBAL style so labels
   read as one system everywhere: identical tone (--label-color) + tracking as
   the modal field labels above, plus the block spacing a standalone heading
   needs. Apply this class instead of re-styling per surface (Petřin pokyn
   2026-05-27 — global style, side agent applies). */
.fm-section-label {
  margin: var(--space-3) 0 var(--space-1-5);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--label-color);
}

/* M12.X-PROFILE-FIDELITY — inline note sitting next to a field label
   (email privacy parenthetical, social-links N/5 counter). Stays readable:
   does NOT inherit the label's uppercase caps + letter-spacing. */
.field__label-note {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  /* Tracks the label size (--fs-xs) so the note never reads bigger than
     the label it annotates (label shrink 2026-06-03). */
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* A <legend> doesn't participate in the fieldset's flex gap, so it needs an
   explicit margin matching the within-field label→content gap (0.4rem). One
   rule for every fieldset legend (privacy group + social-links group) so the
   rhythm is uniform instead of per-dialog hand-tuning. */
fieldset.field > legend.field__label {
  margin-bottom: 0.4rem;
}

/* Social-links legend = label + capacity counter on one row. The counter
   sits right after the label (like the email "(not visible)" note), NOT
   pushed to the far right (Petřin pokyn 2026-05-27). */
.contacts-legend {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
}

/* AI-S1-M11D-002 per new P-052 — required field label MUSÍ mít visual
   marker, ne jen absence of "(optional)" suffix u sourozenců. The copper
   accent matches the destructive/attention colour family already in
   use (see --copper-accent in :root). */
.field__label--required::after,
/* Echo composer: the LOCATION group head IS the required field label (its
   inner .field__label was dropped, ADR-236), so the marker rides the heading.
   Composer-only modifier toggled by form.js — never on the read-only chip. */
.echo-group__head--required h3::after {
  content: " *";
  color: var(--copper-accent, var(--accent-copper));
  font-weight: 700;
  /* Bigger than the inherited label size — the baseline-sized "*" read as
     titěrné (Petřin pokyn 2026-06-08). em keeps it proportional per surface;
     vertical-align nudge keeps the taller glyph from lifting the line box. */
  font-size: 1.35em;
  line-height: 1;
  vertical-align: -0.08em;
  margin-left: 0.15rem;
}

/* AI-S1-M11D-003 per new P-050 — field-specific validation error sits
   adjacent to the focused field (inside the same .field label wrapper)
   so users on mobile keyboard reflow see the message without scrolling
   to the bottom shared #form-error region. Shared #form-error stays
   for field-agnostic errors (CAPTCHA, network). */
.field__error {
  font-size: 0.8rem;
  color: var(--danger-text);
  font-weight: 600;
  margin-top: 0.2rem;
  /* Stays in DOM with [hidden] toggling so layout doesn't reflow
     unexpectedly when the error appears. */
}

/* Beta feedback 2026-06-09 — the comment counter + validation message share one
   row: the message takes the left and wraps if long, the counter stays pinned to
   the right under the textarea (its original spot), so showing the message never
   pushes the counter down. The counter drops its absolute placement inside here. */
.field__meta-row {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin-top: 0.25rem;
}
.field__meta-row .field__error {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0; /* allow the message to wrap instead of overflowing the row */
}
.field__meta-row .field__counter {
  position: static; /* override the global absolute bottom-right placement */
  margin: 0 0 0 auto; /* keep it on the right even when the message is hidden */
  flex: 0 0 auto;
  white-space: nowrap;
}
/* The counter is no longer absolute in this field, so drop the reserved strip
   the global rule adds for the absolute placement (higher specificity wins). */
.echo-group .field:has(.field__meta-row) {
  padding-bottom: 0;
}

/* Petřin postřeh 2026-05-16 odp. — datetime-local v Add Pin „More
   details" byl defaultním browser stylem (Czech locale, calendar icon
   bez tint). Generalize: všechny inputs uvnitř `.field` wrapper dostanou
   Frisson cream-sunken styling napříč aplikací (pin-form, suggest-online,
   admin events). Per feedback_audit_repair_app_wide_not_milestone. */
/* The Frost time/duration pickers carry their own box on the wrapper
   (.frost-time-picker / .frost-duration-picker__field); their inner inputs
   must stay borderless/transparent. Exclude them here so the global field
   box doesn't double up (was the "frame in frame" + outside-chevron bug). */
.field input[type="text"]:not(.frost-time-picker__input):not(.frost-duration-picker__input),
.field input[type="datetime-local"],
.field input[type="date"],
.field input[type="time"],
.field input[type="number"],
.field input[type="url"],
.field input[type="email"],
.field input[type="password"],
.field input[type="search"],
.field textarea,
.field select {
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  /* --input-pad-left: lets a wrapper (.input-affix) widen the left padding
     for an inline leading glyph WITHOUT fighting this rule's specificity —
     the literal padding-left on .input-affix > input always lost here and
     the text slid under the magnifier (Petřin postřeh 2026-06-07). */
  padding: 0.7rem 0.9rem 0.7rem var(--input-pad-left, 0.9rem);
  color: var(--text-dark);
  font: inherit;
  /* M-UX-AUDIT-005 (M10 closure audit 2026-05-15) per P-033 (mobile):
     iOS Safari auto-zoom při focus pokud font-size < 16px. */
  font-size: 1rem;
  width: 100%;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}

.field textarea {
  resize: vertical;
}

/* Comment box mirrors the moment's display VOICE live (Petřin nápad
   2026-05-28) so users see exactly how their text will render — not a
   generic system font. Short text = handwriting (Caveat); past the length
   threshold it flips to the calm roman serif (Lora). Same tokens + same
   `isHandwrittenMoment()` helper as the popup/list (textExpandable.js), so
   the field and the rendered moment can never diverge. form.js toggles
   `.is-roman` once the text crosses MOMENT_HAND_MAX_CHARS.
   Bonus: the roman voice (13px) fits far more text than the old flat size —
   so it also solves the "text too big / too little fits" ask.
   Tradeoff: the roman voice is < 16px → iOS Safari focus auto-zoom; accepted
   for this single field. */
#comment-input {
  font-family: var(--font-moment);
  font-size: var(--fs-moment-hand);
  line-height: var(--lh-moment-hand);
  font-weight: var(--fw-moment-hand);
  /* Stable height regardless of voice: the smaller roman font would shrink a
     rows="3" box, so pin a min-height to the handwriting 3-row size. The box
     never collapses on the voice flip; the user can still drag it taller
     (resize: vertical inherited from .field textarea). Petřin pokyn
     2026-05-28. */
  min-height: 6.5rem;
}
#comment-input.is-roman {
  font-family: var(--font-moment-long);
  font-size: var(--fs-moment-roman);
  line-height: var(--lh-moment-roman);
  font-weight: 400;
}

/* M12 (Petřin pokyn 2026-05-26) — placeholder text inside inputs used the
   browser default, which on the dark Noir surface is almost invisible (very
   low opacity) → "nejde to přečíst". 2026-06-04 placeholder audit (Petřin
   pokyn): ALL placeholders share this ONE rule + ONE token (--text-placeholder)
   — per-component placeholder colors were removed. Dimmer than typed text
   (--text-dark) so it still reads as a hint, but legible (~7.6:1). */
input::placeholder,
textarea::placeholder {
  color: var(--text-placeholder);
  opacity: 1;
}

/* M12 desktop density (Petřin pokyn 2026-05-26) — the base field padding +
   the date-picker display are tuned for mobile (44px tap target + iOS
   focus-zoom guard via 1rem font). On desktop that vertical padding reads as
   oversized and inflates every modal/form top-to-bottom. Trim the vertical
   padding on wide viewports ONLY; mobile (< 768px) keeps the comfortable tap
   target untouched. Font stays 1rem so readability is unchanged. One shared
   rule → propagates to every form/modal app-wide (the systemic "puffy modals"
   fix, not a per-dialog patch). Location search (0.5rem) is already moderate;
   time/duration pulled to 0.5rem too so the whole When row + Location align. */
@media (min-width: 768px) {
  .field input[type="text"]:not(.frost-time-picker__input):not(.frost-duration-picker__input),
  .field input[type="datetime-local"],
  .field input[type="date"],
  .field input[type="time"],
  .field input[type="number"],
  .field input[type="url"],
  .field input[type="email"],
  .field input[type="password"],
  .field input[type="search"],
  .field textarea,
  .field select,
  /* Bespoke modal textareas NOT wrapped in `.field` — kept in lockstep so a
     modal never mixes a tall reason/description box with short `.field` rows
     (Petřin pokyn 2026-05-26 — density musí být globální v modálech). */
  .confirm-dialog textarea,
  #report-form textarea,
  .frost-modal textarea {
    /* Same --input-pad-left hook as the base rule above (leading-glyph inputs). */
    padding: 0.5rem 0.9rem 0.5rem var(--input-pad-left, 0.9rem);
  }
  /* All three When-row pickers share ONE box height on desktop (date display,
     time wrapper, duration field) so date/time/duration line up (Petřin pokyn
     2026-05-26 — "každá komponenta má jinou výšku"). */
  .frost-date-picker__display,
  .frost-time-picker,
  .frost-duration-picker__field {
    padding: 0.4rem 0.6rem 0.4rem 0.9rem;
  }
}

/* Native datetime / date / time picker icon — tint to match the teal
   accent so it doesn't read as foreign Chrome chrome. The filter
   roughly approximates --accent-turquoise (#0d7575). */
.field input[type="datetime-local"]::-webkit-calendar-picker-indicator,
.field input[type="date"]::-webkit-calendar-picker-indicator,
.field input[type="time"]::-webkit-calendar-picker-indicator {
  filter: invert(38%) sepia(72%) saturate(467%) hue-rotate(141deg) brightness(85%);
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 200ms ease;
}

.field input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
.field input[type="date"]::-webkit-calendar-picker-indicator:hover,
.field input[type="time"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  /* Glowing turquoise halo around the focused field. */
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

.field__counter {
  /* Petřin postřeh 2026-05-15 večer „male odsazeni počtadla znaků a
     pole pod nim". Counter je absolute pod textareou ALE parent .field
     flex column gap 0.4rem nezarezervoval místo → next element (More
     details / submit) překrýval counter. Fix: counter bottom 0,
     positioned uvnitř field padding (= reserved space), text-align
     right. Plus field-level padding-bottom 1.1rem pro vyhrazení místa. */
  position: absolute;
  right: 0.2rem;
  bottom: 0;
  font-size: 0.7rem;
  color: var(--text-muted);
}

.field:has(.field__counter) {
  /* Reserve room below textarea/input so absolute counter doesn't
     overlap next element. */
  padding-bottom: 1.1rem;
}

/* AI-S1-M11D-004 per new P-051 — char-limited counter MUSÍ mít threshold
   warn state. The skok 280→500 v M11D ADR-080 = delší typing trajektorie,
   warn state preventuje submit fail with `commentTooLong`. */
.field__counter--warn {
  color: var(--accent-copper);
  font-weight: 600;
}
.field__counter--max {
  color: var(--danger-text);
  font-weight: 700;
}

/* AI-S1-M11D-006 — sticky footer pattern. Overrides global .form-actions
   margin-top: 0.6rem so the footer can sit flush at the bottom of the
   form, sticky against scroll. The cream-fade gradient softens the
   transition from scrollable body so users perceive the footer as a
   continuation of the surface, not a floating overlay. */
#pin-form > .form-actions {
  margin: 0;
  padding: 0.7rem 1.4rem 1.3rem;
  flex-shrink: 0;
  position: sticky;
  bottom: 0;
  background: linear-gradient(
    180deg,
    rgba(252, 248, 240, 0) 0%,
    var(--cream-surface) 35%
  );
}

/* Avatar grid — 4 columns (12 avatars = 3 rows). */
.avatar-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.6rem;
}

.avatar-tile {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;

  aspect-ratio: 1;
  background: var(--cream-base);
  border: 2px solid transparent;
  /* Petřin pokyn 2026-05-15 odp.: kulaté všude (konzistence s pin
     marker + popup nick avatar). Předchozí 14px rounded square byl
     outlier. */
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  padding: 0;
  box-shadow: inset 0 0 0 1px var(--border-on-light);
  transition: transform 200ms ease, background 200ms ease, color 200ms ease,
              border-color 200ms ease, box-shadow 200ms ease;
}

/* UX-AUDIT-009 (M10 closure audit 2026-05-15): hover guarded behind
   @media (hover: hover) so touch devices don't get sticky-hover state
   that lingers after tap. Per P-024. */
@media (hover: hover) and (pointer: fine) {
  .avatar-tile:hover {
    background: var(--cream-surface);
    color: var(--accent-turquoise);
    transform: translateY(-2px);
    box-shadow:
      inset 0 0 0 1px var(--border-on-light),
      0 6px 14px rgba(var(--accent-turquoise-rgb), 0.14);
  }
}

.avatar-tile:focus {
  outline: none;
}

.avatar-tile:focus-visible {
  outline: none;
  border-color: var(--accent-turquoise);
  box-shadow:
    inset 0 0 0 2px var(--accent-turquoise),
    0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.20);
}

.avatar-tile.selected,
.avatar-tile.selected:hover {
  border-color: var(--accent-turquoise);
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
  /* Cyan halo — the selected avatar glows like the splash in the paintings. */
  box-shadow:
    inset 0 0 0 2px var(--accent-turquoise),
    0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18),
    0 8px 24px rgba(var(--accent-turquoise-rgb), 0.25);
  transform: translateY(-2px);
}

/* Avatar icon — uses CSS mask so the SVG inherits currentColor of the
   parent .avatar-tile (otherwise <img>'d SVGs fall back to black). */
.avatar-tile-icon {
  width: 60%;
  height: 60%;
  background-color: currentColor;
  -webkit-mask-position: center;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-position: center;
  mask-repeat: no-repeat;
  mask-size: contain;
  pointer-events: none;
}

/* Form footer */
.form-actions {
  display: flex;
  gap: 0.7rem;
  justify-content: flex-end;
  margin-top: 0.6rem;
}

/* Account-modals redesign (designer handoff 2026-06-04) — full-width
   balanced footer pair, EQUAL widths (Petřin pokyn 2026-06-04 večer:
   "mají být stejně velké"; supersedes the mockup's 1 : 1.5 ratio).
   Opt-in modifier; the base .form-actions stays right-aligned
   everywhere else. */
.form-actions--span > button {
  flex: 1 1 0;
  min-width: 0;
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
}

/* Hairline above the footer pair (Profile + Preferences boards) — closes
   the form before the actions. Sign in keeps its rule BELOW the buttons
   (.auth-signup-row owns it). */
.form-actions--ruled {
  border-top: 1px solid var(--border-on-light);
  padding-top: 1rem;
  margin-top: 0.9rem;
}

/* Ghost (cancel) — transparent with subtle outline. */
/* M11.X-CONTAINER-ENTITY (Petřin pokyn 2026-05-24) — the `:not([class*="frost-"])`
   guard keeps this generic form-button chrome (border/padding) OFF the shared
   Frost date/time picker's internal buttons (calendar nav arrows, day cells,
   time toggle/slots). Without it the picker rendered "completely different"
   in the pin form (empty nav boxes, wrapped date) vs the event modal, which
   has no #pin-form scope. */
/* :not(.rating-step) added (ADR-236) — the inline song-rating ± steppers
   reuse the canonical give-stars .rating-step look; this generic chrome's
   :not() chain carries higher specificity than any single-class override,
   so the exclusion has to live HERE (same pattern as .echo-location__locate). */
#pin-form button:not([class*="frost-"]):not([class*="btn-"]):not(.pin-form__close):not(.avatar-toggle):not(.echo-location__locate):not(.venue-card__map-toggle):not(.rating-step):not(.echo-grouping__card):not(.rating-country__link) {
  background: transparent;
  color: var(--text-dark);
  border: 1px solid var(--border-strong-light);
  border-radius: 10px;
  padding: 0.65rem 1.3rem;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease, transform 200ms ease;
}

@media (hover: hover) and (pointer: fine) {
  #pin-form button:not([class*="frost-"]):not([class*="btn-"]):not(.pin-form__close):not(.echo-location__locate):not(.venue-card__map-toggle):not(.rating-step):not(.echo-grouping__card):not(.rating-country__link):hover {
    background: rgba(238, 240, 250, 0.05);
    border-color: var(--text-dark);
  }
}

/* Submit "Add a moment" — TURQUOISE per mockup (Petřin pokyn 2026-05-24:
   "submit tlačítka mají být tyrkysová s mírným gradientem uvnitř"). Uses the
   shared .btn-primary class (teal gradient). The generic `#pin-form button`
   rule above excludes `[class*="btn-"]` so it no longer clobbers role-classed
   buttons. NOTE: supersedes ADR-078 Fix 3 (which proposed violet) — the
   designer mockup shows the form's primary submit as teal, not iris. */

/* M12 Noir A② (mockup 08) — "+" glyph before "Add your moment". Toggled via
   .has-plus (form.js applyMode) so edit mode ("Save changes") stays glyph-less. */
#pin-form-submit.has-plus::before {
  content: "+";
  font-weight: 700;
  font-size: 1.15em;
  line-height: 0;
  margin-right: 0.15rem;
}

.form-error {
  color: var(--danger-text);
  font-size: 0.85rem;
  font-weight: 600;
}

/* Heads-up tone (not an error): the federated "tap once more" notice after an
   in-app-browser handoff. Gold reads as "one more step", never the red of a
   real failure — same element/spot, colour only. */
.form-error.is-notice {
  color: var(--warning);
}

.form-success {
  color: var(--success);
  font-size: 0.85rem;
  font-weight: 600;
}

/* Label row with a right-aligned affordance — account-modals redesign
   (designer handoff 2026-06-04): "Forgot password?" sits ON the PASSWORD
   label line instead of its own row below the field (supersedes the
   .auth-forgot-row + :has() pull-up from earlier the same day; that row
   is gone from the markup). The label inside keeps its canonical
   .field__label styling; baseline align so label and link share a line. */
.field__label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
}

/* "Don't have an account? Sign up" cross-link under the Cancel/Sign in row
   (Petra 2026-06-04 — anonymous users used to have to close the dialog and
   find Sign up in the menu). Muted prefix + canonical teal .btn-link.
   Hairline rule + breathing room turn the row into a deliberate footer
   note instead of an afterthought stuck to the buttons (designer variant C,
   Petra 2026-06-04). */
.auth-signup-row {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 0.35rem;
  margin: 1.25rem 0 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border-on-light);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* "Forgot password?" = teal underlined (Petra 2026-05-27 — reverted the
   M12 PR6 Dávka C copper treatment back to the teal link hue). */
#auth-forgot-link {
  color: var(--accent-turquoise);
  text-decoration: underline;
  font-weight: 600;
}
#auth-forgot-link:hover,
#auth-forgot-link:focus-visible {
  color: var(--accent-turquoise-bright);
}

/* ==========================================================================
   App footer — Privacy / Terms links over the map.
   ========================================================================== */

.app-footer {
  /* M10E sub-blok 3/4 — Frost atmospheric pill: subtle 2-corner teal+plum
     wash on cream-surface + halo-multi shadow (single layer). */
  position: absolute;
  left: 50%;
  bottom: 0;
  z-index: 900;
  transform: translateX(-50%);
  margin-bottom: max(0.5rem, env(safe-area-inset-bottom));
  /* No inner padding — links carry their own generous padding so the
     hover/tap area spans the whole chip (same pattern as events tabs). */
  padding: 0;
  border-radius: var(--radius-pill);
  background:
    radial-gradient(circle at 0% 50%, var(--frost-wash-teal), transparent 65%),
    radial-gradient(circle at 100% 50%, var(--frost-wash-plum), transparent 65%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: var(--halo-multi);
  display: flex;
  align-items: stretch;
  font-family: var(--font-body);
  /* Clip link chips to the pill so their hover state respects the rounded
     edges instead of bleeding past them. */
  overflow: hidden;
}

.app-footer a {
  /* Manrope 600 CAPS 0.7rem + 0.10em tracking + teal — secondary meta
     weight (Petřin postřeh 2026-05-15 „nemusí být tak tučné, není hlavní
     věc"). Events tabs mají 800 protože jsou primary navigation; footer
     je secondary meta info, tlumenější weight stačí. Hover = teal-tint
     background change on whole chip area (padding included). */
  /* M-UX-004 (Stage 1 review): padding 0.55rem 0.95rem → 0.85rem 1.1rem +
     min-height 44px. Footer chip height was ~28-30px → fingers miss tap
     target on mobile. About link is primary entry to sub-blok 3 page. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--accent-turquoise);
  padding: 0.85rem 1.1rem;
  background: transparent;
  transition: background 160ms ease, color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .app-footer a:hover {
    background: var(--accent-turquoise-tint);
    color: var(--accent-turquoise);
  }
}

.app-footer a:focus-visible {
  background: var(--accent-turquoise-tint);
  outline: 2px solid var(--accent-violet-bright);
  outline-offset: -2px;
  border-radius: 4px;
}

/* Hide app-footer (Privacy / Terms / About) on mobile per Petřin pokyn
   2026-05-16: links už jsou v hamburger menu (#app-menu), duplicate
   v persistent footer není potřeba na mobile + zbytečně bere bottom
   real estate. Desktop-only. */
@media (max-width: 767px) {
  .app-footer {
    display: none !important;
  }
}

.app-footer__sep {
  /* Thin teal-tinted vertical divider — same DNA jako events-panel__rail-label
     (border-bottom: 1px solid var(--border-on-light-strong-brand)) per Petřin
     pokyn 2026-05-15 „oddělovače jako u záložek". HTML obsah glyphu skryt
     přes font-size 0, separator je čistě CSS vertical line. */
  align-self: stretch;
  width: 1px;
  margin: 0.45rem 0;
  background: var(--border-on-light-strong-brand);
  font-size: 0;
  pointer-events: none;
  user-select: none;
}

/* ==========================================================================
   Cookie banner — bottom-of-screen info notice (GDPR-friendly).
   ========================================================================== */

.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: 0;
  z-index: 1100;
  transform: translate(-50%, 110%);
  margin-bottom: max(0.75rem, env(safe-area-inset-bottom));
  padding: 0.95rem 1.1rem;
  border-radius: var(--radius-md);
  background: var(--cream-surface);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border-on-light);
  box-shadow: var(--shadow-modal);
  max-width: calc(100vw - 1.5rem);
  width: 460px;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  transition: transform 240ms ease;
}

.cookie-banner--visible {
  transform: translate(-50%, 0);
}

.cookie-banner__text {
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--text-dark);
}

.cookie-banner__btn {
  align-self: flex-end;
  background: linear-gradient(135deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
  color: var(--text-on-accent);
  border: 1px solid var(--accent-copper);
  border-radius: var(--radius-sm);
  /* M-UX-AUDIT-035 per P-036: 44px tap target — GDPR consent primary. */
  padding: 0.7rem 1.2rem;
  min-height: 44px;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    0 3px 10px rgba(var(--accent-copper-rgb), 0.25),
    inset 0 1px 0 rgba(238, 240, 250, 0.18);
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}

.cookie-banner__btn:hover,
.cookie-banner__btn:focus-visible {
  box-shadow:
    0 6px 16px rgba(201, 139, 75, 0.35),
    inset 0 1px 0 rgba(238, 240, 250, 0.22);
  outline: none;
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-media guard
   for transform-y lift (touch sticky-hover prevention per Atmosphere rule 8). */
@media (hover: hover) and (pointer: fine) {
  .cookie-banner__btn:hover {
    transform: translateY(-1px);
  }
}

@media (min-width: 768px) {
  .cookie-banner {
    flex-direction: row;
    align-items: center;
  }
  .cookie-banner__btn {
    align-self: auto;
    flex-shrink: 0;
  }
}

/* ==========================================================================
   Legal pages (privacy.html, terms.html) — body class "legal-page".
   Different layout from the map: scrollable, max-width content.
   ========================================================================== */

/* Legal pages need a scrollable root — base `html, body { height: 100dvh;
   overflow: hidden }` is for the map viewport, not for long-form text. */
html.legal-html,
html.legal-html body.legal-page {
  height: auto;
  min-height: 100dvh;
  overflow: auto;
}

body.legal-page {
  background: var(--cream-base);
  color: var(--text-dark);
}

.legal-content {
  max-width: 720px;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
  line-height: 1.6;
  color: var(--text-dark);
}

.legal-content h1 {
  /* M12 rollout — Cormorant 600 + Aurora gradient. Center aligned + smaller
     size 2026-05-14 (Petřin "nadpis stránky uprostřed, ne tak velký, není
     to název aplikace") — Privacy/Terms je section title, ne brand. */
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.8rem;
  letter-spacing: 0.03em;
  line-height: 1.2;
  text-align: center;
  margin: 0.5rem 0 1rem;
  background: linear-gradient(120deg, var(--accent-violet) 0%, var(--accent-turquoise) 50%, var(--accent-copper) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  display: block;
}

.legal-content .legal-meta {
  /* Center the "Last updated" line under the centered h1 */
  text-align: center;
}

.legal-content h2 {
  /* M12 rollout — Cormorant 600 v turquoise (consistent s panel section heads).
     Bumped from 500 → 600 (Petřin pokyn 2026-05-15 „aspoň semibold pro
     čitelnost") — Cormorant 500 na light cream readable, ale na úzkém vidění
     splývalo. Same change applied to .about-section h2 below. */
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: 0.02em;
  margin: 2.2rem 0 0.6rem;
  color: var(--accent-turquoise);
}

.legal-content h3,
.about-section h3 {
  /* M19.X-TRANSLATE (Petřin pokyn 2026-06-12) — subheadings blended into body
     text: they were body-coloured (--text-dark) and only a hair larger. Give
     them the heading teal (one size below the h2 section heads) so the 2.x
     subsections read clearly as headings on all three legal pages. */
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: 0.02em;
  margin: 1.6rem 0 0.4rem;
  color: var(--accent-turquoise);
}

.legal-content p,
.legal-content ul,
.legal-content ol {
  margin-bottom: 0.85rem;
  color: var(--text-dark);
  font-size: 0.95rem;
}

.legal-content ul,
.legal-content ol {
  padding-left: 1.4rem;
}

.legal-content li {
  margin-bottom: 0.35rem;
}

.legal-content strong {
  color: var(--text-dark);
  font-weight: 600;
}

.legal-content a {
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.legal-content a:hover,
.legal-content a:focus-visible {
  color: var(--accent-copper);
}

/* Long URLs shown as link text (GitHub feedback repo) — keep the URL in
   one piece: if it doesn't fit the line, the whole link drops to the next
   line instead of breaking mid-URL (Petřin pokyn 2026-06-07). */
.legal-repo-link {
  display: inline-block;
}

/* Back-to-map links are navigation chrome, not body-copy links — keep the
   admin-back teal pill treatment (no underline, no copper hover flip). */
.legal-content a.legal-back,
.legal-content .legal-back--bottom a {
  text-decoration: none;
}
.legal-content a.legal-back:hover,
.legal-content a.legal-back:focus-visible,
.legal-content .legal-back--bottom a:hover,
.legal-content .legal-back--bottom a:focus-visible {
  color: var(--accent-turquoise);
}

.legal-content code {
  background: var(--cream-sunken);
  color: var(--text-dark);
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  border: 1px solid var(--border-on-light);
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.9em;
}

.legal-back {
  /* M-UX-AUDIT-040 per P-036 (mobile): 44px tap target for back link.
     M10 closure Session #7 admin polish (2026-05-16) — Petřin canonical
     spec „na samostatných stránkách je Back to Map vždycky VLEVO" —
     reverted to inline-flex at start of centered .legal-content 720px
     container. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.8rem 0.5rem 0.8rem;
  margin-bottom: 1.2rem;
  margin-left: -0.8rem; /* optical left-align: text edge stays on the container line */
  /* Teal back-to-map — same pattern as .admin-header__back (Petřin pokyn
     2026-06-07): teal + semibold, hover = turquoise tint pill. */
  color: var(--accent-turquoise);
  font-weight: 600;
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: background 160ms ease;
  font-size: 0.9rem;
}

@media (hover: hover) and (pointer: fine) {
  .legal-back:hover {
    background: var(--accent-turquoise-tint);
  }
}
.legal-back:focus-visible {
  background: var(--accent-turquoise-tint);
  outline: none;
}

/* UX-AUDIT-004 (M10 closure audit 2026-05-15): duplicate Back-to-map
   link on legal page bottoms so long-scroll users don't have to scroll
   all the way up to navigate out. Centered + spaced from the body copy. */
.legal-back--bottom {
  display: block;
  margin: 2.4rem 0 0;
  text-align: center;
  font-size: 0.95rem;
}
.legal-back--bottom a {
  /* Same teal treatment as the top .legal-back (admin-header__back parity). */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.8rem;
  color: var(--accent-turquoise);
  font-weight: 600;
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: background 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .legal-back--bottom a:hover {
    background: var(--accent-turquoise-tint);
  }
}
.legal-back--bottom a:focus-visible {
  background: var(--accent-turquoise-tint);
  outline: none;
}
.legal-back--bottom a:focus-visible {
  color: var(--accent-turquoise);
}

.legal-meta {
  margin-top: 0.4rem;
  margin-bottom: 1.4rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.legal-draft-banner {
  background: rgba(184, 134, 45, 0.10);
  border: 1px solid rgba(184, 134, 45, 0.35);
  color: var(--warning);
  padding: 0.75rem 1rem;
  border-radius: var(--radius-md);
  font-size: 0.85rem;
  margin-bottom: 1.5rem;
}

/* In-flow emphasis paragraph used in legal pages to flag important
   anonymity/visibility caveats (Privacy Policy §2.1). Warm warning
   tone consistent with the draft banner. */
.legal-warning {
  color: var(--warning);
  font-weight: 600;
  margin-top: 0.6rem;
  margin-bottom: 0.4rem;
}

/* ==========================================================================
   About page (about.html) — landing-page-lite layout on legal-page base.
   Reuses .legal-back, .legal-meta, .legal-draft-banner from privacy/terms.
   Skeleton stage — final wording & visual polish in M14.
   ========================================================================== */

body.about-page {
  /* Subtle atmospheric mesh across the whole page — painted feel, 4-corner
     washes per M12 Frost atmosphere. PERF-002/011 (Stage 1 review):
     `background-attachment: fixed` removed — iOS Safari paint cliff
     (WebKit #155198) caused white-flash on fast scroll on long-scroll
     pages. Scroll-attached mesh paints once, scrolls with content; the
     atmospheric feel is identical for the user but GPU-cheap. */
  background:
    radial-gradient(ellipse 55% 45% at 0% 0%,
      var(--frost-wash-teal) 0%, transparent 60%),
    radial-gradient(ellipse 55% 45% at 100% 0%,
      var(--frost-wash-plum) 0%, transparent 60%),
    radial-gradient(ellipse 55% 45% at 0% 100%,
      var(--frost-wash-copper) 0%, transparent 60%),
    radial-gradient(ellipse 55% 45% at 100% 100%,
      var(--frost-wash-plum) 0%, transparent 60%),
    var(--cream-base);
}

.about-content {
  max-width: 840px;
  margin: 0 auto;
  padding: 2rem 1.25rem 5rem;
  line-height: 1.7;
  color: var(--text-dark);
}

/* Hero — bigger Cormorant headline + italic tagline. Generous space below. */
.about-hero {
  text-align: center;
  padding: 1.5rem 0 1rem;
}

.about-hero h1 {
  /* Aurora gradient — same DNA as privacy/terms h1, but bigger + brand-scale.
     about.html is the brand landing tone, not section title. */
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.2rem, 5vw, 3.2rem);
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0 0 0.85rem;
  background: linear-gradient(120deg,
    var(--accent-violet) 0%,
    var(--accent-turquoise) 50%,
    var(--accent-copper) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.about-hero__tagline {
  /* Bumped 300 → 600 (Petřin pokyn 2026-05-15 „aspoň semibold pro čitelnost").
     Barva zachována na text-muted (Petřin postřeh „fialová byla dobrá, ta
     navy byla mimo design") — gray-blue na warm cream čte oko jako lehce
     fialově-tlumenou, fits Frisson atmospheric paletě lépe než navy. */
  font-family: var(--font-display);
  font-weight: 600;
  font-style: italic;
  font-size: clamp(1.05rem, 2.2vw, 1.35rem);
  letter-spacing: 0.015em;
  color: var(--text-muted);
  margin: 0;
}

/* Sections — generous vertical rhythm, no hard borders, atmospheric. */
.about-section {
  margin: 2.6rem 0;
}

.about-section h2 {
  /* Cormorant 600 turquoise — same as legal-content h2 (bumped 500 → 600
     per Petřin pokyn 2026-05-15 „aspoň semibold pro čitelnost"). */
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: 0.02em;
  color: var(--accent-turquoise);
  margin: 0 0 0.75rem;
}

.about-section p {
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text-dark);
  margin: 0 0 0.85rem;
  /* Full container width — aligns the text edge with the pillar-card row
     (Petřin pokyn 2026-06-07; the old 64ch measure ended mid-row). */
}

.about-section a {
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

.about-section a:hover,
.about-section a:focus-visible {
  color: var(--accent-copper);
}

.about-section code {
  background: var(--cream-sunken);
  color: var(--text-dark);
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  border: 1px solid var(--border-on-light);
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.92em;
}

/* 3-pillar grid — desktop 3 cols, mobile stack.
   M16 legal-final: cards rebuilt per designer handoff
   docs/handoff/frisson-about-handoff/about-how-it-works.html —
   duotone medallion icons, numbered steps, accent kickers. */
.about-pillars {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  margin-top: 1.6rem;
}

@media (min-width: 861px) {
  .about-pillars {
    grid-template-columns: repeat(3, 1fr);
  }
}

.about-pillar {
  position: relative;
  overflow: hidden;
  padding: 30px 28px;
  border-radius: var(--radius-lg);
  background: linear-gradient(168deg, var(--cream-lifted), var(--cream-surface) 70%);
  border: 1px solid var(--border-on-light);
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.about-pillar::after {
  /* Accent aura per card — colour set by the --aura custom prop below. */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background: radial-gradient(120% 80% at 0% 0%, var(--aura), transparent 55%);
}

@media (hover: hover) and (pointer: fine) {
  .about-pillar:hover {
    transform: translateY(-4px);
  }
}

/* Per-card accent (handoff: magenta · iris · teal mapped to app tokens —
   Echo = mine, map browsing = violet, concerts = turquoise). */
.about-pillar--echo {
  --accent: var(--accent-mine);
  --aura: rgba(var(--accent-mine-rgb), 0.10);
}

.about-pillar--map {
  --accent: var(--accent-violet);
  --aura: rgba(var(--accent-violet-rgb), 0.12);
}

.about-pillar--concerts {
  --accent: var(--accent-turquoise);
  --aura: rgba(var(--accent-turquoise-rgb), 0.10);
}

/* Step index — top-right "01 / 02 / 03", number in the card accent. */
.about-pillar__step {
  position: absolute;
  top: 22px;
  right: 24px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.about-pillar__step b {
  color: var(--accent);
}

/* Icon medallion — 74px tinted plate, duotone 42px glyph inside. */
.about-pillar__medallion {
  position: relative;
  width: 74px;
  height: 74px;
  border-radius: var(--radius-lg);
  display: grid;
  place-items: center;
  background: linear-gradient(
    150deg,
    color-mix(in oklab, var(--accent) 26%, transparent),
    color-mix(in oklab, var(--accent) 8%, transparent)
  );
  border: 1px solid color-mix(in oklab, var(--accent) 34%, transparent);
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, var(--accent) 30%, transparent),
    0 12px 30px -16px color-mix(in oklab, var(--accent) 70%, transparent);
}

.about-pillar__medallion svg {
  width: 42px;
  height: 42px;
  display: block;
}

.about-pillar__eyebrow {
  margin: 22px 0 0;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-2xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}

.about-pillar__label {
  margin: 7px 0 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-style: italic;
  font-size: var(--fs-xl);
  letter-spacing: -0.005em;
  color: var(--text-dark);
}

/* Two classes so the dim actually wins over `.about-section p` above
   (single class lost on specificity → the body stayed near-white). */
.about-pillar p.about-pillar__desc {
  margin: 11px 0 0;
  font-family: var(--font-body);
  /* Per designer mockup (frisson-about-handoff `.card p`): 13.5px body in
     --moon-muted = our --text-muted, one clear step below the Cormorant
     title. Nearest scale token for 13.5px is --fs-sm (13px). */
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: 1.62;
  text-wrap: pretty;
}

/* Section subtitle under the "How it works" heading (designer handoff). */
.about-section__sub {
  color: var(--text-muted);
}

/* Coda — final album line, sign-off treatment (italic Cormorant, centered,
   subtle top divider, slightly violet/turquoise emphasis on key words). */
.about-section--coda {
  margin-top: 4rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border-on-light);
  text-align: center;
}

.about-section--coda p {
  /* Bumped 400 → 600 (Petřin pokyn 2026-05-15 „aspoň semibold pro čitelnost").
     Italic Cormorant 600 zachová sign-off feel ale je solidně čitelný. */
  max-width: 56ch;
  margin-inline: auto;
  font-family: var(--font-display);
  font-weight: 600;
  font-style: italic;
  font-size: 1.1rem;
  color: var(--text-dark);
}

.about-section--coda em {
  font-style: italic;
  color: var(--accent-violet-bright);
}

.about-section--coda strong {
  font-weight: 600;
  font-style: normal;
  color: var(--accent-turquoise);
}

/* Reduced motion — fixed background can feel heavy on low-power devices;
   detach so the page scrolls plain. No animations to disable here. */
/* Tablet/mobile padding tweaks. */
@media (max-width: 540px) {
  .about-content {
    padding: 1.5rem 1rem 3.5rem;
  }
  .about-hero {
    padding: 1rem 0 0.5rem;
  }
  .about-section {
    margin: 2rem 0;
  }
  .about-section--coda {
    margin-top: 3rem;
    padding-top: 1.5rem;
  }
}

/* Soft variant of dialog-hint — used for the "Sign in / Register for
   higher limits" hint at the bottom of the anonymous pin form. Less
   visual weight than the regular hint so it doesn't compete with the
   primary action. */
.dialog-hint--soft {
  font-size: 0.82rem;
  color: var(--text-muted);
  font-style: italic;
  margin: 0.6rem 0 0;
  line-height: 1.5;
}

.dialog-hint--soft .btn-link {
  font-size: inherit;
  color: var(--accent-turquoise);
}

/* Spam-folder note in the verify / reset-password dialogs — styled exactly
   like .form-error per Petra (2026-06-07): the two alternate in the same
   slot (authUI.js hides the note while an error is shown), so they share
   one look. Compound selector so the generic `.dialog-hint` (defined later
   in this file) doesn't win. */
.dialog-hint.dialog-hint--spam {
  color: var(--danger-text);
  font-size: 0.85rem;
  font-weight: 600;
}

/* The generic `#pin-form button` rule (border + 0.65rem 1.3rem padding)
   inherits a higher specificity than `.btn-link`, so without an explicit
   override the Sign in / Register links inside #pin-form-auth-hint render
   as big bordered buttons and wrap awkwardly across two lines. Reset
   them back to plain text-links here. */
#pin-form .btn-link {
  border: none;
  padding: 0.2rem 0;
  font-weight: 600;
  background: transparent;
  border-radius: 0;
}

@media (hover: hover) and (pointer: fine) {
  #pin-form .btn-link:hover {
    background: transparent;
    border-color: transparent;
    transform: none;
    color: var(--accent-copper);
  }
}

/* Keep "Want to manage your pins later?" + the two link buttons on a
   single visual row when they fit, and wrap as a tight group (with the
   "or" sticking to the surrounding link) when they don't. flex-wrap +
   gap avoids the previous "Sign in <wrap> or <wrap> Sign up" stagger. */
#pin-form-auth-hint {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.4rem;
}

/* Consent checkbox label — primary commitment line + a quieter sub-line for
   the withdrawal note, so the block reads as hierarchy instead of one heavy
   wall of text (M12.X auth review 2026-05-31, Petřin pokyn). Privacy/Terms
   stay reachable via the page footer + the Google consent line above. */
.consent-text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.consent-text__note {
  font-size: 0.8rem;
  color: var(--text-muted);
  line-height: 1.4;
}

/* ==========================================================================
   Pin popup (Leaflet) — avatar + nick + optional comment.
   ========================================================================== */

/* Map popups (echo / concert / POI) must open ABOVE the hero pills
   (.hero-badges, z-index 1000) — a user-opened popup outranks the ambient
   "N new echoes" / chart-leader pills. Leaflet's default popupPane is 700;
   lift it just past the pills, still below the hero panel + dialogs. */
.leaflet-popup-pane {
  z-index: 1001;
}

/* Leaflet's own popup chrome — Frisson Frost atmospheric (M12 rollout 2026-05-14):
   atmospheric mesh wash + multi-color halo + copper top hairline + plum left rim.
   5 barev v ko-existenci na 1 komponentě, halo glow místo solid border. */
.leaflet-popup-content-wrapper {
  /* M12 Noir PR5 ② — per-entity edge tone (Petřin pokyn 2026-05-25). Default
     = iris (moment/pin); concert + landmark wrappers override to teal / gold
     below. The top hairline + left rim (::before/::after) read this so the
     popup's colour signals which entity you're looking at. */
  --popup-edge: var(--accent-violet);
  --popup-edge-rgb: var(--accent-violet-rgb);
  background:
    radial-gradient(at 0% 100%, rgba(var(--popup-edge-rgb), 0.10) 0%, transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-on-light);
  box-shadow: var(--halo-multi);
  position: relative;
  overflow: hidden;
  /* Override Leaflet's default 300px popup width cap. */
  max-width: 520px !important;
}
/* Top hairline — atmospheric "spotlight kiss" in the entity tone. */
.leaflet-popup-content-wrapper::before {
  content: "";
  position: absolute;
  top: 0; left: 14px; right: 14px;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--popup-edge) 50%, transparent);
  opacity: 0.95;
  pointer-events: none;
  z-index: 1;
}
/* Left rim — decorative accent in the entity tone. */
.leaflet-popup-content-wrapper::after {
  content: "";
  position: absolute;
  left: 0; top: 12%; bottom: 12%;
  width: 4px;
  background: linear-gradient(180deg, transparent, var(--popup-edge) 50%, transparent);
  border-radius: 0 2px 2px 0;
  pointer-events: none;
  z-index: 1;
}
/* Same belt-and-braces on the inner content cell. */
.leaflet-popup-content {
  max-width: 100% !important;
}

.leaflet-popup-tip {
  background: var(--cream-surface);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}

.leaflet-popup-content {
  margin: 0.85rem 1rem;
  font-family: inherit;
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--text-dark);
}

.leaflet-container a.leaflet-popup-close-button,
.map-legend__close {
  /* M12 Noir PR5 ② — round close button matching the mockups (Petřin pokyn
     2026-05-25): the <a> IS the circle (no ::before, avoids the stacking/
     centring fight), inset from the corner so the rounded popup edge doesn't
     clip it, light × centred via flex. ~32px (slightly under the 44 tap ideal
     — accepted to match the mockup chip size on this beta surface).
     PR5 ④ — the map legend's × reuses this exact chip (Petřin pokyn
     2026-05-25 "použij standardní tlačítko pro popup"); it's a <button>
     outside .leaflet-container, hence the shared selector here. */
  position: absolute;
  top: 12px;
  right: 12px;
  /* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — above the popup content so
     the × is never covered/blocked by header chips or other elements. */
  z-index: 5;
  width: 32px;
  height: 32px;
  min-width: 0;
  min-height: 0;
  padding: 0;
  display: flex !important;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  /* M12 Noir PR5 ③ (Petřin pokyn 2026-05-25) — LIGHT frosted disc so the
     close chip reads identically on BOTH backdrops: the bright concert hero
     image AND the dark moment popup surface. The previous dark fill
     (rgba(cream-base 10,13,30)) vanished on the dark surface (dark-on-dark),
     leaving only the thin ring → "looked like no background". One global rule,
     so every popup's × is consistent. */
  background: rgba(238, 240, 250, 0.16);
  border: 1px solid rgba(238, 240, 250, 0.45);
  color: var(--text-light);
  font-size: 1.1rem;
  line-height: 1;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  z-index: 2;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}

/* M-UX-AUDIT-025 per P-036 (mobile): Leaflet zoom controls bumped from
   default 26×26 to 44×44 for thumb-zone usability. Visual + glyph stays
   centered via line-height. */
.leaflet-touch .leaflet-bar a,
.leaflet-bar a {
  width: 44px !important;
  height: 44px !important;
  line-height: 44px !important;
  font-size: 1.4rem !important;
}

@media (hover: hover) and (pointer: fine) {
  .leaflet-container a.leaflet-popup-close-button:hover,
  .map-legend__close:hover {
    color: var(--text-light);
    background: rgba(238, 240, 250, 0.28);
    border-color: rgba(238, 240, 250, 0.6);
  }
}

.pin-popup__header {
  /* M12 rollout — bumped gap 0.5 → 0.85rem + align-items flex-start (Petřin
     pokyn 2026-05-14: "username + listened odsadit od avataru, namáčkle"). */
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  font-size: 0.95rem;
}

.pin-popup__avatar {
  width: 28px;
  height: 28px;
  color: var(--accent-turquoise); /* avatars use currentColor for stroke — match brand */
  flex-shrink: 0;
}

.pin-popup__nick {
  /* M12 rollout — Cormorant 700 Bold + violet, 1.1rem. Match feed-list
     title (Petřin pokyn 2026-05-14: "username musí být větší a tučné
     na obou místech — v popup i v záložkách"). */
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: 0.02em;
  line-height: 1.3;
  color: var(--nick-color);
  word-break: break-word;
  overflow-wrap: anywhere;
}

.pin-popup__kotva-name {
  /* Kotva header = primary heading. Petřin pokyn 2026-05-22 evening
     (iter 2): "MyHome má být větší" — bump to 1.75rem so it reads as
     a real nadpis above the nick subtitle. Turquoise stays. */
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.75rem;
  letter-spacing: 0.01em;
  line-height: 1.15;
  color: var(--accent-turquoise);
  word-break: break-word;
  overflow-wrap: anywhere;
  /* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — reserve the top-right zone of
     the close × (32px @ top/right 12px) so the inline TEST/OPEN chips at the end of
     the title don't run UNDER it. Only the title line needs it (× sits at the very
     top); the meta/location lines below clear the × naturally. */
  padding-right: 2.5rem;
}

/* Meta CAPS shared base — POUŽÍVÁ stejný styl pro VŠECHNY meta/time
   labels napříč popup + kotva preview rows + feed-list rows. Petřin pokyn
   2026-05-14: "stejný písmo + size + tracking" = explicitly identical via
   shared selector. Color override per use case (.pin-popup__meta = teal
   primary location signal; .pin-popup__preview-when + .feed-list__meta =
   gray secondary metadata). */
.pin-popup__meta,
.pin-popup__preview-when,
.feed-list__meta {
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}

/* Relative "x ago" timestamps (feed Echo rows + gathering preview dates)
   render in NORMAL case, YouTube-style — overriding the uppercase caps
   inheritance from the shared meta base above (Petřin pokyn 2026-06-03).
   Absolute dates (events, single-Echo detail header) keep the caps look. */
.rel-time {
  text-transform: none;
  letter-spacing: normal;
}

.pin-popup__meta {
  /* Petřin pokyn 2026-05-14: gray + right-align = default for KOTVA
     popup heading (sekundární meta, primary info je v per-row preview).
     Single pin override níž = teal + left-align (primary location signal). */
  display: block;
  align-self: flex-end;
  margin-top: 0.2rem;
  color: var(--text-muted);
  opacity: 0.75;
  text-align: right;
}

/* Single pin popup (= .pin-popup__nick v heading, žádná kotva-name).
   M11.X-CONTAINER-ENTITY (Petřin pokyn 2026-05-24) — PLACE (teal, primary
   location signal) stays on line 1; LISTENED · TIME drops to line 2, right-
   aligned + muted gray so it no longer blends into the place. Previously both
   sat on one flex row in the same teal and read as one mixed blob.
   :has() je supported v Chrome 105+ / Safari 15.4+ / Firefox 121+. */
.pin-popup__heading:has(.pin-popup__nick) .pin-popup__meta {
  display: flex;
  flex-direction: column;
  /* M13.X-ECHO-LIKES (Petřin pokyn 2026-06-01) — the location hugs the @nick
     (drop the 0.2rem margin-top → only the heading's --space-1 gap separates
     them) and the heart row gets MORE air above it (gap 0.2 → 0.4rem). Mirrors
     the echo-detail rhythm where the location sits tight under the nick. */
  margin-top: 0;
  gap: 0.4rem;
  opacity: 1;
  align-self: stretch;
}
.pin-popup__heading:has(.pin-popup__nick) .pin-popup__meta-place {
  display: block;
  width: 100%;
  /* M12.X (Petřin pokyn 2026-05-30) — match the side-list location token
     (`--location-color`, soft gold). The full `--accent-turquoise` read "too teal"
     on a normal Echo; one token across popup + list. */
  color: var(--location-color);
  text-align: left;
}
/* M13.X-ECHO-LIKES — the single Echo's like rides this date line: heart on the
   LEFT, date pushed RIGHT. The heart owns its own colour (muted/magenta), so the
   muted+dimmed treatment moves onto the date sub-span (never the heart). */
.pin-popup__heading:has(.pin-popup__nick) .pin-popup__meta-when {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}
.pin-popup__heading:has(.pin-popup__nick) .pin-popup__meta-when-date {
  margin-left: auto;
  color: var(--text-muted);
  opacity: 0.75; /* match the Pins panel / feed-list gray exactly */
  text-align: right;
}

.pin-popup__by-nick {
  /* Kotva subtitle row — wraps the nick alone (flag + place teď žijí
     v `.pin-popup__location` pod nickem). Petřin pokyn 2026-05-22
     evening (iter 2): "písmo má být stejné jako v záložkách pins" =
     nick si bere base `.pin-popup__nick` styling (Cormorant 700 1.1rem
     violet, mixed case), takže tento wrapper jen drží pozici v
     flex-column heading bez vlastních text overrideů. */
  display: block;
  text-transform: none;
}

/* Location row pod nickem — flag + PRAGUE pill inline (Petřin pokyn
   2026-05-22 evening iter 2: "Lokalita i s vlajkou má být pod jménem").
   Vlajka i place sdílí jeden řádek; CAPS look se zachoval na `.pin-popup__place`. */
.pin-popup__location {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.2rem;
}

/* Flag inside `.pin-popup__location` shouldn't carry the `margin-left:
   0.4rem` it gets in base `.pin-popup__nationality` (designed pro inline
   after nick). Location row už má `gap`, takže margin by zdvojil mezeru. */
.pin-popup__location .pin-popup__nationality {
  margin-left: 0;
}

/* M13.X (Petřin pokyn 2026-06-01) — place flag LEADS the location text on the
   standalone Echo's meta line. Base `.pin-popup__nationality` uses margin-LEFT
   (it sits AFTER the nick); here it precedes the text, so flip to margin-right. */
.pin-popup__meta-place .pin-popup__nationality {
  margin-left: 0;
  margin-right: 0.3rem;
}

/* L3 location pill — small CAPS. M12 Noir PR5 ③ (mockup 19): muted (tertiary)
   so it recedes below the L1 name + L2 author in the 3-tier hierarchy (was
   teal "primary location signal" — Frost era). */
.pin-popup__place {
  font-family: var(--font-body);
  /* M12.X (Petřin pokyn 2026-05-31) — match the NORMAL ECHO location size exactly
     (`.pin-popup__meta-place` = the shared `.pin-popup__meta` base, 0.7rem). The
     gathering location read BIGGER (0.75rem) than a normal Echo / the list. */
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  /* M12.X (Petřin pokyn 2026-05-30) — the location name uses the SAME token as
     the location in the side list (`.feed-list__item--pin .feed-list__where`):
     `--location-color` (soft gold), so popup + list read as one system. */
  color: var(--location-color);
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}

/* ADR-192 — bare avatar orb on Noir popup surfaces. Cream plate +
   copper ring removed; per-avatar glow is applied by the shared
   "Avatar unification" rule (search ADR-192 in this file). */
.pin-popup__avatar {
  border-radius: 50%;
}

/* Optional nationality flag inside the popup nick line — tiny + subtle,
   matches the sidebar pattern. Per M10D Task #12 (Petřin pokyn 2026-05-14). */
.pin-popup__nationality {
  width: 14px;
  height: 10px;
  margin-left: 0.4rem;
  object-fit: cover;
  border-radius: 1px;
  vertical-align: middle;
  display: inline-block;
}

/* `.flag--anon` dim removed (Petřin pokyn 2026-05-25, reverses ADR-070
   Rozhodnutí 2): the 60 % opacity on anonymous self-declared flags was
   never wanted — on the dark Noir surface it read as a grayed-out / dead
   flag instead of a soft "unverified" cue. Anon nationality flags now
   render at full opacity, identical to registered ones. The honest signal
   survives via the `title="Self-declared, anonymous"` tooltip set in JS
   (pin popup + Moments feed), which doesn't alter the flag's appearance. */

/* `.pin-popup__nick-anon-suffix` + `.feed-list__nick-anon-suffix`
   removed per Petřin pokyn 2026-05-21 — the visible "(self-declared)"
   suffix was UI clutter; the flag's `title=` tooltip stays as the only
   on-hover disclosure surface. ADR-070 opt-in storage + flow preserved;
   the 60 %-opacity dim was later removed (see above). */

.pin-popup__comment {
  margin-top: 0.4rem;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--text-dark);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ==========================================================================
   Auth — header buttons + dialog forms (functional baseline; the design
   pass with Anthropic Design will refine these later in MVP).
   ========================================================================== */

.auth-header {
  /* Lives inside the .top-right-cluster (Add Pin + auth). */
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* ============================================================
   Hero strip (M10D scope per ADR-048 D.5)
   ============================================================
   Always-visible identity row at the top, full-width. Logo + product
   name + tagline + Online live badge cluster. The strip floats over
   the map (the map still fills 100dvh; the strip sits on top with a
   semi-translucent atmospheric background).

   Final visual polish (logo asset, font swap, brand colors fine-tuning)
   = M12. M10D delivers the structural foundation.
*/
/* Hero brand block — map-first redesign 2026-05-14 per Petřin pokyn:
   "informace o mapě bude uprostřed a bude mnohem větší ... stylizovaný
   nadpis ... Backend is alive a Beta bude pod tím". Vertical column
   floating top-center. M12 polish bude doladit typografii (custom font,
   logotype, case treatment); this is the pre-launch foundation. */
/* Hero floating panel — cinematic cream-blur card top-center.
   Single source of truth (cleaned 2026-05-14 — the previous file had
   3 conflicting .hero-strip__title definitions because a parallel agent
   started a `.top-rail__*` layout refactor that was never finished). */
.hero-strip {
  position: absolute;
  top: max(0.9rem, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  z-index: 1000;
  padding: 0.85rem 1.4rem 0.95rem;
  /* M12 rollout — Frisson Frost atmospheric: 4-corner painted vignette
     (Petřin "velvet ribbon + vignette" choice 2026-05-14) + cream-blur. */
  background:
    radial-gradient(ellipse 55% 70% at 0% 0%,
      rgba(var(--accent-turquoise-rgb), 0.18) 0%, rgba(var(--accent-turquoise-rgb), 0.08) 25%, transparent 50%),
    radial-gradient(ellipse 55% 70% at 100% 0%,
      rgba(var(--accent-violet-rgb), 0.20) 0%, rgba(var(--accent-violet-rgb), 0.10) 25%, transparent 50%),
    radial-gradient(ellipse 55% 70% at 0% 100%,
      rgba(var(--accent-copper-rgb), 0.18) 0%, rgba(var(--accent-copper-rgb), 0.08) 25%, transparent 50%),
    radial-gradient(ellipse 55% 70% at 100% 100%,
      rgba(28, 37, 50, 0.22) 0%, rgba(28, 37, 50, 0.10) 25%, transparent 50%),
    rgba(var(--cream-surface-rgb), 0.94);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.10);
  border-radius: 18px;
  box-shadow:
    inset 0 0 60px rgba(28, 37, 50, 0.08),
    var(--halo-multi);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  /* M13.X-HERO-TAGLINE 2026-06-04 (Petřin výběr varianty C nad renderem):
     the title is mathematically centred via a counterweight grid — the
     left 1fr phantom track mirrors the badges track width, BETA keeps its
     exact 0.9rem gap as a satellite, and the full-width centred tagline
     therefore shares the title's axis (measured Δ 0.0px). Survives BETA
     removal (phantom track collapses with the badges). Supersedes the
     2026-05-14 panel-centred tagline; logo slot tried in M10E and pulled
     2026-05-15 — final brand mark = M12 work. */
  grid-template-areas:
    ".       title   badges"
    "tagline tagline tagline";
  column-gap: 0.9rem;
  row-gap: 0.55rem;
  align-items: center;
  max-width: calc(100vw - 1.5rem);
  pointer-events: auto;
}

.hero-strip__title {
  /* M12 rollout — Cormorant Garamond 700 Bold + violet-shifted Aurora gradient.
     Po Petřin "tučnější písmo + víc fialové v gradientu kombinace". */
  grid-area: title;
  justify-self: center; /* M13.X-HERO-TAGLINE — centred between counterweight tracks */
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.1;
  color: var(--text-dark);
}

.hero-strip__title-text {
  /* Violet-shifted Aurora — fialová zabírá ~40% místo ~5% (Petřin pokyn) */
  background: linear-gradient(
    120deg,
    var(--accent-violet) 0%,
    var(--accent-violet-bright) 30%,
    var(--accent-turquoise) 65%,
    var(--accent-copper) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.hero-strip__tagline {
  /* Reverted 2026-05-15 19:30 (Petřin postřeh „jak je je menší a písmo
     taky, to bylo v pořádku jak bylo"): Cormorant italic 600 → Manrope
     inherited italic 400. Cormorant optically čte menší díky x-height
     rozdílu vs Manrope při stejných 0.92rem. Brand DNA cross-page
     consistency s about hero TBD v M12 visual sweep. */
  grid-area: tagline;
  margin: 0;
  font-size: 0.92rem;
  font-style: italic;
  font-weight: 400;
  color: var(--text-muted);
  line-height: 1.3;
  text-align: center;
  letter-spacing: 0.02em;
}

/* Hero tabs row — mobile-only per ADR-085 Y variant. Default hidden on
   desktop (events panel side rail handles Pins/Events tabs there).
   Mobile reveals tabs as compact pill switcher integrated into hero strip. */
.hero-strip__tabs {
  display: none;
}

.hero-tab {
  appearance: none;
  background: transparent;
  border: none;
  font-family: var(--font-body, "Manrope", system-ui, sans-serif);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-muted);
  cursor: pointer;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease;
  min-height: 28px;
  white-space: nowrap;
}
.hero-tab.is-active {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  box-shadow:
    0 0 10px rgba(var(--accent-turquoise-rgb), 0.3),
    inset 0 1px 0 rgba(238, 240, 250, 0.18);
}
.hero-tab:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
@media (hover: hover) and (pointer: fine) {
  .hero-tab:not(.is-active):hover {
    color: var(--text-dark);
    background: rgba(var(--accent-turquoise-rgb), 0.06);
  }
}
/* M11.X-NAVPIVOT S1 AI-008 (M-UX-NAVPIVOT-001) per P-058 (mobile):
   primary navigation tabs (mobile pill bar children = hot-path
   navigation) MUSÍ mít effective tap target ≥ 44×44. Visual style
   zůstává kompaktní (font 0.62rem), hit area expanded via padding.
   Apple HIG + parity s .songs-pebble (44×44) + .menu-toggle (44×44). */
@media (max-width: 767px) {
  .mobile-pill-bar .hero-tab {
    min-height: 40px;
    padding: 8px 14px;
  }
}

@media (max-width: 767px) {
  /* Mobile hero — Petřin pokyn 2026-05-16: "hero vrať jak bylo, zmenši
     beta" + 2026-05-16 večer „dole je příliš prostoru, mohlo by být
     stejně místa jako nahoře". Single-row grid (tagline hidden + row gap
     odstraněn), symetrický padding top/bottom. */
  .hero-strip {
    /* Symmetric vertical padding — top = bottom */
    padding: 0.5rem 0.95rem;
    border-radius: 14px;
    /* Collapse to single row (desktop 2-row grid had ghost tagline row
       eating bottom space even when display:none). Columns reset to two —
       desktop's M13.X-HERO-TAGLINE counterweight track doesn't apply here.
       (Legacy block: mobile.css hides #hero-strip entirely since M12D-3.) */
    grid-template-columns: 1fr auto;
    grid-template-areas: "title badges";
    grid-template-rows: auto;
    row-gap: 0;
    align-items: center;
  }
  .hero-strip__title {
    font-size: 1.4rem;
    white-space: nowrap;
    line-height: 1; /* drop default 1.1 line-height = extra vertical fat */
  }
  .hero-strip__title-text {
    white-space: nowrap;
  }
  .hero-strip__tagline {
    display: none;
  }
}

/* ============================================================
   Mobile floating pill bar — Pins / Events tabs DOLE mezi FABs.
   Per ADR-085 revised 2026-05-16: Y variant *inspirace* (pill
   tabs styling), ALE tlačítka dole místo v hero stripu.
   Click = open bottom sheet to that tab. Hidden on desktop.
   ============================================================ */
.mobile-pill-bar {
  display: none; /* default hidden; revealed only on mobile */
}

@media (max-width: 767px) {
  .mobile-pill-bar {
    position: absolute;
    /* Same bottom anchor as FABs (0.6rem + safe-area) so pill bar sits
       vertically aligned s Add Pin = unified bottom chrome row. */
    bottom: max(1.5rem, calc(env(safe-area-inset-bottom) + 1.1rem));
    left: 50%;
    transform: translateX(-50%);
    z-index: 1010; /* above map (1000), below modals */
    display: inline-flex;
    gap: 2px;
    padding: 4px;
    border-radius: var(--radius-pill);
    /* Frost glass capsule — same DNA as hero strip recipe */
    background:
      radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 50%),
      radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 50%),
      rgba(var(--cream-surface-rgb), 0.85);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border: 1px solid rgba(var(--accent-turquoise-rgb), 0.12);
    box-shadow:
      0 0 16px rgba(var(--accent-turquoise-rgb), 0.18),
      0 6px 18px rgba(0, 0, 0, 0.10),
      inset 0 1px 0 rgba(238, 240, 250, 0.55);
  }

  /* Leaflet attribution na mobile — minimálně intrusive (Petřin pokyn
     2026-05-16: „ruší" + „Leaflet OpenStreetMap"). Plně skrýt NELZE
     per CartoDB Voyager TOS + OSM ODbL license — atribuce dat je
     smluvní povinnost. Decentně tlumeno: tiny font, transparentní bg,
     text-muted color, blend do mapy. */
  .leaflet-control-attribution {
    font-size: 0.55rem !important;
    padding: 1px 6px !important;
    background: rgba(var(--cream-surface-rgb), 0.55) !important;
    color: var(--text-muted) !important;
    border-radius: var(--radius-sm) 0 0 0 !important;
    margin: 0 !important;
  }
  .leaflet-control-attribution a {
    color: var(--text-muted) !important;
  }
}

/* Screen-reader-only utility: removed from visual flow but available
   to assistive tech + JS hooks. Used by #status-banner which the JS
   keeps in DOM for /health polling but is no longer shown to users
   (the maintenance overlay covers the failure UX). */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ----------------------------------------------------------------
   Toast notifier (uiUtils.js showToast / withOptimistic revert path).
   Bottom-centre stack of short-lived messages; click to dismiss early.
   Variants follow design-system semantics — danger red for errors,
   turquoise for info, muted cream for success.
   ---------------------------------------------------------------- */
.toast-stack {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 1.25rem);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column-reverse;
  gap: 0.5rem;
  z-index: 4000;       /* above modals (1500-3000 range), below maintenance overlay */
  pointer-events: none; /* container is non-interactive; toasts re-enable below */
  max-width: min(92vw, 32rem);
}
/* The stack runs as a manual popover so toasts paint above <dialog> top-layer
   surfaces (ADR-244 nudge over the give-stars dialog). Neutralise the UA
   [popover] box (inset:0, margin:auto, border, padding, canvas background) —
   the bottom-centre positioning above stays authoritative. */
.toast-stack[popover] {
  top: auto;
  right: auto;
  margin: 0;
  border: 0;
  padding: 0;
  width: auto;
  height: auto;
  background: transparent;
  overflow: visible;
}
.toast {
  pointer-events: auto;
  background: var(--cream-surface);
  color: var(--text-dark);
  font-family: var(--font-body, "Manrope", system-ui, sans-serif);
  font-size: 0.9rem;
  line-height: 1.35;
  padding: 0.6rem 0.95rem;
  border-radius: var(--radius-md);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.22);
  box-shadow: var(--shadow-dropdown);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 180ms ease, transform 180ms ease;
  cursor: pointer;
}
.toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.toast--error {
  background: var(--danger);
  color: var(--text-light);
  border-color: transparent;
}
.toast--success {
  border-color: rgba(var(--accent-turquoise-rgb), 0.45);
}
@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity 60ms linear; transform: none; }
  .toast.is-visible { transform: none; }
}

/* Shared × close button injected by modalUtils.installCloseButtonOnAllDialogs.
   UX-AUDIT-010 + UX-AUDIT-015 (M10 closure audit 2026-05-15) — appears
   automatically on every <dialog> that doesn't already have its own
   close markup. Sized 44×44 px per M-UX-003 tap target rule. */
/* Close × = SHARED popup chip (Petřin pokyn 2026-05-31 "použij stejný pattern
   jako u popupů"). Modal close býval 48×48 / glyph 2.4rem / tmavý disc — četl
   se výrazně těžší než popup × a navíc auto-focus po showModal() rozsvěcel
   teal outline ring. Sjednoceno na PŘESNÝ vzhled .leaflet-popup-close-button:
   32×32 kruh, světlý frosted disc, glyph 1.1rem, hover/focus = jen zesvětlení
   (žádný teal ring). Jeden close-button pattern napříč popupy + modaly.
   Popup × (.leaflet-popup-close-button) řídí blok výše; tady ho zrcadlíme pro
   modální dialogy. Events panel close (.events-panel__close) má vlastní rule níž. */
.dialog-close,
.event-detail-modal__close,
.public-profile-modal__close,
.pin-expand-overlay__close,
.delete-kotva-modal__close,
.online-event-detail-dialog__close,
#pin-form .pin-form__close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  appearance: none;
  background: rgba(238, 240, 250, 0.16);
  border: 1px solid rgba(238, 240, 250, 0.45);
  cursor: pointer;
  width: 32px;
  height: 32px;
  min-width: 0;
  min-height: 0;
  padding: 0;
  border-radius: 50%;
  color: var(--text-light);
  font-size: 1.1rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  transition: background 150ms ease, color 150ms ease,
              border-color 150ms ease;
  z-index: 2;
}
@media (hover: hover) and (pointer: fine) {
  .dialog-close:hover,
  .event-detail-modal__close:hover,
  .public-profile-modal__close:hover,
  .pin-expand-overlay__close:hover,
  .delete-kotva-modal__close:hover,
  .online-event-detail-dialog__close:hover,
  .rating-dialog__close:hover,
  #pin-form .pin-form__close:hover {
    background: rgba(238, 240, 250, 0.28);
    color: var(--text-light);
    border-color: rgba(238, 240, 250, 0.6);
    transform: none;
  }
}
/* Focus = same gentle lighten as hover. NO teal outline ring — the popup ×
   has none, and showModal() auto-focuses the close button, so a teal ring
   was flashing on every modal open (Petřin postřeh 2026-05-31). */
.dialog-close:focus-visible,
.event-detail-modal__close:focus-visible,
.public-profile-modal__close:focus-visible,
.pin-expand-overlay__close:focus-visible,
.delete-kotva-modal__close:focus-visible,
.online-event-detail-dialog__close:focus-visible,
.rating-dialog__close:focus-visible,
#pin-form .pin-form__close:focus-visible {
  color: var(--text-light);
  background: rgba(238, 240, 250, 0.28);
  border-color: rgba(238, 240, 250, 0.6);
  outline: none;
}

/* Confirm dialog (UX-AUDIT-002 + new P-033) — Frost-styled replacement
   for native window.confirm(). Sized for the short title + 1-2 line
   body + 2 buttons; the form-actions row reuses the same layout as
   other Frost dialogs so spacing matches. Backdrop dismiss = cancel. */
.confirm-dialog {
  max-width: min(92vw, 26rem);
  padding: 1.5rem 1.4rem 1.2rem;
  /* Robustness (M12D mobile audit 2026-05-30) — a centred native <dialog>
     does NOT scroll its own overflow. A tall confirm card (mod-delete =
     5 reason radios + note + 2 stacked buttons) pushed its Delete/Cancel
     buttons below the fold on a short viewport (phone landscape / keyboard
     open) where they became unreachable. Mirror the .frost-modal cap so the
     whole card scrolls instead of clipping. */
  max-height: 90dvh;
  overflow-y: auto;
  /* M12 PR6 Dávka D — canonical dialog shell (ADR-179): teal hairline 0.15,
     cream-surface + 2-corner atmospheric mesh, atmospheric halo shadow.
     Was a flat cream-base box with a 0.18 border + dropdown shadow — off the
     locked shell. Drives confirmDialog / promptReasonDialog / modDeleteReason. */
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}
.confirm-dialog__form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.confirm-dialog__title {
  /* Quick confirm/reason prompt — small tier (M13.X-TITLE-TIERS). Covers the
     generic confirmDialog, promptReasonDialog, and mod-delete-reason dialog. */
  font-family: var(--font-display, "Cormorant Garamond", serif);
  font-size: var(--fs-dialog-title);
  font-style: italic;
  font-weight: 700;
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  color: var(--text-dark);
}
.confirm-dialog__body {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text-muted);
}
/* Author / nick inside a confirm body — soft-violet user-identity token, the
   same treatment as .pin-form__gathering-author / .pin-popup__by-nick so the
   actor reads as an identifier, not body copy (Petřin pokyn 2026-05-31). */
.confirm-dialog__author {
  color: var(--nick-color);
  font-weight: var(--fw-semibold);
}
.confirm-dialog .form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.55rem;
  margin-top: 0.25rem;
}

/* M11.X-DELETED-PINS V2.1 (2026-05-23) — moderator delete-reason
   dialog. Shares Frost confirm-dialog frame + adds a radio group
   styled like the reporter form (= same field--radio rhythm, plus
   slightly tighter spacing because there are 5 options in a tight
   modal). Per Petřin pokyn 2026-05-23: moderation/delete must use
   our design system end-to-end. */
.mod-delete-reason-dialog {
  max-width: min(94vw, 30rem);
}
.mod-delete-reason-dialog__reasons {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  border: 0;
  padding: 0;
  margin: 0;
}
.mod-delete-reason-dialog__legend {
  margin-bottom: 0.4rem;
  padding: 0;
}
.mod-delete-reason-dialog__option {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.35rem 0.5rem;
  border-radius: 8px;
  transition: background 140ms ease;
  cursor: pointer;
}
.mod-delete-reason-dialog__option:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.06);
}
.mod-delete-reason-dialog__option input[type="radio"] {
  accent-color: var(--danger);
}
.mod-delete-reason-dialog__option span {
  font-size: 0.92rem;
  color: var(--text-dark);
}
.mod-delete-reason-dialog__note-label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.mod-delete-reason-dialog__note-label-text {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* M11B Stage M follow-up 2026-05-22 evening (Petřin pokyn) — Frost
   textarea + backdrop styling for the moderator delete-pin reason
   prompt. Previously the textarea inherited UA defaults (= bare black
   border, no rounding, mismatched font) which broke the Frost mood. */
.confirm-dialog::backdrop {
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.confirm-dialog textarea {
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  width: 100%;
  resize: vertical;
  min-height: 4.5rem;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}

.confirm-dialog textarea:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

.confirm-dialog .form-error {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--danger-text);
  line-height: 1.4;
}

/* Hero satellite badges — Petřin pokyn 2026-06-06: the day-of pills moved
   OUT of the hero counterweight grid (the mirrored phantom track stretched
   the panel whenever a badge appeared). They now hang as a centred satellite
   row BELOW the hero panel. Desktop top reuses the --hero-strip-bottom
   variable measured by eventLiveIndicator.js; mobile re-anchors the row
   under the bridge bar (mobile.css). */
.hero-badges {
  position: absolute;
  z-index: 1000;
  left: 50%;
  transform: translateX(-50%);
  top: calc(var(--hero-strip-bottom, 7.5rem) + 0.55rem);
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  /* ADR-295 — up to FOUR pills can co-exist (leader + online + concert +
     new echoes); a single non-wrapping row overflowed the viewport. Desktop
     has room for one row (Petřin pokyn 2026-06-10 — nezalamovat), so the
     only cap is the viewport itself; wrap stays as the safety net for
     narrow windows. Mobile re-caps in mobile.css (two centred rows). */
  flex-wrap: wrap;
  justify-content: center;
  max-width: calc(100vw - 1.5rem);
  /* The row itself must never block map interaction — only the pills do. */
  pointer-events: none;
}
.hero-badges > * {
  pointer-events: auto;
}

/* ADR-295/297 — row order (Petřin pokyn 2026-06-10): new echoes FIRST,
   chart leader second; the online/concert day-of pair is TIME-ordered by
   eventLiveIndicator.js (inline order 3/4 — earlier start first, a
   timeless event goes after the timed one). The CSS values below are the
   defaults when JS hasn't decided yet. */
.map-news-badge { order: 1; }
.chart-leader-badge { order: 2; }
#online-live-badge { order: 3; }
#concert-today-badge { order: 4; }
/* M18.X-MAP-BACK — the return pill closes the row (navigation utility,
   not a content signal; most of the time it is the only pill visible). */
.map-back-badge { order: 5; }

/* M18.X-UX-QUICKWINS (ADR-295) — soft entrances for the satellite pills.
   The `hidden` flip stays the show/hide mechanism (JS untouched); the
   animation simply plays whenever a pill enters rendering. appMenuPop's
   calm fade+rise, scaled down to pill size. */
@keyframes pill-soft-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.hero-badges > :not([hidden]) {
  animation: pill-soft-in 200ms ease;
}
/* Soft exit — mapNews.js adds .is-leaving, waits ~200 ms, then flips
   `hidden` (dismiss / queue exhausted). */
.hero-badges > .is-leaving {
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 200ms ease, transform 200ms ease;
  pointer-events: none;
}
/* Count "tick" — re-applied by JS when the number on a visible pill
   changes (echo-like-pop's overshoot curve, smaller excursion). */
@keyframes pill-count-tick {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}
.online-live-badge__count.is-ticking {
  display: inline-block;
  animation: pill-count-tick 220ms cubic-bezier(0.2, 0.9, 0.3, 1.5);
}
@media (prefers-reduced-motion: reduce) {
  .hero-badges > :not([hidden]),
  .online-live-badge__count.is-ticking {
    animation: none;
  }
  .hero-badges > .is-leaving {
    transition: none;
  }
}

/* Popup yield — the map is one sealed stacking context (Leaflet's transformed
   map-pane), so an open popup's z-index can NEVER out-rank these pills via CSS
   alone: the pills always paint above the whole map block. When a popup opens
   HIGH enough to physically overlap the pills it reads as "popup under pill"
   (Petřin nález 2026-06-10). map.js measures both rects on popupopen / moveend
   and toggles .is-yielding ONLY while they actually intersect — a popup that
   opens low or off to the side leaves the pills visible. */
.hero-badges {
  transition: opacity 220ms ease;
}
.hero-badges.is-yielding {
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .hero-badges {
    transition: none;
  }
}

/* (Removed 2026-06-06, Petřin pokyn: body.has-live-banner offsets — the
   persistent LIVE banner is gone; the satellite pill + mint tab dot carry
   the live signal.) */

/* Online live badge — noir glass pill (designer round 2026-06-06; supersedes
   the ADR-108 orange/green wash). The pill body stays dark glass — same
   chrome family as the hero panel — so it never collides with the solid-gold
   TEST MODE pill and keeps contrast on the light map. State is carried by
   the DOT + text tint:
     • .is-today → gold dot + "N online today" (day-of, not yet live)
     • .is-live  → mint pulsing dot + "N online" (in progress now)
   Click = open Online tab → LIVE sub-section. */
.online-live-badge {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.34rem 0.72rem;
  border-radius: var(--radius-pill);
  background: var(--glass-strong);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border-on-light);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  white-space: nowrap;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.30),
              inset 0 1px 0 rgba(238, 240, 250, 0.06);
  transition: transform 200ms ease, box-shadow 200ms ease,
              color 200ms ease, border-color 200ms ease;
}

/* TODAY (day-of, not yet live) — gold dot + tint (--accent-copper family;
   replaces the old out-of-palette hardcoded orange that read as danger). */
.online-live-badge.is-today {
  color: var(--accent-copper-bright);
  border-color: rgba(var(--accent-copper-rgb), 0.40);
}
.online-live-badge.is-today:hover,
.online-live-badge.is-today:focus-visible {
  outline: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36),
              0 0 0 1px rgba(var(--accent-copper-rgb), 0.38);
}
.online-live-badge.is-today .online-live-badge__dot {
  background: var(--accent-copper-bright); /* solid gold dot, no pulse */
  box-shadow: 0 0 6px rgba(var(--accent-copper-rgb), 0.7);
}

/* LIVE (event in progress now) — mint dot + tint with pulse. Live token per
   ui-standards.md sekce 1.1. */
.online-live-badge.is-live {
  color: var(--live);
  /* --live family throughout (M15 review STYLE-M15-001) — the border/glow
     used --success-rgb, which only matches --live by coincidence today;
     a future palette tweak would split the pill's colours. */
  border-color: rgba(var(--live-rgb), 0.40);
}
.online-live-badge.is-live:hover,
.online-live-badge.is-live:focus-visible {
  outline: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36),
              0 0 0 1px rgba(var(--live-rgb), 0.42);
}
.online-live-badge.is-live .online-live-badge__dot {
  background: var(--live); /* solid mint dot */
  box-shadow: 0 0 8px var(--live);
  animation: online-live-badge-pulse 1.4s ease-in-out infinite;
}

/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift. */
@media (hover: hover) and (pointer: fine) {
  .online-live-badge:hover {
    transform: translateY(-1px);
  }
}

.online-live-badge__dot {
  /* Plain CSS circle — color filled via parent state class
     (.is-today → gold, .is-live → mint pulse). */
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: transparent;
}

@keyframes online-live-badge-pulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.86); }
}

/* Respect users with reduced-motion preference — kill the live pulse +
   chevron rotation transition (M10D Stage 2 AI-S2-M10D-006/007). */
@media (prefers-reduced-motion: reduce) {
  .online-live-badge.is-live .online-live-badge__dot,
  .events-section__dot--live {
    animation: none;
  }
  .events-section__past-toggle .events-section__icon,
  .rsvp__btn {
    transition: none;
  }
}

.online-live-badge__count {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* M12.X-TESTMODE-HERO — concert "today" badge. Reuses the noir glass
   .online-live-badge pill and pins the calm green treatment with a SOLID
   dot (no pulse): a timeless concert on its day is a "today" signal, not a
   pulsing live stream. Kept a separate badge so the online gold/green
   semantics stay intact (Petřin pokyn 2026-05-31). */
.concert-today-badge {
  color: var(--live);
  /* --live family throughout (STYLE-M15-001 parity with the online pill) —
     --success-rgb only matched --live by coincidence. */
  border-color: rgba(var(--live-rgb), 0.40);
}
.concert-today-badge:hover,
.concert-today-badge:focus-visible {
  outline: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36),
              0 0 0 1px rgba(var(--live-rgb), 0.42);
}
.concert-today-badge .online-live-badge__dot {
  background: var(--live); /* solid mint dot, no pulse */
  box-shadow: 0 0 6px rgba(var(--live-rgb), 0.7);
}

/* M18-LIVE-MAP — "N new echoes since your last visit" pill. Same noir glass
   pill; mint = the established "live/new" semantic (tab dots, feed reply
   dot). SOLID dot, no pulse — news since last visit is a calm letter from
   the world, not a live stream. Click = fly to the next new echo; the ×
   corner (a span inside the button — click branching in mapNews.js)
   dismisses without flying. */
.map-news-badge {
  color: var(--live);
  border-color: rgba(var(--live-rgb), 0.40);
}
/* ADR-295 — the pill became a DIV around two real buttons (nested buttons
   are invalid HTML), so the focus glow rides :focus-within instead of
   :focus-visible. Visuals identical. */
.map-news-badge:hover,
.map-news-badge:focus-within {
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36),
              0 0 0 1px rgba(var(--live-rgb), 0.42);
}
.map-news-badge .online-live-badge__dot {
  background: var(--live);
  box-shadow: 0 0 6px rgba(var(--live-rgb), 0.7);
}
/* Inner fly-to button — invisible chrome; the wrapper div carries the whole
   pill look. Inherits the pill's font/colour; same 0.4rem gap the pill used
   to give the dot/count/label trio. */
.map-news-badge__main {
  appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  outline: none;
}
.map-news-badge__dismiss {
  appearance: none;
  background: none;
  font-family: inherit;
  color: inherit;
  cursor: pointer;
  outline: none;
  border: 0;
  border-left: 1px solid rgba(var(--live-rgb), 0.25);
  /* Bigger tap target without moving a pixel: padding grows the hit area,
     negative margins give the space back to the layout. */
  margin: -0.4rem -0.5rem -0.4rem 0.15rem;
  padding: 0.4rem 0.5rem 0.4rem 0.4rem;
  font-size: 0.9rem;
  line-height: 1;
  opacity: 0.65;
  transition: opacity 150ms ease;
}
.map-news-badge:hover .map-news-badge__dismiss,
.map-news-badge__dismiss:hover,
.map-news-badge__dismiss:focus-visible {
  opacity: 1;
}
/* The × glyph rides a span so it can grow PAINT-ONLY (Petřin pokyn 2026-06-11
   — the × was tiny). A font-size bump instead would make the dismiss the
   tallest flex item and grow the whole pill (+2.6px measured) out of line
   with its gold sibling; transform keeps layout untouched. */
.map-news-badge__dismiss-glyph {
  display: inline-block;
  transform: scale(1.3);
}

/* M18.X-MAP-BACK — "Back to map" one-tap zoom-out pill (mapBackView.js).
   Same noir glass pill as its row siblings; teal accent = navigation
   utility (the Locate family), and NO status dot — an undo-arrow glyph
   instead. PHONES ONLY (Petřin pokyn 2026-06-10): pinching out is the
   mobile pain; desktop has the wheel + the bottom-left zoom control. */
.map-back-badge {
  display: none;
  color: var(--accent-turquoise);
  border-color: rgba(var(--accent-turquoise-rgb), 0.40);
}
@media (max-width: 767px) {
  .map-back-badge:not([hidden]) {
    display: inline-flex;
  }
}
.map-back-badge:hover,
.map-back-badge:focus-visible {
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36),
              0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.42);
}
.map-back-badge svg {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
}

/* M18.X-UX-QUICKWINS (ADR-295, Petřin pivot 2026-06-10) — chart-leader
   pill ("{song} leads"). Same noir glass pill; pins the gold treatment
   (copper family, mirrors .online-live-badge.is-today) — gold = the
   rating/chart surface colour. SOLID dot, no pulse. Click = open Chart. */
.chart-leader-badge {
  color: var(--accent-copper-bright);
  border-color: rgba(var(--accent-copper-rgb), 0.40);
}
.chart-leader-badge:hover,
.chart-leader-badge:focus-visible {
  outline: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.36),
              0 0 0 1px rgba(var(--accent-copper-rgb), 0.38);
}
.chart-leader-badge .online-live-badge__dot {
  background: var(--accent-copper-bright); /* solid gold dot, no pulse */
  box-shadow: 0 0 6px rgba(var(--accent-copper-rgb), 0.7);
}
/* Song titles vary wildly in length — cap the name and ellipsize so the
   pill can never blow up the satellite row (Petřin pokyn 2026-06-10).
   Italic = the title reads as a WORK ("The Trace Outlives" leads), and the
   name visually separates from the plain "leads" verb. */
.chart-leader-badge__song {
  display: inline-block;
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
  font-style: italic;
  /* Bright gold + bold = the song NAME is the hero; the trailing verb below
     is made TYPOGRAPHICALLY finer (not greyed) so the hierarchy reads in one
     warm tone (Petřin pokyn 2026-06-10, "splývá → leads jemnější"). */
  color: var(--accent-copper-bright);
  font-weight: 700;
  /* The italic slant of the LAST glyph overhangs the box and overflow:hidden
     clipped it ("Outlives" lost its s — Petřin nález). Padding gives the
     overhang room inside the clip box; the negative margin hands the same
     space back so the gap before "leads" doesn't grow. */
  padding-right: 0.15em;
  margin-right: -0.15em;
}
/* The "leads" verb: kept full gold and fully legible, but typographically
   LIGHT (400 vs the bold 700 name) and a touch smaller + spaced — the big
   weight gap does the separating, no dimming (Petřin nález: opacity killed
   legibility; weight contrast keeps it readable). */
.chart-leader-badge__verb {
  font-weight: 400;
  font-size: 0.74rem;
  letter-spacing: 0.03em;
}
/* (Removed 2026-06-11, Petřin pokyn: the __amp tie connector — a 2-way tie
   no longer names both songs ("A & B lead" did not fit a phone); every tie
   now reads "N songs lead by M ★", so the "&" glyph has no caller.) */
/* 3+ co-leaders ("N songs tied at the top") — the count + "songs" is the hero,
   so it stays bold + bright like a song name BUT upright (it is a statement,
   not a work title, hence no italic). The trailing words ride the fine verb. */
.chart-leader-badge__many {
  font-weight: 700;
  color: var(--accent-copper-bright);
}
/* Lead margin tail ("by 7 ★") — the count rides as a bold gold figure (same
   weight/tone as the song name, no italic — it is data, not a title) and the
   star reuses the Chart's #icon-star glyph, sized down + copper to sit on the
   small pill. Petřin pokyn 2026-06-11 — náskok místo mrtvého celkového počtu. */
.chart-leader-badge__lead {
  font-weight: 700;
  color: var(--accent-copper-bright);
}
/* 0,2,0 specificity ON PURPOSE — the shared `.icon { width:16px }` rule sits
   LATER in this file and ties a bare `.chart-leader-badge__star` (0,1,0), so
   the bare class LOSES on source order and the star renders 16px: it grows
   the line box, which drags the bottom-anchored song name 1.6px below the
   verb's baseline (same cascade trap documented at the `.icon` rule). */
.chart-leader-badge .chart-leader-badge__star {
  width: 0.86em;
  height: 0.86em;
  /* The star is a DIRECT flex child of the pill (appended by appendLead in
     eventLiveIndicator.js), so align-items:center centers it on the PILL's
     height (Petřin pokyn 2026-06-11). The pill's own 0.4rem flex gap lands
     between label and star — the negative side of the calc pulls the glyph
     back tight to the "18" figure (~0.18em visual gap, as inline before). */
  margin-left: calc(0.18em - 0.4rem);
  color: var(--accent-copper-bright);
  /* Paint-only enlargement — a bigger box would grow the pill height. */
  transform: scale(1.25);
}

/* (Removed 2026-06-06, Petřin pokyn: the persistent LIVE banner block —
   .live-banner + __dot/__label/__link/__cta/__more. It collided with the
   hero wordmark and duplicated the satellite online pill; the live signal
   is now the pill's mint pulse + the Online tab data-live dot.) */

/* Top-right floating cluster — Add Pin + auth header. Map-first redesign
   2026-05-14. Sidebar (.events-panel) starts BELOW this cluster
   (top: ~4 rem) so they live in vertical stack instead of fighting for
   the same column. */
.top-right-cluster {
  position: absolute;
  top: max(0.6rem, env(safe-area-inset-top));
  right: max(0.75rem, env(safe-area-inset-right));
  z-index: 1000;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}

/* M-UX-AUDIT-002 (M10 closure audit 2026-05-15) per ADR-074 R2 + nový
   P-038 (mobile): hamburger toggle visible only on mobile. Desktop hides
   it because the auth-header CTAs are already visible inline.

   ADR-085 (2026-05-16) — Frost glass pebble recipe per P-051 chrome family
   rule. Cream-glass + blur(16px) + corner mesh teal + halo-teal shadow +
   turquoise-tint border. NE solid cream-white (Petřin pet peeve), NE navy
   (Petřin 2026-05-16 explicit rejection „chci zůstat ve světlém frosty
   designu"). Same DNA jako hero strip, jen v kulaté formě. */
.menu-toggle {
  display: none;
  appearance: none;
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 60%),
    rgba(var(--cream-surface-rgb), 0.78);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.12);
  border-radius: 50%;
  min-width: 44px;
  min-height: 44px;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--text-dark);
  cursor: pointer;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow:
    0 0 14px rgba(var(--accent-turquoise-rgb), 0.18),
    0 4px 14px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(238, 240, 250, 0.55);
  align-items: center;
  justify-content: center;
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .menu-toggle:hover {
    background:
      radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 60%),
      var(--cream-surface);
    box-shadow:
      0 0 18px rgba(var(--accent-turquoise-rgb), 0.24),
      0 6px 18px rgba(0, 0, 0, 0.12),
      inset 0 1px 0 rgba(238, 240, 250, 0.6);
  }
}
.menu-toggle:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* (.songs-pebble removed — M13.X-SONGS-TAB ADR-237: the song world is a
   first-class panel tab now; the floating pebble + its dropdown are gone.
   Was ADR-088 / M11.X-NAVPIVOT.) */
/* Vertical stack pro top-right chrome na mobile: hamburger ☰ nahoře,
   Songs 🎵 pod ní. Per Petřin pokyn Q4 2026-05-16 "samostatně
   top-right corner pod menu (vertikální stack)". Desktop ponechává
   horizontal inline-flex (auth-header Sign in / Sign up + songs
   pebble inline). */
@media (max-width: 767px) {
  .top-right-cluster {
    flex-direction: column;
    align-items: flex-end;
  }
}

/* Songs placeholder dialog (ADR-088) — reuses generic dialog atmospheric
   pattern from pin-form-dialog. Minimal copy: title + body + Close. */
/* === Songs leaderboard dialog (M12I, ADR-178) ===========================
   Amethyst-accented chrome to match the songs pebble identity
   (--accent-songs). Replaces the old "coming soon" placeholder. */
/* M12.X-CHART-REDESIGN (ADR-198) — deeper plum→midnight→ink surface with
   violet (top-left) + magenta (bottom-right) atmospheric washes, per the
   designer's chart-spec §2. Tokenised (no raw designer hex) so it tracks the
   Noir palette. Wider canvas (600px) gives the 3-up podium room to breathe. */
.songs-dialog {
  border: 1px solid var(--border-on-light);
  border-radius: 28px;
  padding: 1.25rem;
  max-width: 92vw;
  width: 560px;
  background:
    radial-gradient(ellipse 130% 70% at 12% -8%, rgba(var(--accent-violet-rgb), 0.20) 0%, transparent 52%),
    radial-gradient(ellipse 120% 80% at 100% 108%, rgba(var(--accent-mine-rgb), 0.12) 0%, transparent 50%),
    linear-gradient(176deg, var(--surface-plum) 0%, var(--cream-surface) 58%, var(--cream-base) 100%);
  color: var(--text-dark);
  box-shadow: var(--shadow-modal), 0 40px 90px rgba(0, 0, 0, 0.55);
}
.songs-dialog::backdrop {
  background:
    radial-gradient(circle at center, rgba(var(--accent-songs-rgb), 0.16) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.80) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
/* Header row — title only (badge icon removed — Petřin pokyn 2026-06-03,
   header audit). Desktop stays left-aligned (centred variant retracted). */
.songs-dialog__head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0 0 0.3rem;
}
/* (Badge tile removed — Petřin pokyn 2026-06-03, header audit: both song
   windows are title-only, no icon in the head.) */
/* Title mirrors the ADR-179 unified dialog-title canon (white Cormorant
   italic 1.55rem). Kept per-dialog to avoid editing the PR6 unified
   selector list mid-stream — fold in during the PR6 pass. */
.songs-dialog h2 {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-modal-title); /* big content-modal tier (M13.X-TITLE-TIERS) */
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  color: var(--text-dark);
}
.songs-dialog__intro {
  margin: 0 0 0.3rem;
  font-size: var(--fs-md);
  line-height: 1.45;
  color: var(--text-muted);
}
/* Anon nudge = link-look (underline default, color shift on hover), not a
   button (per feedback_anon_nudge_link_look). */
.songs-dialog__nudge {
  display: inline-block;
  margin: 0 0 0.8rem;
  padding: 0;
  background: none;
  border: none;
  font: var(--fw-bold) var(--fs-sm)/1.3 var(--font-body);
  /* Violet TEXT on noir = the SOFT variant (standard: --accent-violet-soft
     reads clear, base --accent-violet reads dim) — Petřin pokyn 2026-05-31. */
  color: var(--accent-violet-soft);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .songs-dialog__nudge:hover { color: var(--accent-violet-bright); }
}
.songs-dialog__state {
  margin: 0.4rem 0 0.8rem;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* ==========================================================================
   CHART — podium leaderboard (M12.X-CHART-REDESIGN / ADR-198).
   Source of truth: docs/handoff/frisson-chart-handoff (chart-spec.md +
   mockups). Rank accent flows through one custom prop (--chart-accent) set on
   the rank/mine modifiers; bar fill, number gradient + border all read it, so
   the gold/iris/teal/magenta story is declared once per item.
   Token map: gold→copper · iris→violet · teal→turquoise · mine→magenta.
   ========================================================================== */
/* Cold start = album order, no podium, NO medals (ADR-198 Rozhodnutí 1). The
   shared row markup still tags ranks 1–3, so neutralise the medal accent here
   (your own MY TOP track keeps its gold highlight). */
.chart-body--coldstart .chart-row:not(.chart-row--top) {
  --chart-accent: var(--text-light-muted);
  --chart-accent-rgb: 140, 149, 184;
}
.chart-body--coldstart .chart-row--rank1:not(.chart-row--top) .chart-rank,
.chart-body--coldstart .chart-row--rank2:not(.chart-row--top) .chart-rank,
.chart-body--coldstart .chart-row--rank3:not(.chart-row--top) .chart-rank {
  color: var(--text-light-muted);
  text-shadow: none;
}

/* Rank accent — one custom prop, set per rank / mine. Default = muted mist. */
/* Two accents per item: --chart-accent (base) drives fills/bars/borders;
   --chart-accent-text is the BRIGHT variant for the big numeral + title text.
   On the dark Noir surface the base violet/teal read dim as text, so text uses
   the -soft / -bright variants (memory: violet text = --accent-violet-soft).
   This is the fix for "barvy jsou tlumené" — numbers were base-violet + a
   white-topped gradient that washed them out. */
.chart-podium__card,
.chart-row {
  --chart-accent: var(--text-light-muted);
  --chart-accent-rgb: 140, 149, 184;
  --chart-rank-text: var(--text-light-muted);
  --chart-rank-rgb: 140, 149, 184;
  /* light → deep stops for the polished two-tone numeral gradient (podium) */
  --chart-rank-light: var(--text-light);
  --chart-rank-deep: var(--text-light-muted);
}
.chart-podium__card--rank1, .chart-row--rank1 {
  --chart-accent: var(--accent-copper);  --chart-accent-rgb: var(--accent-copper-rgb);
  --chart-rank-text: var(--accent-copper); --chart-rank-rgb: var(--accent-copper-rgb);
  /* deep = the saturated gold base, NOT copper-deep (#8a6a2a olive read grey) */
  --chart-rank-light: var(--accent-copper-bright); --chart-rank-deep: var(--accent-copper);
}
.chart-podium__card--rank2, .chart-row--rank2 {
  --chart-accent: var(--accent-violet);   --chart-accent-rgb: var(--accent-violet-rgb);
  --chart-rank-text: var(--accent-violet-soft); --chart-rank-rgb: var(--accent-violet-soft-rgb);
  --chart-rank-light: var(--accent-violet-bright); --chart-rank-deep: var(--accent-violet);
}
.chart-podium__card--rank3, .chart-row--rank3 {
  --chart-accent: var(--accent-turquoise); --chart-accent-rgb: var(--accent-turquoise-rgb);
  --chart-rank-text: var(--accent-turquoise); --chart-rank-rgb: var(--accent-turquoise-rgb);
  --chart-rank-light: var(--accent-turquoise-bright); --chart-rank-deep: var(--accent-turquoise);
}
/* "MY TOP" = soft GOLD frame / bar / tint ONLY (the fan's highest-rated track,
   M13.X-SONG-RATING). The rank numeral keeps its medal colour (#1 stays GOLD,
   #2 iris, #3 teal even when it is your top) — top must never recolour the
   number. So top sets --chart-accent but deliberately NOT --chart-rank-text. */
.chart-podium__card--top, .chart-row--top {
  --chart-accent: var(--accent-copper);  --chart-accent-rgb: var(--accent-copper-rgb);
}

/* Lining + tabular numerals on every chart figure — Cormorant defaults to
   old-style digits whose descenders clip in tight rows (chart-spec §4.1). */
.chart-rank, .chart-stars__n {
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
}

/* ---- Podium (top 3, desktop 2·1·3) ------------------------------------- */
.chart-podium {
  display: grid;
  grid-template-columns: 1fr 1.15fr 1fr;
  /* stretch = all three cards share the row height, so a long title on #2 or #3
     grows BOTH side cards together (never one taller than the other). The podium
     step + #1 dominance is restored by the rank offsets below, not by content
     height (Petřin pokyn 2026-06-08 — "schody, jednička vykukuje"). */
  align-items: stretch;
  gap: 10px;
  margin-top: 1.6rem;   /* generous room for the #1 crown (Petřin pokyn — větší padding od korunky) */
  margin-bottom: 0.85rem;
}
.chart-podium--winners { grid-template-columns: 1fr; }

.chart-podium__card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0.7rem 0.6rem 0.65rem;
  border-radius: 16px;
  border: 1px solid rgba(var(--chart-accent-rgb), 0.33);
  background: linear-gradient(
    180deg,
    rgba(var(--chart-accent-rgb), 0.16) 0%,
    rgba(var(--chart-accent-rgb), 0.05) 45%,
    transparent 100%
  );
}
/* #1 sits taller — bigger number, crown headroom (chart-spec §5). */
.chart-podium__card--rank1 {
  padding-top: 1.05rem;
  padding-bottom: 0.65rem;
}
/* Side cards step DOWN below #1 — the podium silhouette. Equal offset keeps #2
   and #3 perfectly level with each other (chart-spec §3 "2 · 1(tall) · 3"). */
.chart-podium__card--rank2,
.chart-podium__card--rank3 {
  margin-top: var(--space-6);
}
/* "MY TOP" podium card — soft gold frame + halo over the accent fill. */
.chart-podium__card--top {
  border-color: rgba(var(--accent-copper-rgb), 0.6);
  background: linear-gradient(
    180deg,
    rgba(var(--accent-copper-rgb), 0.16) 0%,
    rgba(var(--accent-copper-rgb), 0.05) 55%,
    transparent 100%
  );
  box-shadow:
    0 0 0 1px rgba(var(--accent-copper-rgb), 0.33),
    0 18px 50px rgba(var(--accent-copper-rgb), 0.20);
}

/* Crown — gold, floats over the #1 card top edge. */
.chart-crown {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  width: 24px;
  height: 24px;
  color: var(--accent-copper);
  filter: drop-shadow(0 2px 6px rgba(var(--accent-copper-rgb), 0.5));
}
.chart-crown svg { width: 100%; height: 100%; display: block; }

/* Podium number — polished two-tone medal gradient WITHIN the hue (light top →
   base → deep bottom), per the mockup ("vítězové mají uvnitř gradients"). Stays
   in-hue so #1 reads GOLD, not cream; NOT mine-magenta — the numeral always
   follows its rank (Petřin pokyn 2026-05-31). */
.chart-podium__card .chart-rank {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 40px;
  line-height: 1;
  background: linear-gradient(
    180deg,
    var(--chart-rank-light) 0%,
    var(--chart-rank-deep) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  /* faint glow only — a strong halo flattened the gradient (Petřin pokyn) */
  text-shadow: 0 0 14px rgba(var(--chart-rank-rgb), 0.18);
}
.chart-podium__card--rank1 .chart-rank { font-size: 52px; }

/* Titles WRAP (no truncation) and stay readable — Petřin pokyn 2026-05-31
   ("směrem zalomení textu", winner titles must be legible). */
.chart-podium__title {
  display: grid;
  place-items: center;
  /* grow into the shared card height so the bar + stars always sit on one
     baseline across all three cards, no matter how many lines a title wraps to.
     min-height = 2-line floor; a 3-line title pushes the whole row taller and
     every card grows with it (Petřin pokyn 2026-06-08). */
  flex: 1 1 auto;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.3;
  min-height: calc(16px * 1.3 * 2); /* reserve 2 lines so cards align */
  margin-top: 0.3rem;
  color: var(--text-dark);
}
.chart-podium__card--rank1 .chart-podium__title {
  font-size: 18px;
  min-height: calc(18px * 1.3 * 2);
}
/* Votes + optional play icon, centred under the bar. */
.chart-podium__meta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin-top: 0.4rem;
}
/* Winner title stays the same colour as every other title, even when it is
   your own pick (Petřin pokyn 2026-05-31) — no lavender mine-tint. */

.chart-podium__flags {
  display: flex;
  justify-content: center;
  gap: 5px;
  margin-top: 8px;
  min-height: 13px;
}
.chart-podium .chart-bar { margin-top: 0.5rem; width: 74%; }
/* Podium bar follows the RANK colour, clearly gold/iris/teal (not greyish, not
   mine-magenta) — base → light so it reads bright (Petřin pokyn 2026-05-31). */
.chart-podium .chart-bar__fill {
  background: linear-gradient(90deg, var(--chart-rank-text) 0%, var(--chart-rank-light) 100%);
  box-shadow: 0 0 10px rgba(var(--chart-rank-rgb), 0.5);
}

/* ---- Hearts (favorite radio) — canonical M12.X-RSVP-UNIFY heart ---------- */
/* Identical heart treatment to the concert .rsvp__heart (Petřin pokyn
   2026-05-31 — "stejné jako u koncertů"): idle ghost outline, active = filled
   #heart-shade glyph + magenta frame, NO bg tile, NO glow (the glow flattened
   it). Circle shape kept (she's fine with the circle). */
/* "MY TOP" badge — the fan's highest-rated track. Magenta chip (the "mine"
   identity axis, --accent-mine) + a star, NOT a heart (heart = Echo like only).
   Magenta keeps it off the rank axis (gold/iris/teal #1·2·3) so the two never
   read as the same signal — chart README §C, Petřin pokyn 2026-06-02 (the gold
   chip clashed with the gold crown on the #1 winner). M13.X-SONG-RATING. */
.chart-top-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(var(--accent-mine-rgb), 0.5);
  background: rgba(var(--accent-mine-rgb), 0.16);
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--magentaLift);
  white-space: nowrap;
}
.chart-top-badge .icon {
  width: 12px;
  height: 12px;
  color: var(--accent-mine);
}
/* Podium (winner) badge tucks into the top-RIGHT corner — clear of the centred
   crown + the big rank numeral. Here the chip keeps its pill but shows JUST the
   magenta star (no "MY TOP" label) so it stays small on the #1 card; the row
   variant keeps the full "★ MY TOP" chip next to the title (Petřin pokyn
   2026-06-02). */
.chart-podium__card .chart-top-badge {
  position: absolute;
  top: 12px;
  right: 12px;
  gap: 0;
  padding: 4px;
  font-size: 0; /* collapse the "MY TOP" text node — star only inside the chip */
}
/* In a row the badge sits inline just after the title, with a slight offset. */
.chart-row__titleline .chart-top-badge {
  flex: 0 0 auto;
  margin-left: 2px;
}

/* ---- Rows (4–10, or 2–10 on mobile / album-order on cold start) --------- */
.chart-section-label {
  margin: 0 0 0.75rem;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted); /* brighter — mist read too grey (Petřin pokyn) */
}
.chart-rows {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.chart-row {
  display: grid;
  grid-template-columns: 38px 1fr auto;
  align-items: center;
  column-gap: 12px;
  padding: 7px 12px;
  border-radius: 12px;
  border: 1px solid transparent;
}
/* "MY TOP" row — soft gold band + frame + glow (the fan's highest-rated track,
   M13.X-SONG-RATING). Gold, never the old iris "your vote" treatment. */
.chart-row--top {
  background: linear-gradient(
    90deg,
    rgba(var(--accent-copper-rgb), 0.18) 0%,
    rgba(var(--accent-copper-rgb), 0.06) 100%
  );
  border-color: rgba(var(--accent-copper-rgb), 0.4);
  box-shadow: 0 0 18px rgba(var(--accent-copper-rgb), 0.18);
}

.chart-row .chart-rank {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 28px;
  line-height: 1;
  text-align: center;
  color: var(--text-light-muted);
}
.chart-row--rank1 .chart-rank,
.chart-row--rank2 .chart-rank,
.chart-row--rank3 .chart-rank {
  color: var(--chart-rank-text);
  text-shadow: 0 0 16px rgba(var(--chart-rank-rgb), 0.4);
}
.chart-row__main { min-width: 0; }
.chart-row__titleline {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.chart-row__title {
  min-width: 0;
  /* Wrap to as many lines as the title needs — no ellipsis truncation
     (Petřin pokyn 2026-05-31). */
  overflow-wrap: anywhere;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.3;
  color: var(--text-dark);
}
.chart-row__flags {
  display: flex;
  gap: 5px;
  margin-top: 6px;
  min-height: 13px;
}
.chart-row__flags:empty { display: none; }
/* Bar fills the row width up to the star sum — on the 560px dialog a 300px cap
   left a big void between the title and the ★ count (Petřin pokyn 2026-06-02). */
.chart-row .chart-bar { margin-top: 7px; }

/* ---- Flags (up to 3 — countries where the song is most loved) ----------- */
.chart-flag {
  border-radius: 3px;
  box-shadow: 0 0 0 1px var(--border-on-light);
}

/* ---- Vote-strength bar -------------------------------------------------- */
.chart-bar {
  display: block;
  width: 100%;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--hairline-dark-lo);
  overflow: hidden;
}
.chart-bar__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: linear-gradient(
    90deg,
    rgba(var(--chart-accent-rgb), 0.6),
    var(--chart-accent)
  );
  box-shadow: 0 0 10px rgba(var(--chart-accent-rgb), 0.4);
}

/* ---- Star sum (gold ★ + total stars fans gave the track) ---------------- */
.chart-stars {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 5px;
  white-space: nowrap;
}
/* Align the ★ under each other across rows — the figure sits at the star
   column's LEFT edge so a 1-digit and a 2-digit sum keep the glyph in the same
   spot (Petřin pokyn 2026-06-02). The number trails to the right. */
.chart-row .chart-stars { justify-self: start; }
.chart-stars__icon {
  width: 15px;
  height: 15px;
  color: var(--accent-copper); /* gold star = rating (never the magenta heart) */
}
.chart-stars__n {
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-md);
  color: var(--text-dark);
}
/* Zero state — quiet mist (a track nobody has rated yet). */
.chart-stars--zero .chart-stars__n { color: var(--text-light-muted); }
.chart-stars--zero .chart-stars__icon { color: var(--text-light-muted); opacity: 0.7; }

/* ---- Play icon (admin-set YouTube link) --------------------------------- */
.chart-play {
  flex: 0 0 auto;
  display: inline-flex;
  width: 19px;
  height: 19px;
  color: var(--accent-turquoise); /* teal link affordance (Petřin pokyn 2026-05-31) */
  transition: color 0.15s ease, transform 0.15s ease, filter 0.15s ease;
}
.chart-play svg { width: 100%; height: 100%; }
@media (hover: hover) and (pointer: fine) {
  .chart-play:hover {
    color: var(--accent-turquoise-bright);
    transform: scale(1.22);
    filter: drop-shadow(0 0 6px rgba(var(--accent-turquoise-rgb), 0.65));
  }
}
.chart-play:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  border-radius: 50%;
}

/* ---- Chart, phones (<= 767px) ------------------------------------------
   Podium collapses to the single #1 winner (JS renders only 1 card here). The
   winner card is a smaller centred box with a medium ~40px numeral (Petřin
   pokyn 2026-05-31 — "celý box menší, jednička nemusí být tak obrovská"); rows
   go compact. Flags + the strength bar stay (Rozhodnutí 3 + 4). */
@media (max-width: 767px) {
  /* The fullscreen sheet itself scrolls (mobile.css) — drop the inner scroll
     so the list isn't trapped in a cramped nested scroll region. */
  .chart-body { max-height: none; overflow: visible; }

  /* Smaller, centred winner box — not full-bleed. */
  .chart-podium--winners {
    grid-template-columns: minmax(0, 250px);
    justify-content: center;
  }
  .chart-podium--winners .chart-podium__card { padding: 0.65rem 0.7rem 0.7rem; }
  .chart-podium--winners .chart-podium__card--rank1 { padding-top: 0.95rem; }
  .chart-podium--winners .chart-podium__card--rank1 .chart-rank { font-size: 40px; }
  .chart-podium--winners .chart-bar { width: 86%; }

  .chart-row {
    grid-template-columns: 34px 1fr auto;
    column-gap: 10px;
    padding: 9px 10px;
  }
  .chart-row .chart-rank { font-size: 30px; }
  .chart-row__title { font-size: 16px; }
}

/* Mobile chrome reduction — on <768px, the auth-header collapses
   behind the hamburger menu. Sign in / Sign up / signed-in user pill
   are all surfaced from the overlay instead. */
@media (max-width: 767px) {
  .top-right-cluster .auth-header {
    display: none;
  }
  .menu-toggle {
    display: inline-flex;
  }
}

/* Mobile menu — floating dropdown panel UNDER the hamburger toggle.
   Per Petřin pokyn 2026-05-16 "pod hamburger ma byt mobilni menu,
   plovouci" — NOT a full-height drawer from the right edge. Compact
   ~280 px dropdown anchored top-right beneath the ☰ button. List-item
   style throughout (NO big copper Sign in/up buttons — they read as
   desktop CTAs, not menu items). */
.app-menu {
  position: fixed;
  top: calc(
    max(0.6rem, env(safe-area-inset-top))
      + 44px /* hamburger height */
      + 0.5rem /* gap below toggle */
  );
  right: max(0.75rem, env(safe-area-inset-right));
  width: min(86vw, 280px);
  z-index: 2000;
  display: none;
}
.app-menu.is-open {
  display: block;
  animation: appMenuPop 180ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
  transform-origin: top right;
}
/* Backdrop = invisible full-screen click target that closes the menu.
   No visual treatment — the menu is small enough that a translucent
   overlay would feel heavy. Petřin "plovouci" = pure floating chrome. */
.app-menu__backdrop {
  position: fixed;
  inset: 0;
  background: transparent;
  cursor: pointer;
  z-index: -1;
}
.app-menu__panel {
  background: var(--cream-base);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.18);
  border-radius: 14px;
  box-shadow:
    0 10px 32px rgba(0, 0, 0, 0.18),
    0 2px 8px rgba(0, 0, 0, 0.08);
  padding: 0.45rem;
  display: flex;
  flex-direction: column;
  gap: 0;
}
/* × close button — hidden in the floating-dropdown pattern (backdrop
   click + ESC + clicking another link are the dismiss paths). Keep the
   markup so screen readers still get the affordance, but visually
   collapse to zero. */
.app-menu__close {
  display: none;
}
/* Auth slot uses the same list-item visual as nav items below — NO
   copper Sign in / Sign up buttons in the menu (Petřin "menu, ne
   desktop"). Override mobileMenu.js's cloned .auth-btn styling. */
.app-menu__auth {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.app-menu__auth .auth-btn,
.app-menu__auth .auth-header__user {
  /* Reshape cloned auth-header content into ACCOUNT-style nav rows
     (Petřin pokyn 2026-06-01): leading teal glyph + light label, no copper
     pill. Mirrors .app-menu__nav button so anon Sign in / Sign up sit in
     the same visual system as Profile / Security below. */
  appearance: none;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  background: transparent !important;
  border: 0;
  box-shadow: none;
  color: var(--text-dark);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0;
  text-align: left;
  padding: var(--space-3);
  min-height: 52px;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 160ms ease, color 160ms ease;
}
/* Sign up no longer carries the violet accent — both CTAs use the light
   label + teal icon of the nav rows. The glyph colour comes from the
   shared .app-menu__nav-icon rule (mobile.css). */
.app-menu__auth .auth-btn .app-menu__nav-icon {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  color: var(--accent-turquoise);
}
.app-menu__auth .auth-header__user {
  color: var(--text-dark);
  font-weight: 700;
}
@media (hover: hover) and (pointer: fine) {
  .app-menu__auth .auth-btn:hover,
  .app-menu__auth .auth-header__user:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.08);
  }
}
.app-menu__auth .auth-btn:focus-visible,
.app-menu__auth .auth-header__user:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
.app-menu__nav {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid rgba(238, 240, 250, 0.08);
  margin-top: 0.3rem;
  padding-top: 0.3rem;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.app-menu__nav a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.6rem 0.7rem;
  color: var(--text-dark);
  text-decoration: none;
  font-size: 0.95rem;
  border-radius: 8px;
}
@media (hover: hover) and (pointer: fine) {
  .app-menu__nav a:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.08);
    color: var(--accent-turquoise);
  }
}
.app-menu__nav a:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  color: var(--accent-turquoise);
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* Petřin postřeh 2026-05-16 odp. — user-menu buttons v hamburger nav
   (Profile / Security / My pins / Preferences / Data / Sign out).
   Mirror .app-menu__nav a styling so the buttons read jako siblingy
   linků (= konzistentní tap target + hover). */
.app-menu__nav button {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.7rem;
  color: var(--text-dark);
  background: transparent;
  border: none;
  font: inherit;
  font-size: 0.95rem;
  text-align: left;
  cursor: pointer;
  transition: background 200ms ease, color 200ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .app-menu__nav button:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.08);
    color: var(--accent-turquoise);
  }
}
.app-menu__nav button:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  color: var(--accent-turquoise);
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

.app-menu__sep {
  border: none;
  border-top: 1px solid rgba(238, 240, 250, 0.08);
  margin: 0.3rem 0;
}

.app-menu__user-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.8rem;
  background: rgba(var(--accent-violet-rgb), 0.08);
  color: var(--accent-violet-soft);
  border-radius: 999px;
  font-weight: 700;
  font-size: 0.92rem;
}
@keyframes appMenuPop {
  from { opacity: 0; transform: scale(0.92) translateY(-4px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .app-menu.is-open { animation: none; }
}

/* M-UX-AUDIT-003 (M10 closure audit 2026-05-15) per P-027 — hero
   collapse pattern v M10 nakonec NEpouužíván (Petřin pokyn 2026-05-16
   "hero vrať jak bylo a zmenši beta" = base mobile hero už je compact
   enough s tiny BETA badge — kolaps nepotřeba). Mobile-collapsed
   override bloky odstraněné; pointerdown handler v main.js zůstává jako
   no-op trigger pokud M12 polish chce hero animaci znovu zapojit. */

/* Search bar — floating top-left. Map zoom controls moved to
   `bottomleft` (see map.js initMap), so the search can sit flush
   at the top with no offset / collision.
   M10E sub-blok 2/4 polish: container is a flex column so toggle ↑
   input-wrap ↑ results stack vertically; on mobile (<768px) the
   data-state attribute toggles between "collapsed" (icon-only pill,
   per ADR-048 D.3) and "expanded" (full bar). */
#map-search.map-search {
  position: absolute;
  top: max(0.9rem, env(safe-area-inset-top));
  left: max(0.75rem, env(safe-area-inset-left));
  z-index: 1000;
  width: 320px;
  max-width: calc(100vw - 1.5rem);
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

/* ============================================================
   Map legend (M12 Noir PR5 ④ — mockup 02-markers-legend.png)
   ============================================================
   Bottom-left toggle pill opens a reference popover explaining each
   marker. Rows are injected by mapLegend.js using the REAL marker SVG
   constants, so the icons here are pixel-identical to the map. Desktop
   only — the mobile treatment lands in M12D (mobile redesign milestone). */
.map-legend-toggle {
  position: absolute;
  /* Stacked directly above the Leaflet zoom +/− control (Petřin pokyn
     2026-05-25). Zoom bar sits at Leaflet's 10px bottom-left margin and is
     ~89px tall (2 × 44px buttons + bar border); clear it + an 8px gap. Left
     edge aligns with the zoom bar (Leaflet's 10px). */
  bottom: calc(10px + 91px + var(--space-2));
  left: max(10px, env(safe-area-inset-left));
  z-index: 1000;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px; /* P-041 tap area */
  padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-on-light);
  background: rgba(var(--cream-surface-rgb), 0.92);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-overlay-bar);
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 180ms ease, border-color 180ms ease, background 180ms ease;
}
.map-legend-toggle__icon {
  flex: 0 0 auto;
}
@media (hover: hover) and (pointer: fine) {
  .map-legend-toggle:hover {
    color: var(--text-dark);
    border-color: var(--accent-copper);
  }
}
.map-legend-toggle:focus-visible {
  outline: 2px solid var(--accent-copper);
  outline-offset: 2px;
}
/* Open state — copper accent so the pill reads as "active". */
.map-legend-toggle[aria-expanded="true"] {
  color: var(--accent-copper-bright);
  border-color: var(--accent-copper);
}

.map-legend {
  position: absolute;
  /* Opens above the toggle (which sits above the zoom bar): zoom bar (10+91)
     + toggle height (44) + two 8px gaps. */
  bottom: calc(10px + 91px + 44px + var(--space-2) * 2);
  left: max(10px, env(safe-area-inset-left));
  z-index: 1000;
  width: 252px;
  max-width: calc(100vw - 1.5rem);
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-on-light);
  /* Same atmospheric mesh wash as .events-panel — one surface system. */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 50%),
    radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 50%),
    radial-gradient(at 100% 100%, var(--frost-wash-copper) 0%, transparent 60%),
    rgba(var(--cream-surface-rgb), 0.97);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-card);
}
.map-legend[hidden] {
  display: none;
}
/* ADR-295 — soft entrance: fade + gentle scale from the toggle's corner
   (panel opens above the bottom-left toggle). Plays on the `hidden` flip;
   mobile uses the legend bottom sheet, so this is desktop-only by nature. */
@keyframes legend-soft-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
.map-legend:not([hidden]) {
  transform-origin: bottom left;
  animation: legend-soft-in 160ms ease;
}
@media (prefers-reduced-motion: reduce) {
  .map-legend:not([hidden]) {
    animation: none;
  }
}
/* × close — reuses the popup chip's visual rule above; this just resets the
   <button> UA chrome so the shared disc styling reads cleanly. */
.map-legend__close {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}
.map-legend__title {
  /* Header band reserves the height of the 32px popup-style × so it never
     overlaps the first legend row. */
  display: flex;
  align-items: center;
  min-height: 28px;
  margin: 0 0 var(--space-3);
  padding-right: 32px;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-copper);
}
.map-legend__rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.map-legend__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.map-legend__icon {
  flex: 0 0 34px;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Open-gathering sample (M13.X-JOIN-DISCOVERY) — the fan teardrop with the
   REAL sonar ring mask painted statically behind its head: same
   --open-ring-mask + violet-deep tone as .pin-marker-wrap--open on the map,
   minus the pulse animation (static sample, like the cluster row). */
.map-legend__open-sample {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.map-legend__open-sample > svg {
  position: relative; /* lift the teardrop above the ::before rings */
}
.map-legend__open-sample::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 15px; /* head centre of the 30×41 teardrop sample */
  /* Map ratio is a 64px ring box behind a 30px-wide marker; anything close to
     the head width hides the rings BEHIND the teardrop (render-verified).
     46px keeps the outer ring clearly visible without invading the text. */
  width: 46px;
  height: 46px;
  transform: translate(-50%, -50%);
  /* The map ring is violet-DEEP because it sits on LIGHT CartoDB tiles; the
     legend panel is dark, where deep vanished (Petřin screenshot 2026-06-03).
     Soft variant = the canonical violet foreground on noir surfaces. */
  background-color: var(--accent-violet-soft);
  -webkit-mask: var(--open-ring-mask) center / contain no-repeat;
  mask: var(--open-ring-mask) center / contain no-repeat;
  opacity: 1;
  pointer-events: none;
}

/* Cluster sample — reuse the real .cluster-frost orb, just static + smaller
   and with a tighter halo so it sits cleanly in the list row. */
.map-legend__icon .cluster-frost__inner {
  --cluster-size: 30px;
  animation: none;
  box-shadow:
    0 0 0 2px rgba(var(--accent-violet-rgb), 0.88),
    0 2px 8px rgba(0, 0, 0, 0.4);
}
.map-legend__text {
  display: flex;
  flex-direction: column;
}
.map-legend__name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-lg);
  font-weight: 600;
  line-height: 1.1;
  color: var(--text-dark);
}
.map-legend__desc {
  margin-top: var(--space-0-5);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  line-height: 1.3;
  color: var(--text-muted);
}

/* Mobile (<768px): the toggle lives as a rail tile (actionRail.js reparents
   it; mobile.css reskins it) and the legend CONTENT opens in the bottom
   sheet like every other detail (mapLegend.js → bottomSheet.js, Petřin
   pokyn 2026-06-10). This popover card is desktop-only — it stays closed
   on phones via the [hidden] attribute, so no display:none is needed here.
   The old blanket display:none predated the rail and kept the panel hidden
   even though the tile was tappable — tapping Legend did nothing. */

/* ============================================================
   Events panel (M10D scope — ADR-068 + ADR-048 D.2)
   ============================================================
   Desktop ≥ 768px: fixed right sidebar, 340px wide.
   Mobile < 768px: bottom sheet with peek state (60px showing tabs)
                   + tap-to-expand to ~60vh.
   Both viewports share the same DOM tree; CSS handles the layout swap.
*/

.events-panel {
  /* M12 rollout — atmospheric mesh wash + multi-color halo */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 50%),
    radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 50%),
    radial-gradient(at 100% 100%, var(--frost-wash-copper) 0%, transparent 60%),
    rgba(var(--cream-surface-rgb), 0.97);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid var(--border-on-light);
  color: var(--text-dark);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow:
    0 1px 0 rgba(238, 240, 250, 0.7) inset,
    var(--halo-multi);
}

/* Mobile peek preview row (per ADR-085 + P-050) — default hidden on
   desktop, enabled only inside @media (max-width: 767px). M11E will add
   live-data binding; for now shows empty-state hint. */
.events-panel__preview {
  display: none;
}

/* Map-first redesign 2026-05-14: panel floats on the right side, starting
   BELOW the top-right Add Pin / auth cluster (~ top: 4rem) so there's no
   collision. Doesn't span to viewport top (no overlap with branding /
   controls) and has `bottom: 1rem` margin so the map remains visible all
   around. Two states:
     - expanded  → width: 320 px
     - collapsed → width: 48 px (rail with ☰ + mini icons)
*/
@media (min-width: 768px) {
  .events-panel {
    position: absolute;
    top: calc(max(0.6rem, env(safe-area-inset-top)) + 4rem);
    right: max(0.75rem, env(safe-area-inset-right));
    bottom: 1rem;
    z-index: 1020; /* above floating controls (1000) */
    /* Width bumped 320 → 400 px (Petřin pokyn 2026-05-17 — split CTAs
       were cramped; wider panel gives content + the Suggest concert /
       Suggest online event row room to breathe without shrinking
       buttons). Bumped 400 → 480 px (M13.X-SONGS-TAB 2026-06-03 — the
       fifth tab SONGS overflowed the tab row and cut off YOURS; 480 px
       fits all five tabs at unchanged tab padding). Collapsed rail
       width unchanged at 48 px. */
    width: 480px;
    border-radius: 16px;
    /* Transition added by JS after restoring persisted collapse state on
       first frame — prevents the "open then close" flicker when the panel
       is restored as collapsed from localStorage on page load. */
  }

  /* Anon callers have no Yours tab — four tabs fit the pre-Songs 400 px,
     so the panel keeps the narrower width and hides less map. Class is
     toggled by eventsPanel.js _syncYoursTabVisibility() on auth flips.
     Declared BEFORE .is-collapsed so the 48 px rail still wins. */
  .events-panel.events-panel--no-yours {
    width: 400px;
  }

  .events-panel.has-init {
    transition: width 250ms ease;
  }

  .events-panel.is-collapsed {
    width: 48px;
  }

  /* In collapsed state, hide every interior surface except the rail. */
  .events-panel.is-collapsed .events-panel__header,
  .events-panel.is-collapsed .events-panel__topbar,
  .events-panel.is-collapsed .events-panel__body,
  .events-panel.is-collapsed .events-panel__detail,
  .events-panel.is-collapsed .events-panel__close,
  .events-panel.is-collapsed .events-panel__toggle {
    display: none;
  }

  /* Conversely, the rail is visible only when sidebar is collapsed (desktop). */
  .events-panel.is-collapsed .events-panel__rail {
    display: flex;
  }

  /* Mobile-specific bottom-sheet drag handle is hidden on desktop. */
  .events-panel__toggle {
    display: none;
  }
}

/* Mobile layout: Variant Y per ADR-085 revised 2026-05-16. Bottom panel
   is HIDDEN by default on mobile — Pins/Events tabs live in hero strip
   instead (per .hero-strip__tabs). Tab click in hero opens this panel
   as a full sheet from bottom (60dvh). Sheet has close × instead of
   drag pip; no peek state. */
@media (max-width: 767px) {
  .events-panel {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom));
    z-index: 1050;
    border-radius: 18px;
    /* Default hidden — tap hero tab to open. */
    display: none;
    max-height: 60dvh;
    /* Atmospheric mesh + halo same recipe as hero strip. */
    background:
      radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 50%),
      radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 50%),
      radial-gradient(at 100% 100%, var(--frost-wash-copper) 0%, transparent 60%),
      rgba(var(--cream-surface-rgb), 0.97);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border: 1px solid rgba(var(--accent-turquoise-rgb), 0.10);
    padding-bottom: 0;
    box-shadow:
      0 1px 0 rgba(238, 240, 250, 0.7) inset,
      var(--halo-multi);
    animation: panel-slide-up 280ms ease-out;
  }

  .events-panel.is-expanded {
    display: flex;
  }

  @keyframes panel-slide-up {
    from { transform: translateY(20px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
  }

  /* Drag pip → close × on mobile sheet. Repurpose existing toggle button
     as the dismissal control. */
  .events-panel__toggle {
    appearance: none;
    background: transparent;
    border: none;
    align-self: flex-end;
    margin: 6px 8px -2px 0;
    width: 44px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 1.4rem;
    line-height: 1;
    padding: 0;
  }
  .events-panel__toggle::before {
    content: "×";
  }
  /* Hide the original drag pip visual on mobile. */
  .events-panel__toggle-handle {
    display: none;
  }

  /* Preview row from previous iteration — hidden on mobile (no peek state
     anymore; hero tabs are the entry point). */
  .events-panel__preview {
    display: none;
  }

  /* Mobile uses the bottom-sheet pattern, not the desktop rail/close. */
  .events-panel__rail,
  .events-panel__close {
    display: none;
  }

  /* Internal panel tabs (PINS | EVENTS row) — hidden on mobile because
     hero-strip__tabs serve the same role. Avoid duplicate controls. */
  .events-panel__header {
    display: none !important;
  }
}

/* M12 Noir PR4 (mockups 20–23) — panel top bar: just the round close ×
   on the right, sitting above the tab row. (The "My Echoes first" toggle
   + Song filter moved into the Echoes pane — Petřin pokyn 2026-06-03.) */
.events-panel__topbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
  padding: 0.6rem 0.7rem 0.55rem;
  flex-shrink: 0;
}

/* Echoes-only controls row (Petřin pokyn 2026-06-03) — "My Echoes first"
   toggle + Song filter capsule side by side on the left, one row under
   the Leave-an-echo CTA. Both act only on the Echoes list, so they live
   on this tab instead of the panel top bar. */
.pins-filter-row {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.75rem;
  flex-wrap: wrap;
  /* Side inset = .feed-list card gutter (0.7rem) so the toggle pill lines
     up with the cards' left edge; the Song capsule sits right next to it
     (Petřin pokyn 2026-06-03). */
  margin: 0 0.7rem 0.6rem;
}

/* Surface 3 (M13.X-FRISSON-JOURNEY) — the "Song" filter. Custom anchored
   listbox (ADR-145 — the native <select> popup renders UA-default and can't
   be styled). Capsule button per song-filter.png; the open list reuses the
   .pin-row-menu dropdown recipe. */
.song-filter {
  position: relative;
  display: inline-flex;
}
.song-filter__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(var(--song-name-rgb), 0.4);
  background: rgba(var(--song-name-rgb), 0.08);
  /* line-height 1.25 (ne 1) — the label clips overflow for the ellipsis,
     so a 1.0 line box cut the "g" descender (Petřin postřeh 2026-06-03). */
  font: var(--fw-bold) var(--fs-sm) / 1.25 var(--font-body);
  color: var(--song-name);
  white-space: nowrap;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease;
}
.song-filter__btn:hover,
.song-filter__btn[aria-expanded="true"] {
  background: rgba(var(--song-name-rgb), 0.14);
  border-color: rgba(var(--song-name-rgb), 0.6);
}
.song-filter__btn:focus-visible {
  outline: 2px solid var(--song-name);
  outline-offset: 2px;
}
.song-filter__label {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.song-filter__note,
.song-filter__chevron {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  color: var(--song-name);
}
.song-filter__chevron { opacity: 0.85; transition: transform 150ms ease; }
.song-filter__btn[aria-expanded="true"] .song-filter__chevron {
  transform: rotate(180deg);
}
.song-filter--active .song-filter__btn {
  border-color: rgba(var(--song-name-rgb), 0.7);
  background: rgba(var(--song-name-rgb), 0.16);
}
/* One-click clear × beside the capsule (visible only while a song is
   chosen) — small round ghost chip, same family as the panel close. */
.song-filter__clear {
  flex: 0 0 auto;
  align-self: center;
  margin-left: 6px;
  width: 24px;
  height: 24px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(238, 240, 250, 0.10);
  border: 1px solid rgba(238, 240, 250, 0.45);
  border-radius: 50%;
  /* Glyph ≈ half the chip diameter + light ink — same ratio as the
     canonical .dialog-close (1.1rem/32px); 13px in the 22px chip read
     tiny (Petřin postřeh 2026-06-03). */
  color: var(--text-light);
  font: var(--fw-bold) 1rem / 1 var(--font-body);
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.song-filter__clear:hover,
.song-filter__clear:focus-visible {
  background: rgba(238, 240, 250, 0.2);
  outline: none;
}
.song-filter__clear[hidden] { display: none; }
/* The open track list — anchored under the capsule, left-aligned (the
   capsule sits left in the row; right-anchoring would overflow the panel
   edge). Same surface recipe as .pin-row-menu (dark surface + hairline +
   dropdown shadow); scrolls internally when the album outgrows the panel. */
.song-filter__menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40; /* above the feed cards inside the pane */
  margin: 0;
  list-style: none;
  min-width: 218px;
  /* Tall enough for the full 12-track album + "All songs" with NO scroll
     (Petřin pokyn 2026-06-03); the vh bound is a short-viewport safety. */
  max-height: min(70vh, 560px);
  overflow-y: auto;
  padding: 6px;
  /* Elevated surface (--cream-lifted, ADR-236) + stronger hairline — the
     plain --cream-surface read "black on dark" against the feed cards. */
  background: var(--cream-lifted);
  border: 1px solid rgba(238, 240, 250, 0.22);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-dropdown);
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Safety scroll (short viewports only) — thin dark thumb, never the
     chunky white UA scrollbar. */
  scrollbar-width: thin;
  scrollbar-color: rgba(238, 240, 250, 0.25) transparent;
}
.song-filter__menu::-webkit-scrollbar { width: 6px; }
.song-filter__menu::-webkit-scrollbar-thumb {
  background: rgba(238, 240, 250, 0.25);
  border-radius: 3px;
}
.song-filter__menu[hidden] { display: none; }
.song-filter__option {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.5rem 0.6rem; /* = .pin-row-menu__item rhythm */
  border: 0;
  background: transparent;
  color: var(--text-dark);
  font: var(--fw-medium) var(--fs-sm) / 1.3 var(--font-body);
  text-align: left;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 150ms ease;
}
.song-filter__option:hover,
.song-filter__option:focus-visible {
  background: rgba(238, 240, 250, 0.06);
  outline: none;
}
/* Every row reserves the check slot so track numbers stay column-aligned;
   only the chosen one shows it (song-identity teal). */
.song-filter__option::before {
  content: "✓";
  flex: 0 0 auto;
  font-weight: var(--fw-bold);
  visibility: hidden;
}
.song-filter__option.is-selected {
  color: var(--song-name);
}
.song-filter__option.is-selected::before {
  visibility: visible;
}

/* Desktop close (×) — collapses panel to the rail. M12 Noir PR4: now a
   round chip in the top bar (was an absolute bare × at top-right that
   collided with the Yours pending dot). Matches the unified .dialog-close
   circle so the close affordance reads the same across all windows. */
.events-panel__close {
  flex: 0 0 auto;
  /* Pin the × to the right edge regardless of whether the "Highlight my
     moments" toggle is present. When logged out the toggle is `hidden`, so
     justify-content:space-between would otherwise leave the lone × at
     flex-start (left). margin-left:auto keeps it right in both states. */
  margin-left: auto;
  appearance: none;
  width: 34px;
  height: 34px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(238, 240, 250, 0.06);
  border: 1px solid var(--border-on-light);
  border-radius: 50%;
  cursor: pointer;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--text-muted);
  transition: color 160ms ease, background 160ms ease, border-color 160ms ease;
}

.events-panel__close:hover,
.events-panel__close:focus-visible {
  color: var(--text-dark);
  background: rgba(238, 240, 250, 0.12);
  border-color: var(--border-strong-light);
  outline: none;
}
.events-panel__close:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* Desktop collapsed rail (48 px) — clickable surface that expands the
   panel. Petřin minimal variant per scope question 2026-05-14: just
   the ☰ icon + a couple of static mini-icons hinting at the panel's
   content. No counters, no live data. */
.events-panel__rail {
  background: transparent;
  border: none;
  width: 100%;
  height: 100%;
  /* Default hidden — only desktop sidebar in collapsed state shows the
     rail. Petřin postřeh 2026-05-16: rail vertical labels accidentally
     visible on iPad portrait (768 px = exact mobile boundary edge case).
     The explicit hide is defence-in-depth on top of the mobile
     @media (max-width: 767px) rule. */
  display: none;
  flex-direction: column;
  align-items: stretch;     /* children fill the full rail width */
  justify-content: flex-start;
  padding: 0;
  color: var(--text-muted);
  /* Container is no longer a button (per Petřin fixup) — child labels
     own click + hover semantics; no rail-level hover bg. */
}

.events-panel__rail-icon {
  font-size: 1.2rem;
  line-height: 1;
  font-weight: 700;
}

.events-panel__rail-mini {
  font-size: 0.95rem;
  line-height: 1;
  opacity: 0.75;
}

/* Vertical tab labels (AI-S1-M10D-037 + Petřin refinements 2026-05-15).
   Each label is a full-rail-width clickable strip with a bottom
   divider — hover fills the whole strip (ne jen text padding) +
   oddělovač i za poslední tab (Petra "oddělovač i za events").
   Same Manrope CAPS teal typography jako hlavní tabs. */
.events-panel__rail-label {
  appearance: none;
  background: transparent;
  border: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  flex-shrink: 0;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--font-body);
  font-size: 0.72rem;
  /* Manrope 800 + opacity 1 — parita s app-footer linky (PRIVACY · TERMS ·
     ABOUT) per Petřinou referencí 2026-05-15 odp. Předchozí 700 + opacity
     0.7 dělalo rail labels vizuálně faded vůči footer. */
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--border-on-light-strong-brand);
  white-space: nowrap;
  transition: background 160ms ease, color 160ms ease;
}
/* UX-AUDIT-009: hover wrapped, focus-visible separated so keyboard
   nav stays highlighted on touch devices too. */
@media (hover: hover) and (pointer: fine) {
  .events-panel__rail-label:hover {
    outline: none;
    background: rgba(var(--accent-turquoise-rgb), 0.12);
    color: var(--accent-turquoise);
  }
}
.events-panel__rail-label:focus-visible {
  outline: none;
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--accent-turquoise);
}
.events-panel__rail-label:active {
  background: rgba(var(--accent-turquoise-rgb), 0.20);
}

.events-panel__header {
  display: flex;
  border-bottom: 1px solid var(--border-on-light);
  padding: 0 0.5rem;
  flex-shrink: 0;
}

.events-panel__tab {
  /* M12 rollout — same styl jako popup meta line (Petřin pokyn 2026-05-14:
     "v záložkách stejné písmo + tučnost + velikost + barva jako v popupu
     u času a názvu místa"): Manrope 700 0.7rem caps + 0.10em tracking + teal.
     Hierarchy active/inactive přes opacity (= jednotný stylistický jazyk). */
  appearance: none;
  font-family: var(--font-body);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0.95rem 1.1rem;
  font-size: var(--fs-2xs);
  /* Handoff m12-list-items.md §0 — inactive tabs Manrope 500, --text-muted;
     active = teal 700 + gradient underline (rule below). */
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  color: var(--text-muted);
  letter-spacing: var(--ls-loose);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  /* Plná opacity 1 — parita s app-footer linky (PRIVACY · TERMS · ABOUT).
     Petřin reference 2026-05-15 odp.: opacity fade na turquoise dělal tabs
     vizuálně tenké. Hierarchie active vs inactive drží color (text-dark
     vs turquoise) + gradient bar pod active. */
  transition: background 160ms ease, color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .events-panel__tab:hover {
    background: var(--accent-turquoise-tint);
  }
}

.events-panel__tab.is-active {
  /* Handoff m12-list-items.md §0 — active tab = teal text 700 + 2px
     gradient underline (teal→iris) with a teal glow. */
  color: var(--accent-turquoise);
  font-weight: 700;
  opacity: 1;
  border-bottom-color: transparent;
  position: relative;
}
.events-panel__tab.is-active::after {
  content: "";
  position: absolute;
  left: 0.5rem; right: 0.5rem; bottom: -1px;
  height: 2px;
  /* M12 Noir 2026-05-28 — solid teal underline (matches the active tab text);
     the old teal→violet gradient + strong glow read as a frosty/rainbow leftover. */
  background: var(--accent-turquoise);
  border-radius: 1px;
  box-shadow: 0 0 6px rgba(var(--accent-turquoise-rgb), 0.25);
}

.events-panel__tab:focus-visible {
  outline: 2px solid var(--accent-violet-bright);
  outline-offset: -2px;
  border-radius: 4px;
}

.events-panel__body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.events-panel__pane {
  padding: 0.5rem 0 1rem;
}

.events-panel__pane[hidden] {
  display: none;
}

/* Concert + Online "add" CTA row (AI-S1-M10D-032 / 034).
   Stacked vertically on narrow rails; full-width buttons either way. */
.events-panel__cta-row {
  padding: 0.75rem 0.75rem 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

/* M12 Noir PR4 (mockups 21/22 + Petřin screenshoty 2026-05-24) — the two
   "add" CTAs differ by world AND by treatment:
     • Concert = teal GHOST (transparent fill, teal border + teal text)
     • Online  = filled azure (.events-panel__cta-btn--primary)
   Both: rounded rect, UPPERCASE, leading "+" (CSS ::before; the emoji was
   removed from the i18n labels). The base rule IS the concert ghost;
   --primary paints the online azure over it. */
.events-panel__cta-btn {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-lg);
  /* Handoff m12-list-items.md §5 (Petřin pokyn 2026-05-24 "snížit barevnost,
     nejde přes to vidět popisek") — the ADD CTA is a DARK button (subtle
     world wash → navy), NOT a saturated fill. The label is the world colour
     and reads cleanly on the dark surface. Concert = teal, online = azure. */
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.33);
  background: linear-gradient(180deg, rgba(var(--accent-turquoise-rgb), 0.22), var(--navy-soft));
  color: var(--accent-turquoise);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  box-shadow: 0 0 18px rgba(var(--accent-turquoise-rgb), 0.13);
  transition: transform 180ms ease, box-shadow 180ms ease, background 180ms ease;
}
.events-panel__cta-btn::before {
  content: "+";
  font-size: 1.15em;
  font-weight: 700;
  line-height: 1;
}

/* Online primary — DARK CTA (handoff §5: subtle world wash → navy, NOT a
   saturated fill), azure variant of the concert/moment dark buttons. Online
   world = azure (Petřin pokyn 2026-05-31, was violet). Contrast-only fix
   (Petřin pokyn 2026-05-31 "měl jsi jen zesvětlit popisek nebo udělat větší
   kontrast, ne měnit vzhled"): --accent-online-deep alone was too light under
   the bright-azure label, so the gradient top is darkened toward navy. Same
   dark-button shape + bright azure label as before — only the contrast lifts. */
.events-panel__cta-btn--primary {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent-online-deep) 58%, var(--navy-soft)),
    var(--navy-soft));
  border-color: rgba(var(--accent-online-rgb), 0.33);
  color: var(--accent-online-bright);
  box-shadow: 0 0 18px rgba(var(--accent-online-rgb), 0.12);
}

.events-panel__cta-btn:hover,
.events-panel__cta-btn:focus-visible {
  outline: none;
  border-color: rgba(var(--accent-turquoise-rgb), 0.55);
  box-shadow: 0 0 22px rgba(var(--accent-turquoise-rgb), 0.22);
}
.events-panel__cta-btn:focus-visible {
  outline: 2px solid var(--accent-turquoise-bright);
  outline-offset: 2px;
}
@media (hover: hover) and (pointer: fine) {
  .events-panel__cta-btn:hover {
    transform: translateY(-1px);
  }
}

.events-panel__cta-btn--primary:hover,
.events-panel__cta-btn--primary:focus-visible {
  border-color: rgba(var(--accent-online-rgb), 0.55);
  box-shadow: 0 0 22px rgba(var(--accent-online-rgb), 0.22);
}
.events-panel__cta-btn--primary:focus-visible {
  outline-color: var(--accent-online-bright);
}

/* Moment magenta variant — "Your moment" world (Petřin pokyn 2026-05-27:
   „pro moments máme magentu na pebble"). Same dark-CTA DNA as concert/online,
   painted in the magenta moment identity (--accent-mine) so the Moments tab
   add-CTA matches the magenta moment pebble instead of the teal concert hue. */
.events-panel__cta-btn--mine {
  background: linear-gradient(180deg, rgba(var(--accent-mine-rgb), 0.22), var(--navy-soft));
  border-color: rgba(var(--accent-mine-rgb), 0.33);
  color: var(--accent-mine);
  box-shadow: 0 0 18px rgba(var(--accent-mine-rgb), 0.13);
}
.events-panel__cta-btn--mine:hover,
.events-panel__cta-btn--mine:focus-visible {
  border-color: rgba(var(--accent-mine-rgb), 0.55);
  box-shadow: 0 0 22px rgba(var(--accent-mine-rgb), 0.22);
}
.events-panel__cta-btn--mine:focus-visible {
  outline-color: var(--accent-mine-bright);
}

/* M15 [UX] (Petřin pokyn 2026-06-06) — ADR-137 pin-gate disabled the
   propose CTA but no :disabled style existed, so the button looked
   active yet silently swallowed clicks ("broken button"). CANONICAL
   grey disabled for dark world-CTAs (ui-standards §2) — label =
   tertiary --text-light-muted (~6.5:1, "recedes, never vanishes",
   HARD RULE 14; Petra: slightly lower contrast than --text-muted).
   The gate sets aria-disabled (NOT `disabled`) so the tap still fires
   and the reason shows as a toast on mobile — hence the [aria-disabled]
   selectors + explicit :hover neutralisation (the button is live to
   the browser). Listed per variant to beat the variant hover rules. */
.events-panel__cta-btn:disabled,
.events-panel__cta-btn[aria-disabled="true"],
.events-panel__cta-btn[aria-disabled="true"]:hover,
.events-panel__cta-btn--primary:disabled,
.events-panel__cta-btn--primary[aria-disabled="true"],
.events-panel__cta-btn--primary[aria-disabled="true"]:hover,
.events-panel__cta-btn--mine:disabled,
.events-panel__cta-btn--mine[aria-disabled="true"],
.events-panel__cta-btn--mine[aria-disabled="true"]:hover {
  cursor: not-allowed;
  transform: none;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--text-muted) 10%, var(--navy-soft)),
    var(--navy-soft)
  );
  border-color: color-mix(in srgb, var(--text-muted) 28%, transparent);
  color: var(--text-light-muted);
  box-shadow: none;
}

/* Composite empty state — shown when LIVE + UPCOMING + Past are all
   empty (per P-019). Replaces three lonely section headers with one
   clear explainer + optional anon sign-in nudge. */
.events-empty-composite {
  margin: 0.4rem 0.75rem 1rem;
  padding: 1.2rem 1rem 1.4rem;
  text-align: center;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
}
.events-empty-composite__icons {
  font-size: 1.8rem;
  margin-bottom: 0.5rem;
  letter-spacing: 0.15em;
}
.events-empty-composite__title {
  margin: 0 0 0.35rem;
  font-family: var(--font-display, "Cormorant Garamond", serif);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text-dark);
}
.events-empty-composite__body {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.45;
}
/* Anon nudge — text-link look (per Petřin pokyn 2026-05-15: má vypadat
   jako odkaz, ne jako tlačítko s hover-bg; podtržení VŽDY, ne jen hover).
   Stále <button> kvůli sémantice (= akce, ne navigace) + accessibility. */
.events-empty-composite__nudge {
  appearance: none;
  display: inline;
  margin: 0.65rem 0 0;
  padding: 0;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent-turquoise);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color 180ms ease, text-decoration-color 180ms ease;
}
.events-empty-composite__nudge:hover,
.events-empty-composite__nudge:focus-visible {
  outline: none;
  color: var(--accent-violet-soft);
}

/* Events sub-section headers (LIVE NOW / UPCOMING / Past). */
.events-section {
  padding: 0.4rem 0;
}

.events-section__title {
  /* M12 Noir PR4 (mockups 21/22, Petřin pokyn 2026-05-24) — section labels
     are clean Manrope CAPS (same family + tracking as the panel tabs, so
     they read as one system), teal, with a muted count next to the word.
     Was Cormorant serif + a leading ⏳/🔴 emoji. */
  margin: 0;
  padding: 0.7rem 0.85rem 0.45rem;
  font-family: var(--font-body);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--ls-widest);
  color: var(--accent-turquoise);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

/* Count next to a section label (UPCOMING (7) / STREAMS) — muted, sits
   right after the word per mockups 21/22. */
.events-section__count {
  /* Count matches the section-label tone (currentColor), not grey
     (Petřin pokyn 2026-05-24 "počet je šedý"). */
  color: currentColor;
  opacity: 0.8;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* M12 Noir PR4 (Petřin pokyn 2026-05-24 "UPCOMING má mít oranžový indikátor
   podle naší palety") — small amber status dot before the UPCOMING label,
   with a soft glow (same glow language as the card status dots). Reuses the
   --status-upcoming token. */
/* Handoff m12-list-items.md §6 — section-label dot inherits the label
   tone via currentColor (bg + glow), 6px. */
.events-section__dot--upcoming {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  background: currentColor;
  box-shadow: 0 0 8px currentColor;
}

/* Handoff §6 + Petřin pokyn 2026-05-24 ("UPCOMING oranžové, ne fialové") —
   the UPCOMING label is gold/amber on BOTH tabs (dot + count follow via
   currentColor). Was: online wrongly iris. */
.events-section--upcoming .events-section__title {
  color: var(--accent-copper);
}

.events-section__dot--live {
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24 "LIVE NOW nemá být červené") —
     mint CSS dot (was a red 🔴 emoji). font-size:0 collapses the emoji glyph. */
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  font-size: 0;
  background: var(--live);
  box-shadow: 0 0 8px var(--live);
  animation: online-live-badge-pulse 1.6s ease-in-out infinite;
}

.events-section--live .events-section__title {
  color: var(--live);
}

.events-section__dot--today {
  /* M12.X-STATUS-UNIFY (Petřin pokyn 2026-05-25 "dnešní koncerty mají stejnou
     váhu jako live") — TODAY dot shares the LIVE green + glow so the section
     carries equal weight, but drops the live pulse: a date-only concert is
     happening today, not streaming live this second. font-size:0 is harmless
     (the dot span is empty) and keeps it interchangeable with the live dot. */
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  font-size: 0;
  background: var(--live);
  box-shadow: 0 0 8px var(--live);
}

.events-section--today .events-section__title {
  color: var(--live);
}

.events-section__past-toggle {
  appearance: none;
  font-family: inherit;
  background: transparent;
  border: none;
  cursor: pointer;
  width: 100%;
  padding: 0.55rem 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  text-align: left;
  transition: color 180ms ease;
}

.events-section__past-toggle:hover {
  color: var(--text-dark);
}

.events-section__past-toggle .events-section__icon {
  transition: transform 200ms ease;
  display: inline-block;
}

.events-section__past-toggle[aria-expanded="true"] .events-section__icon {
  transform: rotate(90deg);
}

.events-section__past-count {
  margin-left: auto;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Generic feed list-item shared by Pins + Events. */
.feed-list {
  list-style: none;
  margin: 0;
  /* M12 Noir PR4 (mockups 20–23) — list is now a stack of cards with a
     gap between them and side gutters, not edge-to-edge zebra rows. */
  padding: 0.35rem 0.7rem 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* M12 Noir PR4 — each row is a self-contained card: raised dark surface,
   hairline border, rounded corners (mockups 20–23). Zebra striping +
   the bottom-divider row treatment are retired. `position: relative`
   anchors the mine stripe + the absolutely-positioned inline actions. */
.feed-list__item {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.8rem 0.85rem;
  /* Handoff m12-list-items.md §1 .fm-list-card (Petřin pokyn 2026-05-24
     "boxy jsou světlejší, ne navy") — card = a faint LIGHT wash that lifts
     off the dark panel, NOT a navy fill. Subtle top-down gradient + hairline. */
  background: linear-gradient(180deg, rgba(238, 240, 250, 0.055) 0%, rgba(238, 240, 250, 0.02) 100%);
  border: 1px solid var(--hairline-dark-lo);
  border-radius: var(--radius-md);
  cursor: pointer;
  position: relative;
  transition: background 160ms ease, border-color 160ms ease,
              transform 160ms ease, box-shadow 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .feed-list__item:hover {
    background: var(--cream-mist);
    border-color: var(--border-strong-light);
    transform: translateY(-1px);
    box-shadow: var(--shadow-overlay-bar);
  }
}

.feed-list__item:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
}

/* ---- Per-world accent (M12 Noir PR4, Petřin pokyn 2026-05-24) ----
   Concert world = teal, online world = azure (Petřin pokyn 2026-05-31).
   Scoped by pane so the row markup stays untouched. Date colour echoes
   the world. */
#events-panel-pane-events .feed-list__when {
  color: var(--accent-turquoise);
}
#events-panel-pane-online .feed-list__when {
  color: var(--accent-online-bright);
}

/* Handoff m12-list-items.md §3/§4 (Petřin pokyn 2026-05-24 "indikátory
   koncertů, ty tečky pryč") — concert/online cards carry NO per-row status
   dot; status is conveyed by the UPCOMING/PAST section grouping (+ a LIVE
   pill on online, structural pass). Removes the green/orange/red dots. */
#events-panel-pane-events .feed-list__status-dot,
#events-panel-pane-online .feed-list__status-dot {
  display: none !important;
}

/* Leading icon-tile (concert music note / online platform glyph). */
.feed-list__icon-tile {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.feed-list__icon-tile svg,
.feed-list__icon-tile .platform-icon {
  width: 18px;
  height: 18px;
  /* ADR-255 — list icon-tiles keep the WORLD tone (concert teal / online
     azure set on the tile), NOT the per-platform tint: in lists the tile
     answers "what kind of event", the platform identity lives on the CTA
     line + detail surfaces. currentColor pins the glyph to the tile colour
     (the tile carries data-platform, which would otherwise recolour it). */
  color: currentColor;
}
.feed-list__icon-tile--concert {
  /* Handoff m12-list-items.md §3 — teal gradient glyph tile + teal glow. */
  background: linear-gradient(180deg, rgba(var(--accent-turquoise-rgb), 0.20), var(--accent-turquoise-deep));
  color: var(--accent-turquoise-bright);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.40),
              0 0 10px rgba(var(--accent-turquoise-rgb), 0.18);
}
.feed-list__icon-tile--online {
  /* Handoff m12-list-items.md §4 — azure gradient glyph tile + azure glow
     (online world = azure, Petřin pokyn 2026-05-31, supersedes iris). */
  background: linear-gradient(180deg, rgba(var(--accent-online-rgb), 0.20), var(--accent-online-deep));
  color: var(--accent-online-bright);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-online-rgb), 0.40),
              0 0 10px rgba(var(--accent-online-rgb), 0.18);
}
/* M17.X-ONLINE-PLATFORM-TILES (local experiment) — the big online list tile
   shows the hub's brand-coloured platform glyph (platformIconSvg WITHOUT mono)
   in a platform-tinted tile: the bigger feed analogue of .hub-link__ic. Mirrors
   the per-tour concert tiles (concert = tour colour, online = platform colour).
   [data-platform] resolves --platform, which tints both the tile and the
   currentColor glyphs; gradient-shaded glyphs (instagram, …) keep their own
   brand gradient. Glyph is 22px — bigger than the 18px world-tile glyph. The
   small per-platform icon on the CTA row is unified to one "open" glyph instead
   (Petřin pokyn 2026-06-09). Unknown platform / no URL → azure --online tile. */
.feed-list__icon-tile--platform {
  color: var(--platform, var(--text-muted));
  background: color-mix(in srgb, var(--platform, var(--text-muted)) 18%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--platform, var(--text-muted)) 34%, transparent),
              0 0 10px color-mix(in srgb, var(--platform, var(--text-muted)) 16%, transparent);
}
.feed-list__icon-tile--platform .platform-icon { width: 22px; height: 22px; }
/* M17.X-TOUR-TILES (local experiment, not yet formalised) — per-tour coloured
   monogram tile. The date-sorted feed was a wall of identical teal note tiles;
   giving each tour its own colour + initials breaks the monotony AND signals
   tour membership at a glance. Colour = deterministic hash of the tour id → one
   of eight curated palette tokens (same tour = same colour everywhere, no
   storage). The shared --tour rule derives gradient/ring/glow/monogram tone from
   a single base so all eight read as one family — same visual language as the
   --concert / --online tiles. Standalone (tour-less) concerts keep the canonical
   --concert note tile (eventsPanel.js). Hex values live here as the token table;
   formalisation into named :root tokens + ADR follows Petřin approval. */
.feed-list__icon-tile--tour-0 { --tour-c: #5fb0e8; }  /* azure      */
.feed-list__icon-tile--tour-1 { --tour-c: #ad77f2; }  /* violet     */
.feed-list__icon-tile--tour-2 { --tour-c: #e88aae; }  /* rose       */
.feed-list__icon-tile--tour-3 { --tour-c: #d4a04a; }  /* gold       */
.feed-list__icon-tile--tour-4 { --tour-c: #4fd0c4; }  /* teal       */
.feed-list__icon-tile--tour-5 { --tour-c: #7bd88f; }  /* green      */
.feed-list__icon-tile--tour-6 { --tour-c: #f0956b; }  /* coral      */
.feed-list__icon-tile--tour-7 { --tour-c: #9d8df0; }  /* periwinkle */
.feed-list__icon-tile--tour {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--tour-c) 26%, transparent),
    color-mix(in srgb, var(--tour-c) 55%, var(--cream-base)));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tour-c) 48%, transparent),
              0 0 10px color-mix(in srgb, var(--tour-c) 22%, transparent);
}
.feed-list__icon-monogram {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-md);
  line-height: 1;
  letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--tour-c) 30%, var(--text-light));
}

/* (Removed 2026-05-27: the icon-tile counter-saturate band-aid is gone now that
   the parent `.feed-list__item--past` no longer desaturates the whole row — the
   tile keeps its world colour on its own.) */

.feed-list__icon {
  font-size: 1.1rem;
  line-height: 1.1;
  flex-shrink: 0;
  margin-top: 0.1rem;
}

/* Avatar icon variant — used by the Pins tab to render the pin's own
   avatar (per-pin choice for anon, profile avatar for registered users)
   instead of a generic 📍. Per Petřin feedback 2026-05-14.
   Glow filter is applied by the shared "Avatar unification" rule below
   (ADR-192). */
.feed-list__avatar {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 50%;
  object-fit: contain;
}

/* === Avatar unification (ADR-192, Petřin pokyn 2026-05-28) ===========
   Single per-avatar glow rule for every dark Noir surface — Moments
   feed, popups (header + participant stack), mobile detail sheets
   (profile / gathering / moment), and the registered Add Moment
   identity preview. Mockup proof: 19-popup-fan-gathering.png,
   11-detail-gathering.png, 12-detail-profile.png — all show bare orb
   + each avatar's own light glow, no plate, no ring.

   --av-glow is emitted inline by avatarSvg() (avatars.js); fallback =
   cool silver so unmapped/legacy avatars don't render unstyled.

   Light/Frost cream surfaces intentionally opt out — the drop-shadow
   doesn't read on cream, and the avatar's baked gradient is enough:
     • avatarPicker.js (anon picker, cream Frost dialog)

   NOTE 2026-05-31 (Petřin pokyn): the admin Moderators/"crew" surfaces —
   the Add dialog search dropdown AND the crew list table — are NOW dark
   Noir (PR6 Dávka E recoloured these dialogs from cream → dark), so the
   original cream opt-out no longer applies. They get the glow like every
   other dark surface; the crew list avatar was also newly added here.

   NOTE 2026-06-01 (Petřin pokyn): same story for the public-profile modal
   — the M12 Noir swap redefined --cream-surface to dark Noir ink, so the
   "cream modal" opt-out for .public-profile__avatar was stale and the
   avatar rendered with no halo. Added to the glow list below.

   NOTE 2026-06-01 (Petřin pokyn, 2nd): the mobile hamburger hero card avatar
   (.app-menu__hero-avatar) was never in this list, so the @username header
   in the bottom-sheet menu showed no halo. Target ` > svg` because mobileMenu
   injects avatarSvg() (which carries the inline --av-glow) inside the span.
   ====================================================================*/
.feed-list__avatar,
.public-profile__avatar,
.app-menu__hero-avatar > svg,
.app-menu__hero-avatar > img.fm-avatar-photo,
.pin-popup__avatar,
.pin-popup__participant-avatar,
.pin-popup__preview-avatar,
.fm-profile-header__avatar,
.fm-gathering-header__avatar,
.fm-moment-detail__avatar,
.fm-attendance__avatar:not(.fm-attendance__avatar--placeholder),
.rsvp__avatar:not(.rsvp__avatar--placeholder),
.admin-moderators__suggestion-avatar,
.admin-moderators__row-avatar,
.avatar-toggle--inline .avatar-toggle__preview,
#pin-form .avatar-toggle--preview-only .avatar-toggle__preview,
.pin-form__gathering-avatar svg,
.pin-form__gathering-avatar img.fm-avatar-photo,
.fm-moment-row__glyph--avatar svg,
.fm-moment-row__glyph--avatar img.fm-avatar-photo {
  filter: drop-shadow(0 0 4px rgba(var(--av-glow, 221, 227, 244), 0.8))
    drop-shadow(0 0 7px rgba(var(--av-glow, 221, 227, 244), 0.42));
}

/* M13N — photo (raster) avatars. The WebP is pre-cropped to a transparent-
   cornered circle with a baked ring, so no border-radius/clip is needed; the
   per-avatar --av-glow halo comes from the container drop-shadow rule above
   (or the svg-equivalent selectors just extended). inline-block + middle
   alignment matches how the sprite <svg> sits inside feed rows / headers. */
img.fm-avatar-photo {
  display: inline-block;
  vertical-align: middle;
  /* The WebP is a self-contained circular avatar (baked transparent-cornered
     circle + thin ring), so it must NOT inherit the sprite-oriented container
     frames — e.g. .public-profile__avatar's 1px light border or the mobile
     .fm-profile-header__avatar 2px magenta ring — which read as a hard double
     edge on a photo. element+class out-ranks those single-class container
     rules, so border/background drop wherever the class sits on the <img>. */
  border: 0;
  background: transparent;
}

/* ADR-192 — real-attendee sprite override for the attendance summary stack.
   .fm-attendance__avatar base rule (mobile.css) is the mood-tone placeholder
   variant (1.5px border + inline background). For the sprite variant the
   border + inline tint don't apply, so strip them and let the avatar's own
   baked gradient + the unification glow above carry the look. */
.fm-attendance__avatar:not(.fm-attendance__avatar--placeholder) {
  border: 0;
  background: transparent;
}

/* Tiny opt-in nationality flag next to the nick in the title row.
   Per M10D Task #12 (Petřin pokyn 2026-05-14): registered author's
   chosen nationality, rendered as a subtle 14×10 px flag pravo od
   nicku. Anon pins + opted-out users skip this entirely (no fallback).
   Decentní — pomáhá komunitnímu pocitu, ne dominantní. */
.feed-list__nationality {
  /* Petřin pokyn 2026-05-21: flag ZA username (= vlajky uživatelů
     byly vždy za jménem; vlajky pinů jsou před lokalitou). margin-left
     odděluje od @nick textu vlevo. */
  width: 14px;
  height: 10px;
  margin-left: 0.4rem;
  object-fit: cover;
  border-radius: 1px;
  vertical-align: middle;
  flex-shrink: 0;
  display: inline-block;
}

.feed-list__body {
  flex: 1 1 auto;
  min-width: 0;
}

.feed-list__title {
  /* M12 rollout — Cormorant 700 Bold + violet 1.1rem. Same as
     .pin-popup__nick + .pin-popup__kotva-name (Petřin pokyn 2026-05-14:
     "username musí být větší a tučné na obou místech"). */
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: 0.02em;
  line-height: 1.2;
  color: var(--nick-color);
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* Event title in panel row — M12 Noir PR4 (mockups 21/22, Petřin per-world
   princip 2026-05-24): card titles are white Cormorant italic across both
   worlds; the world identity (teal concert / violet online) is carried by
   the icon-tile + date accent, not the title colour. Plain text (NOT a
   link) — row click focuses the map / opens the detail. */
.feed-list__title--event {
  /* Handoff §3/§4 — event card title = Cormorant italic 600, ~16px white
     (designer's proposed size, restored per Petřin pokyn 2026-05-24). */
  color: var(--text-dark);
  font-style: italic;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-base);
}

/* Online card description preview (Petřin pokyn 2026-05-24) — clamped, muted. */
.feed-list__event-desc {
  margin: 0.3rem 0 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Online card primary action — links out to the live event (ADR-171).
   Replaces the old UPPERCASE "host line" label (channel/host name was
   unreliable data → duplicates / placeholders) with a platform-aware action
   verb ("Open on YouTube" / "Join Discord") + chevron. Stays the QUIET muted
   grey of the original host line per designer directive (Petřin pokyn
   2026-05-25) — NOT a per-world violet accent; this line reads as a quiet
   secondary action, not a coloured link. Chevron nudges on hover. */
.feed-list__platform-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  width: fit-content;
  margin: 0.3rem 0 0;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  /* M17.X-ONLINE-CTA-UNIFY — label + arrow stay light (Petřin pokyn 2026-06-09:
     all-teal text read as "omalovánka"); only the open-link icon is teal. */
  color: var(--text-muted);
  text-decoration: none;
  cursor: pointer;
}
.feed-list__platform-cta:hover,
.feed-list__platform-cta:focus-visible {
  outline: none;
  color: var(--text-dark);
}
.feed-list__platform-cta-arrow {
  transition: transform 140ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .feed-list__platform-cta:hover .feed-list__platform-cta-arrow {
    transform: translateX(2px);
  }
}
.feed-list__platform-cta .platform-icon {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  /* ADR-255 — glyph wears the platform tone; CTA text stays muted. */
  color: var(--platform, currentColor);
}
/* M17.X-ONLINE-CTA-UNIFY — unified open-link glyph on the online CTA (replaces
   the per-platform icon; the big tile carries platform identity now). Teal =
   the app's interactive/clickable tone, so the "open" mark reads as a live
   affordance instead of a dead muted glyph (Petřin pokyn 2026-06-09). */
.feed-list__cta-open-ic {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  /* Bright teal (not the mid base) so the small glyph reads vivid, not matte. */
  color: var(--accent-turquoise-bright);
}

/* Pin content (comment / kotva name / custom label / fallback location).
   Per Petřin design 2026-05-14: 3-line wrap, not aggressive truncation.
   `-webkit-line-clamp` is widely supported (Chromium / Safari / Firefox 68+);
   without it, falls back to native overflow + ellipsis on a single line. */
/* ==========================================================================
   ECHO SONG TAG (Surface 1, M13.X-FRISSON-JOURNEY / ADR-217)
   A left-aligner above an Echo's message: 2px rule + "♪ Song title", both in
   --song-name (luminous teal-bright — Petřin výběr 2026-06-03; the original
   pale azure read flat on noir). Distinct from the iris-nick above; the muted
   teal feed LOCATION label is far quieter, so they don't collide. ONE shared
   element (songLabel.js) across the feed, desktop popup and mobile sheet.
   ========================================================================== */
/* Tagged block = the song caption + the Echo message share ONE 2px rule down
   their left edge, so the rule binds the song to the words across the WHOLE
   comment (Petřin pokyn 2026-06-02), not just the caption. The author nick +
   location/like/date stay OUTSIDE the rule. */
.echo-tagged {
  border-left: 2px solid var(--song-name);
  padding-left: 10px;
  margin: 0.35rem 0 0.1rem;
}
.echo-song {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 0.2rem;
  /* JetBrains Mono ships only its 600 cut here — keep the weight literal so
     the pair (font + weight + size) stays one decision (Petřin výběr
     2026-06-03; size postupně 0.92 → 0.85 → 0.8 → 0.75rem). --font-song =
     ONLY this echo tag; an app-wide sweep (chart/journey/rating/profile/
     filter) was tried 2026-06-03 and reverted same day — Cormorant italic
     stays canonical elsewhere. */
  font-family: var(--font-song);
  font-size: 0.75rem;
  font-weight: 600;
  /* Mono space = a full character cell → word gaps read huge. Pull them back
     to proportional-text rhythm (render-verified -0.35ch, Petřin bug 2026-06-03). */
  word-spacing: -0.35ch;
  line-height: 1.2;
  color: var(--song-name);
}
.echo-song .icon {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  color: var(--song-name);
}
.echo-song__name {
  min-width: 0;
  overflow-wrap: anywhere;
}
/* M13.X-FEED-SONG-RATING (ADR-249) — the author's own rating of the tagged
   song, as a passive ★N indicator after the title (raw 1..20 from the 20-star
   budget — same language as the profile + Chart, Petřino rozhodnutí
   2026-06-04). Canonical rating identity = gold star + number
   (.public-profile__song-star pattern) at the tag's small scale. nowrap keeps
   the pair intact when a long title wraps; AFTER `.echo-song .icon` in source
   so the gold + 11px win over the tag's teal 13px for the star sprite. */
.echo-song__stars {
  white-space: nowrap;
  margin-left: 6px;
  /* Counter gold, NOT --warning — flat #d4a04a read like the TEST badge
     (Petřin pokyn 2026-06-04). Number = the Songs tab counter language
     (.fm-tab-count): copper-bright digits + soft copper glow. */
  color: var(--accent-copper-bright);
  text-shadow: 0 0 6px rgba(var(--accent-copper-rgb), 0.6);
}
.echo-song__stars .icon {
  width: 11px;
  height: 11px;
  margin-right: 3px;
  vertical-align: -1.5px;
  /* Plastic depth = #star-shade-gold gradient, carried as the inline path's
     fill attribute in songLabel.js (a sprite <use> can't take a CSS fill —
     see STAR_SVG comment there). No color/fill needed here. */
}
/* 2026-06-03 evening (Petřin pokyn) — FEED rows only: the song-tagged block
   gets the same extra air under the author line as the bare comment above
   (0.35 → 0.55rem, same +3px delta as .feed-list__pin-text 0.4 → 0.6rem).
   Popup / sheet keep the shared base margin. */
.feed-list__item--pin .echo-tagged {
  margin-top: 0.55rem;
}
/* 2026-06-03 night (Petřin pokyn, 2 iterace) — FEED rows only: a song-tagged
   Echo packs the caption + message tight under the left rule, so the location
   line below needs a touch more air — AND it sits nudged DOWN toward the
   heart row (more air above, less below), so it reads as part of the meta
   block, not a tail of the message. */
.feed-list__item--pin .echo-tagged + .feed-list__meta--stacked {
  margin-top: 0.45rem;
}
.feed-list__item--pin .echo-tagged + .feed-list__meta--stacked
  .feed-list__meta-row--left:has(.feed-list__where)
  + .feed-list__meta-row--right:has(.echo-like) {
  margin-top: 0.05rem;
}
/* The message sitting inside the tagged block keeps its own type; drop its top
   margin so it hugs the caption under the shared rule. */
.echo-tagged > .feed-list__pin-text,
.echo-tagged > .pin-popup__preview-text,
.echo-tagged > .fm-moment-row__name,
.echo-tagged > .fm-moment-detail__note {
  margin-top: 0;
}

.feed-list__pin-text {
  /* M12 (Petřin pokyn 2026-05-27) — the moment description now wraps the
     shared `text-expandable` (More/Less), so the 3-line clamp + overflow
     moved INTO `.text-expandable--clamped .text-expandable__body`. This
     element is just a styling wrapper now; clamping it here too would clip
     the More/Less toggle. Petřin pokyn 2026-05-15 odp.: top margin
     keeps the row off the @username underline above. 2026-06-03 evening
     (Petřin pokyn) — bumped 0.4 → 0.6rem, the comment sat too tight under
     the author line (most visible on song-tagged rows). */
  margin: 0.6rem 0 0;
  color: var(--text-dark);
  word-break: break-word;
  /* Moment font (roman/handwriting switch) is owned by the higher-specificity
     `.feed-list__item--pin .feed-list__pin-text(--hand)` rules below — pin
     text only ever renders inside a pin item, so setting it here too would be
     dead/overridden CSS. */
}

/* M12 Noir PR4 (mockup 20) — in the Moments list the MOMENT TEXT is the
   prominent italic Cormorant line; the @username sits ABOVE it as a smaller
   label. Code had it inverted (username big, content small). Pin-scoped so
   event rows keep the event title as their prominent line. */
.feed-list__item--pin .feed-list__title {
  /* M12 Noir per handoff spec m12-list-items.md §2 — moment username =
     Manrope 700 13px, iris (violet) by default, magenta when it's the
     viewer's own moment. */
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 700;
  font-style: normal;
  letter-spacing: 0.01em;
  color: var(--nick-color);
}
.feed-list__item--mine .feed-list__title {
  color: var(--accent-mine);
}

/* Handoff m12-list-items.md §2 — @username + flag + TEST on one inline row. */
.feed-list__pin-titleline {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* Petřin pokyn 2026-05-25 — TEST čip = JEDEN teplý zlatý token napříč celou
   appkou. Dřív byl panel TEST čip šedý dashed mist outline (handoff
   m12-list-items.md §7), zatímco popup verze byla zlatá → vizuální rozpor.
   Override teď nese JEN velikost/mezery pro panel; barvu (gold fill + border +
   text) dědí z kanonického .test-data-pill (--warning), aby všechno
   test-related sdílelo stejný token. */
/* M12.X-GATHERING-CUE (Petřin pokyn 2026-05-31) — the feed GATHERING chip
   shares the TEST pill's EXACT dimensions so the whole chip row reads as one
   system (Petřin pokyn 2026-05-25 — 0.56rem / 0.10em / 0.1rem 0.5rem, aligned
   to the .chip baseline). One shared rule = no drift between the two chips.
   margin-left:0 keeps alignment with the other chips in the row. The GATHERING
   chip's iris tone comes from .chip-uppercase--iris (= soft variant, legible on
   noir); the .feed-list__item scope also outranks the base .chip-uppercase
   padding/size regardless of source order. */
.feed-list__item .test-data-pill,
.feed-list__item .feed-list__gathering-chip {
  font-size: 0.56rem;
  font-weight: var(--fw-bold);
  letter-spacing: 0.10em;
  line-height: 1.5;
  padding: 0.1rem 0.5rem;
  margin-left: 0;
}
/* Řádek pinu (@username + vlajka + chipy) — odsadit první chip víc od vlajky
   než základní gap titleline (Petřin pokyn 2026-05-25). */
.feed-list__pin-titleline .test-data-pill,
.feed-list__pin-titleline .feed-list__gathering-chip {
  margin-left: 0.35rem;
}
/* M13.X-FEED-OPEN-CHIP (Petřin pokyn 2026-06-04) — an OPEN gathering's chip
   flips to the --live tone ("OPEN GATHERING"); same dimensions (rule above),
   same teal as the popup .pin-popup__open-badge + the map-legend sonar ring
   ("Open" is a STATUS, never an ownership signal). Closed gatherings keep
   the muted grey chip untouched. */
.feed-list__gathering-chip--open {
  --chip-tone: var(--live);
  --chip-tone-rgb: var(--live-rgb);
}
/* M17.X-GATHERING-REPLIES — the gathering's total Echo count appended to the
   chip ("Gathering · 3"), mirroring the map marker badge. Inherits the chip's
   own tone + weight so it reads as one uniform chip — identical on EVERY
   gathering, no highlight/dim variation (Petřin pokyn 2026-06-08: "tečka a
   číslo, ne zašedávání"). The "new replies" signal lives on the tab dot only. */
.feed-list__gathering-count {
  margin-left: 0.28em;
}
/* M17.X-GATHERING-REPLIES — the owner's "new replies" dot: a small mint dot
   added BEFORE the chip word. It does NOT restyle the chip or number — the
   gathering is not made more prominent, it just gets one extra dot (Petřin
   pokyn 2026-06-08 "prostě tečka navíc, žádné zašedávání"). Same mint --live +
   soft halo as the tab live-dot, so the cue belongs to the same system. */
.feed-list__gathering-newdot {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 0.32em;
  border-radius: 50%;
  background: var(--live);
  box-shadow: 0 0 6px 1px rgba(var(--live-rgb), 0.85);
  vertical-align: middle;
}
.feed-list__item--pin .feed-list__pin-text {
  /* M12 experiment (2026-05-27, Petřin nápad) — long moments = calm roman
     serif (Lora). Replaces the old Cormorant italic, which read too thin on
     dark. Short moments switch to the handwriting voice via --hand below.
     (Higher specificity than the generic .feed-list__pin-text wrapper, so the
     moment-font switch must live at THIS scope to win.) */
  font-family: var(--font-moment-long);
  font-style: normal;
  font-size: var(--fs-moment-roman);
  font-weight: var(--fw-regular);
  line-height: var(--lh-moment-roman);
  margin-top: 0.15rem;
}
.feed-list__item--pin .feed-list__pin-text--hand {
  /* Short moment — handwriting (Caveat), same size as the popup
     (Petřin pokyn 2026-05-27 „dej to na stejnou jako popup"). */
  font-family: var(--font-moment);
  font-style: normal;
  font-size: var(--fs-moment-hand);
  font-weight: var(--fw-moment-hand);
  line-height: var(--lh-moment-hand);
}

/* Moment-scoped location color (Petřin pokyn 2026-05-28) — list dříve měl
   place na --text-muted (~9:1), což čte jako druhý bílý blok pod bílým
   textem Momentu. Sjednocujeme s pattern z popupu koncertu
   (.popup--concert .popup__location): location dostane soft gold (--location-color)
   --label-color (stejný tón jako .field__label v modálech). Datum NE-mění
   se (Petřin rollback 2026-05-28 — vrátit font + barvu datumu zpět na
   inherit z .feed-list__meta = --text-muted). Scopováno na pin item, takže
   koncertní + online řádky meta beze změny. */
.feed-list__item--pin .feed-list__where {
  color: var(--location-color);
}

.feed-list__meta {
  /* M12 rollout — font properties INHERITED z shared base nahoře. Tady
     řeším JEN flex layout + color override.
     Petřin pokyn 2026-05-15 odp.: left-align (flag + location na začátku
     meta row, vertikálně pod vlajkou u username), čas push doprava přes
     margin-left:auto na .feed-list__when. Nowrap aby se čas nehodil
     na druhý řádek; long location se v případě nutnosti ellipsuje. */
  display: flex;
  align-items: center;
  gap: 0.3rem;
  justify-content: flex-start;
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24) — the 0.75 opacity was dimming
     the per-world date AND the location. Dropped it; location uses the
     brighter --text-muted (--text-light-muted was barely visible), the
     date keeps its full world colour. */
  color: var(--text-muted);
  margin-top: 0.3rem;
  flex-wrap: nowrap;
  min-width: 0;
}

.feed-list__sep {
  margin: 0 0.15rem;
  color: var(--text-muted);
  opacity: 0.6;
}

/* feed-list__where / feed-list__when nominally inherit from .feed-list__meta
   teal — explicit color removal of dead --text-muted rule (M12 rollout
   2026-05-14). */
.feed-list__where,
.feed-list__when {
  color: inherit;
}
.feed-list__where {
  /* Handoff m12-list-items.md §2/§3 — location = Manrope 700, uppercase,
     0.08em tracking, --text-muted (small caps label, not a heavy heading).
     M13.X-PLACE-NAME (Petřin pokyn 2026-06-03) — dropped a step to --fs-3xs
     (10px): the all-caps + bold + wide tracking read heavier than the 13px
     nick in the FEED. Popup / detail keep their own size (those felt fine). */
  font-weight: var(--fw-bold);
  font-size: var(--fs-3xs);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.feed-list__when {
  /* Handoff m12-list-items.md §2 — date = Manrope 500, ~11px → --fs-2xs. */
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-wide);
  margin-left: auto;
  flex-shrink: 0;
}

/* Petřin pokyn 2026-05-17 — meta is split into two rows.
   Row 1 (--left):  flag + location (left aligned), wraps if long.
   Row 2 (--right): absolute date (right aligned, own line).
   The `.feed-list__meta--stacked` modifier overrides the default
   single-row flex so both rows stack vertically; each row keeps its
   own align via .--left / .--right modifiers. */
.feed-list__meta--stacked {
  flex-direction: column;
  align-items: stretch;
  gap: 0.2rem;
}

.feed-list__meta-row {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  min-width: 0;
}

.feed-list__meta-row--left {
  justify-content: flex-start;
  flex-wrap: wrap;
}

.feed-list__meta-row--right {
  justify-content: flex-end;
}
/* M13.X-ECHO-LIKES — when the per-Echo like rides this meta row, push it to the
   LEFT and keep the date on the RIGHT (like LEFT / date RIGHT, same as the
   popup + sheet meta rows). :has() already used elsewhere in this sheet. */
.feed-list__meta-row--right:has(.echo-like) {
  /* M19.X-TRANSLATE (Petřin pokyn 2026-06-12) — cluster the like + 文A glyph
     together on the LEFT and push the date hard RIGHT, so the glyph sits next
     to the heart instead of floating in the middle (space-between spread the
     three children evenly). */
  justify-content: flex-start;
  align-items: center;
  gap: 8px;
}
.feed-list__meta-row--right:has(.echo-like) .feed-list__when {
  margin-left: auto;
}
/* 2026-06-03 evening (Petřin pokyn) — the Echo feed-row date tucks under the
   kebab DOTS, like the gathering popup rows. The pin body's 1.9rem right
   gutter exists so TEXT clears the absolute ⋮ button, but it also pushed the
   date row ~18px left of the kebab column. Pull just the meta row back so the
   date's right edge lands under the dots: dots end ≈26px from the card edge
   (kebab right 0.85rem + (28−16)/2 button centring + 6.7px glyph inset);
   body gutter puts the row edge at ≈44px ⇒ −18px. Yours rows keep their own
   top-right action cluster geometry — excluded. */
.feed-list__item--pin:not(.feed-list__item--yours) .feed-list__meta-row--right {
  margin-right: -18px;
}
/* M13.X-EVENT-LIKE-LIST — the like makes the date row taller, so its top
   edge sat cramped against the LOCATION row above (Petřin nález 2026-06-01). A
   light top nudge lets it breathe (≈4px, matching the Yours rows' rhythm).
   Conditioned on a location row actually preceding it (`--left:has(.where) + …`):
   Online rows have no location (their metaTop is empty + a platform CTA sits
   above instead), so they already carry the meta block's own 0.3rem + 0.2rem gap
   — adding the nudge there over-spaced them (Petřin pokyn 2026-06-01: "online
   bych snížila"). 2026-06-03 evening (Petřin pokyn) — the original `:not(--pin)`
   scope dropped: Echo feed rows want the same light air above the heart+date
   row ("opravdu stačí lehce"). */
.feed-list__item
  .feed-list__meta-row--left:has(.feed-list__where)
  + .feed-list__meta-row--right:has(.echo-like) {
  /* 2026-06-08 (Petřin pokyn) — a touch more air above the heart+date row so it
     sits less cramped under the location line. Desktop + mobile (bumped 0.25→0.4rem). */
  margin-top: 0.4rem;
}

.feed-list__meta--stacked .feed-list__where {
  /* In stacked mode the location row can wrap, so drop the ellipsis. */
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

.feed-list__meta--stacked .feed-list__when {
  /* The date row is its own line — no auto-margin shoving needed. */
  margin-left: 0;
}

/* Title row — title (left) + status dot (right) on the SAME line per
   Petřin pokyn 2026-05-17. flex justify-content:space-between pushes
   the dot to the right edge; align-items center keeps small dot
   visually balanced against multi-line title. */
.feed-list__title-row {
  display: flex;
  /* top-align so the right cluster (kebab + LIVE pill) sits at the top of a
     multi-line title, not vertically centered against it (Petřin pokyn
     2026-05-24 "LIVE má být vpravo, ne u nadpisu"). */
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem;
}
.feed-list__title-row > .feed-list__title {
  flex: 1 1 auto;
  min-width: 0;
}

.feed-list__title-row .feed-list__status-dot {
  flex-shrink: 0;
}

.feed-list__flag {
  width: 18px;
  height: 12px;
  object-fit: cover;
  border-radius: 2px;
  vertical-align: middle;
}

/* Smaller variant for pin location flag in feed meta row — Petřin pokyn
   2026-05-15 odp.: menší než .feed-list__nationality (= username flag
   14×10), tj. méně dominantní v secondary meta řádku, jasná hierarchie
   "vlajka identity uživatele > vlajka geografie pinu". */
.feed-list__flag--small {
  width: 12px;
  height: 8px;
  border-radius: 1px;
  flex-shrink: 0;
}

/* Online event subcategory chip (AI-S1-M10D-009). Small teal pill that
   sits alongside the status badge + relative time in the feed meta row,
   so two visually identical Online rows still disambiguate as
   "Listening party" vs "Q&A" at a glance. */
.feed-list__subcategory {
  display: inline-flex;
  align-items: center;
  padding: 0.05rem 0.5rem;
  border-radius: var(--radius-pill, 999px);
  /* Subcategory chip = VIOLET (secondary classification, NOT the azure world
     colour). The online world tone is azure, but the sub-type label (Livestream
     / Q&A / Watch party) stays violet so it reads as a distinct second axis,
     not "more of the same azure" — Petřin pokyn 2026-05-31 "není účelem mít
     všechny chipy azure". Only appears on ONLINE rows. */
  background: rgba(var(--accent-violet-rgb), 0.12);
  color: var(--accent-violet-soft);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
}

.feed-list__badge {
  display: inline-block;
  padding: 0.08rem 0.45rem;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: rgba(var(--accent-turquoise-rgb), 0.10);
  color: var(--accent-turquoise);
}

/* LIVE + Today share the green --live token (Petřin pokyn 2026-05-25 —
   LIVE was wrongly red here while every other surface used --live; red is
   the danger/cancelled role). Today rides the same rule: a timeless concert
   can't go live without a time, so its day-of badge mirrors LIVE. */
.feed-list__badge--live,
.feed-list__badge--today {
  background: rgba(var(--success-rgb), 0.14);
  color: var(--live);
}

.feed-list__badge--past {
  background: rgba(238, 240, 250, 0.08);
  color: var(--text-muted);
}

/* Tinted danger — matches the --live / --today / --past tinted badges in the
   same admin row instead of a solid brick block (Petřin pokyn 2026-05-25,
   same outline-status rationale as the feed cancelled chip). */
.feed-list__badge--cancelled {
  background: rgba(var(--danger-rgb), 0.14);
  color: var(--danger-text);
}

/* Status dot — Petřin pokyn 2026-05-17 replaces the UPPERCASE text
   badge. Green = upcoming, grey = past, pulsing green = live, red =
   cancelled. Tooltip via title attr surfaces the status name on hover.
   Sits in .feed-list__meta row sibling to date + venue spans. */
.feed-list__status-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  vertical-align: middle;
  flex-shrink: 0;
  cursor: help; /* hint that hover reveals tooltip */
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24 "černý/cream prstenec pryč u všech
     indikátorů — mají glow") — the dot GLOWS in its status colour; the old
     0 0 0 2px cream ring (read as a border) is gone. Each status sets its
     own coloured glow below. */
  box-shadow: none;
}

/* Petřin pokyn 2026-05-17:
   - upcoming (general)     = orange dot
   - upcoming-next (nearest) = green dot (= "next concert coming up")
   - live (running now)      = green dot, pulsing
   - past                    = grey dot
   - cancelled               = red dot */
.feed-list__status-dot--upcoming {
  background: var(--status-upcoming); /* warm orange — "scheduled but not the next one" */
  box-shadow: 0 0 7px 1px rgba(var(--status-upcoming-rgb), 0.85);
}

.feed-list__status-dot--upcoming-next {
  background: var(--status-next); /* forest green — "this is the next concert" */
  box-shadow: 0 0 7px 1px rgba(var(--status-next-rgb), 0.85);
}

.feed-list__status-dot--live {
  background: var(--status-next); /* same green as next-upcoming, set apart by pulse */
  box-shadow: 0 0 8px 2px rgba(var(--status-next-rgb), 0.6);
  animation: feed-list__status-pulse 1.6s ease-in-out infinite;
}

@keyframes feed-list__status-pulse {
  0%, 100% {
    box-shadow: 0 0 8px 2px rgba(var(--status-next-rgb), 0.6);
  }
  50% {
    box-shadow: 0 0 13px 4px rgba(var(--status-next-rgb), 0.28);
  }
}

@media (prefers-reduced-motion: reduce) {
  .feed-list__status-dot--live {
    animation: none;
  }
}

.feed-list__status-dot--past {
  background: var(--status-past);
  box-shadow: 0 0 6px 1px rgba(var(--status-past-rgb), 0.5);
}

.feed-list__status-dot--cancelled {
  background: var(--cancelled-red);
  box-shadow: 0 0 7px 1px rgba(var(--danger-rgb), 0.8);
}

/* Handoff m12-list-items.md §4 — "● LIVE" mint glow pill in the title row
   for live events (the per-row status dot is hidden on Concerts/Online, so
   this is the visible live signal). Text only + glow, no bg/border. */
/* M12.X-LIVE-DURATION Stage 3 — list title-row LIVE + Today indicator.
   Tinted outline chip in the live token — same treatment as the side-panel
   category chips (.chip / .chip--livestream), NOT the solid glowing popup
   chip (Petřin pokyn 2026-05-25). The negative right margin cancels the
   title-row's 2rem kebab reservation so the chip sits flush with the
   right-aligned date below it, while the (multi-line) title keeps its
   clearance from the vertically-centred kebab. */
.feed-list__live-pill,
.feed-list__today-pill {
  flex-shrink: 0;
  margin-right: -2rem;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.1rem 0.5rem;
  border-radius: var(--radius-pill);
  background: rgba(var(--live-rgb), 0.12);
  color: var(--live);
  border: 1px solid rgba(var(--live-rgb), 0.32);
  font-family: var(--font-body);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  line-height: 1.5;
  text-transform: uppercase;
  white-space: nowrap;
}
.feed-list__live-pill::before,
.feed-list__today-pill::before {
  content: "●";
}

/* M12 Noir PR4 (Petřin pokyn 2026-05-24) — always-visible "⋮" kebab that
   consolidates the per-row actions (edit / suggest / delete) into one menu
   (the .pin-row-menu popover). Replaces the scattered hover icons. */
.feed-list__row-menu-btn {
  appearance: none;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.feed-list__row-menu-btn:hover,
.feed-list__row-menu-btn:focus-visible {
  background: rgba(238, 240, 250, 0.08);
  color: var(--text-dark);
  outline: none;
}
/* M12 Noir PR4 (Petřin pokyn 2026-05-24) — on Concerts/Online/Moments cards
   the kebab is pinned to the RIGHT edge, with the same right padding as the
   date below it (LIVE pill sits to its left). The card is position:relative.
   Yours rows keep the kebab in their absolute top-right action cluster (rule
   not applied there).
   M17.X-KEBAB-NAME-LEVEL (Petřin pokyn 2026-06-09) — the kebab sits CENTRED on
   the NAME/title LINE, not on the whole card. Supersedes the 2026-05-24 card
   centring: on a multi-line card (title/@nick + text + meta) that floated the
   dots well below the @name. The @nick/title line box = 1.1rem × 1.2 lh =
   1.32rem, starting at the card's 0.8rem top padding, so its vertical centre is
   0.8rem + 0.66rem = 1.46rem from the card top. The 28px button is centred on
   that ⇒ top = 1.46rem − 14px (half the button). Line-box maths, so it holds
   regardless of which display font actually loads. */
.feed-list__item:not(.feed-list__item--yours) .feed-list__row-menu-btn {
  position: absolute;
  top: calc(1.46rem - 14px);
  right: 0.85rem;
}
/* Reserve room on the title row so the title + LIVE pill don't slide under
   the absolute kebab. */
.feed-list__item:not(.feed-list__item--yours) .feed-list__title-row {
  padding-right: 2rem;
}
/* Moment (pin) rows have no title-row — reserve the right gutter on the body
   so the username / text / meta clear the name-level kebab. */
.feed-list__item--pin .feed-list__body {
  padding-right: 1.9rem;
}

/* M11.X-NAVPIVOT S1 AI-001 (UX-001/003/006) per P-055: per-row RSVP
   badge — disambiguates user's commitment state (Going / Attended)
   napříč Saved tab + Events tab. Subtle pill, sibling visual
   weight ke status badge ale jiná barevná rodina aby user okamžitě
   rozpoznal „event status" (LIVE/UPCOMING/PAST) vs „my state" (Going/…).
   Going = turquoise solid (firm commitment), Attended = muted slate
   (past acknowledgement). The "Interested" variant was removed 2026-06-04
   (Petřin pokyn) — the like heart on the row carries that state since M13Q. */
.feed-list__rsvp-badge {
  display: inline-block;
  padding: 0.08rem 0.45rem;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.feed-list__rsvp-badge--going {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
}
.feed-list__rsvp-badge--attended {
  background: rgba(238, 240, 250, 0.08);
  color: var(--text-muted);
}

/* M11.X-MODERATION-PIVOT (ADR-112) — own pending / own rejected badge
   inline next to event title in fan-facing Concerts/Online list.
   Approved rows render no badge (= default state). Rejected badge
   carries rejection_reason as `title=` tooltip (Petřin add 2026-05-18). */
.feed-list__mod-badge {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.45rem;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  vertical-align: middle;
}
.feed-list__mod-badge--pending {
  background: rgba(232, 130, 31, 0.14);
  color: #b85f15;
  border: 1px solid rgba(232, 130, 31, 0.35);
  /* Compensate border so height matches sibling badges. */
  padding: calc(0.05rem - 1px) calc(0.45rem - 1px);
}
.feed-list__mod-badge--rejected {
  background: rgba(var(--danger-rgb), 0.10);
  color: var(--danger-text);
  border: 1px solid rgba(var(--danger-rgb), 0.32);
  padding: calc(0.05rem - 1px) calc(0.45rem - 1px);
}

/* Question-mark icon next to a rejected badge — opens a Frost dialog
   with the moderator's reason (Petřin pokyn 2026-05-19; replaces the
   tooltip-on-badge pattern which was mobile-hostile + undiscoverable).
   Sized + ghost-styled to match .events-row__dismiss (trash) in the
   trailing cluster — Petřin pokyn 2026-05-19 odp.: "stejně velkou
   ikonu otazníku, ta co tam je vypadá hrozně". */
.feed-list__mod-info {
  margin-left: 0.3em;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--danger-text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  flex: 0 0 auto;
  transition: background 160ms ease;
}
.feed-list__mod-info:hover,
.feed-list__mod-info:focus-visible {
  background: rgba(var(--danger-rgb, 191, 75, 75), 0.10);
  outline: none;
}
.feed-list__mod-info:focus-visible {
  outline: 2px solid var(--danger, #bf4b4b);
  outline-offset: -2px;
}

/* M11.X-EVENT-ACTIONS (ADR-125/127) — inline action icons on event
   rows. Frost ghost pattern: hidden by default, revealed on row hover
   (desktop) or always at 0.6 opacity on touch. Per-action color
   modifier paints currentColor on the inline SVG. */
.feed-list__action-row {
  display: flex;
  gap: 4px;
  margin-left: auto;
  opacity: 0;
  transition: opacity 150ms ease;
  /* Status dot must remain visible — sits to our right. */
  align-items: center;
}
.feed-list__item:hover .feed-list__action-row,
.feed-list__item:focus-within .feed-list__action-row,
.feed-list__action-row--always {
  opacity: 1;
}
@media (hover: none) {
  .feed-list__action-row { opacity: 0.6; }
  .feed-list__item:hover .feed-list__action-row,
  .feed-list__item:focus-within .feed-list__action-row { opacity: 1; }
}

.feed-list__action {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  width: 28px;
  height: 28px;
  /* Mobile 44px hit target without bumping the visual box. The
     transparent padding inflates the click area. */
  min-width: 28px;
  min-height: 28px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 120ms ease, background 120ms ease;
}
.feed-list__action:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
.feed-list__action--edit:hover {
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.10);
}
.feed-list__action--suggest:hover {
  /* Per ADR-127 — suggest is informational, not destructive. Uses the
     same turquoise family as edit but with a copper-ish accent tinge
     to disambiguate at a glance. */
  color: var(--accent-copper);
  background: rgba(var(--accent-copper-rgb), 0.10);
}
.feed-list__action--delete:hover {
  color: var(--danger-text);
  background: rgba(var(--danger-rgb), 0.10);
}

/* M11.X-EVENT-ACTIONS — shared button-row component used by detail
   modals (footer variant) + map popups (popup variant). Row variant is
   inlined on event panel rows above as `.feed-list__action-row`. */
.event-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
}
.event-actions--footer {
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--border-on-light);
}
.event-actions--popup {
  margin-top: 0.6rem;
  padding-top: 0.45rem;
  border-top: 1px solid var(--border-on-light);
  justify-content: flex-end;
  gap: 0.3rem;
}
/* M11.X-EVENT-ACTIONS — inline action buttons on event detail modals + mini
   popup. Frost ghost pattern: muted default, hue-shift on hover per role
   (edit=turquoise, suggest=copper, delete=danger per ADR-078 uniform-per-role).
   Token-only — drift fixed M11.X-EVENT-ACTIONS DESIGN polish 2026-05-19. */
.event-actions__btn {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}
/* M15 [BUG] (Petřin verdict 2026-06-06, real data wins) — duplicate-blocked
   test→real flask: aria-disabled gate pattern (tap fires → reason toast). */
.event-actions__btn[aria-disabled="true"],
.event-actions__btn[aria-disabled="true"]:hover {
  color: var(--text-muted);
  opacity: 0.45;
  cursor: not-allowed;
  background: transparent;
}
.event-actions--footer .event-actions__btn {
  padding: 0.5rem 0.85rem;
  font-size: 0.85rem;
  font-weight: 600;
  min-height: 36px;
  border: 1px solid var(--border-on-light);
}
.event-actions--popup .event-actions__btn {
  width: 28px;
  height: 28px;
  min-width: 28px;
  min-height: 28px;
  padding: 0;
  justify-content: center;
}
.event-actions__btn:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
/* M12D.X-MODERATION-UNIFY (U6) — in-context approve/reject, tones mirrored from
   the desktop POI row (.poi-actions__btn--approve = turquoise, --reject =
   danger). Reject is --danger (NOT magenta — events never carry magenta, that
   hue belongs to own echoes; memory feedback_event_ownership_never_magenta). */
.event-actions__btn--approve:hover,
.event-actions__btn--approve:focus-visible {
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.10);
  border-color: rgba(var(--accent-turquoise-rgb), 0.30);
}
.event-actions__btn--reject:hover,
.event-actions__btn--reject:focus-visible {
  color: var(--danger-text);
  background: rgba(var(--danger-rgb), 0.10);
  border-color: rgba(var(--danger-rgb), 0.30);
}
.event-actions__btn--edit:hover {
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.10);
  border-color: rgba(var(--accent-turquoise-rgb), 0.30);
}
.event-actions__btn--suggest:hover {
  color: var(--accent-copper);
  background: rgba(var(--accent-copper-rgb), 0.10);
  border-color: rgba(var(--accent-copper-rgb), 0.30);
}
.event-actions__btn--delete:hover {
  color: var(--danger-text);
  background: rgba(var(--danger-rgb), 0.10);
  border-color: rgba(var(--danger-rgb), 0.30);
}
/* ADR-171 — in-context test-data flask toggle. Mirrors the admin-table flask
   colour language (.admin-actions__icon--test-on/off): --test-off (event is
   real → "Mark as test data") reads as a muted ghost; --test-on (event IS
   test → "Restore as real data") carries the gold test-data fill (--warning)
   so the current test state is unmistakable. Same vocabulary across admin
   table + detail modal (Petřin pokyn 2026-05-24: gold, not danger red). */
.event-actions__btn--test-off:hover,
.event-actions__btn--test-off:focus-visible {
  color: var(--warning);
  background: rgba(var(--warning-rgb), 0.10);
  border-color: rgba(var(--warning-rgb), 0.30);
}
.event-actions__btn--test-on {
  color: var(--warning);
  background: rgba(var(--warning-rgb), 0.08);
  border-color: rgba(var(--warning-rgb), 0.30);
}
.event-actions__btn--test-on:hover,
.event-actions__btn--test-on:focus-visible {
  color: var(--warning);
  background: rgba(var(--warning-rgb), 0.18);
  border-color: rgba(var(--warning-rgb), 0.40);
}
.event-actions__label {
  font-weight: 600;
}
/* Hide label on the popup variant (icon-only) just in case the helper
   accidentally inlines one. */
.event-actions--popup .event-actions__label,
.event-actions--row .event-actions__label {
  display: none;
}

.feed-loadmore {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  width: calc(100% - 1.8rem);
  margin: 0.5rem 0.9rem;
  padding: 0.45rem 0.85rem;
  border-radius: var(--radius-sm);
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border: 1px solid var(--border-on-light);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent-turquoise);
  transition: background 180ms ease;
}

.feed-loadmore:hover,
.feed-loadmore:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  outline: none;
}

.feed-empty {
  margin: 1rem 0.9rem;
  padding: 0.85rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  text-align: center;
  background: rgba(var(--accent-turquoise-rgb), 0.04);
  border-radius: var(--radius-sm);
}

/* M13.X (Petřin pokyn 2026-06-04) — "My Echoes first" + song filter with no
   own match: notice above the foreign rows so they aren't mistaken for the
   viewer's own. Mirrors .feed-empty's shape; faint mine wash on the pill ties
   it to the toggle. Text = --text-dark (primary neutral light) per Petřin
   pokyn 2026-06-04 — the magenta-bright text read dim/greyish on the plum
   panel; standalone content never gets a muted/accent-dim tone. */
.feed-mine-empty {
  margin: 0.6rem 0.9rem 0.2rem;
  padding: 0.85rem;
  font-size: 0.85rem;
  color: var(--text-dark);
  text-align: center;
  background: rgba(var(--accent-mine-rgb), 0.07);
  border-radius: var(--radius-sm);
  list-style: none;
}

/* ============================================================
   Event detail — modal preview + full views (M10D)
   ============================================================
   Three surfaces:
     - .event-detail-modal       — <dialog> modal preview (both viewports)
     - .events-panel--detail-mode — desktop right-panel transformation
     - .event-detail-overlay     — mobile full-screen overlay
   All share the same `.event-detail__*` interior structure.
*/

/* Modal preview — shares atmospheric chrome with .fm-modal__panel /
   .delete-kotva-modal__panel / .pin-expand-overlay__panel (per design-
   system.md sekce „Custom div-based modal"). */
.event-detail-modal {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 1.5rem 1.4rem 1.2rem;
  max-width: 480px;
  width: calc(100vw - 2rem);
  /* M11.X-NAVPIVOT S1 AI-010 (M-UX-NAVPIVOT-008) per P-039: modal
     MUSÍ být dvh-based. M11.X přidalo 3-button RSVP row + nový
     interested count → content height ~92px navíc vs M10D 2-button.
     Bez max-height risk bottom-cut na iPhone SE (568 px viewport). */
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

.event-detail-modal::backdrop {
  background:
    radial-gradient(circle at 50% 0%, rgba(var(--accent-violet-rgb), 0.12), transparent 60%),
    rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* .event-detail-modal__close — unified styling shared with .dialog-close
   et al. Visual rules live in the unified block above (Petřin pokyn
   2026-05-16 odp.). */

/* ──────────────────────────────────────────────────────────────────────
 * Public profile modal (M10E) — ADR-040 + ADR-067.
 * Shares atmospheric chrome (mesh wash + cream surface + halo shadows)
 * with event-detail-modal + suggest dialogs per the design-system.md
 * shared modal list (M10D Cyklus 2 rollout — all-or-nothing principle).
 * ────────────────────────────────────────────────────────────────────── */
.public-profile-modal {
  /* Explicit viewport centering — defensive against browser UA stylesheet
   * variants. Native <dialog> usually centers with `margin: auto` + `inset:
   * 0` from the UA sheet, but some setups (older Chromium, prefs, certain
   * page-zoom levels) end up top-left. This makes it reproducible. */
  position: fixed;
  inset: 0;
  margin: auto;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 1.5rem 1.4rem 1.2rem;
  max-width: 520px;
  width: calc(100vw - 2rem);
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

/* M13.X-MEDALLION (Petřin pokyn 2026-06-03 round 2) — the desktop <dialog> is
   phone-guarded OFF (openPublicProfile delegates to the native bottom sheet on
   phones), so this modal only ever renders on desktop. No mobile full-screen
   override needed; mobile is a separate .fm-sheet implementation. */

.public-profile-modal::backdrop {
  /* STYLE-M10E-004 (Stage 2 review): single-layer drift → shared 2-layer
     atmospheric backdrop per design-system.md sekce „Custom div-based
     modal" → „Sdílený overlay backdrop". Same DNA jako pin-form-dialog
     / event-detail-modal / avatar-picker-dialog. */
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* .public-profile-modal__close — unified styling shared with
   .dialog-close (Petřin pokyn 2026-05-16 odp.). */

/* Header row — avatar + nick + verified ✓ + flag + reg date. */
.public-profile__header {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--border-on-light);
}

.public-profile__avatar {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 1px solid var(--border-on-light);
  background: var(--cream-surface);
}

.public-profile__meta {
  flex: 1 1 auto;
  min-width: 0;
}

.public-profile__nick-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.public-profile__nick {
  /* M12 PR6 Dávka B — the username IS this modal's heading, so align it to the
     canonical dialog title (ADR-179 shell): Cormorant italic 700. Big
     content-modal tier (M13.X-TITLE-TIERS). Consistent with how usernames render
     as Cormorant italic in the pin popup. STYLE-M10E-009 (Stage 2 review):
     overflow-wrap:anywhere (was break-all) so a long username wraps on the
     boundary, not mid-character. */
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-modal-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  color: var(--text-dark);
  overflow-wrap: anywhere;
}

.public-profile__verified {
  color: var(--accent-turquoise);
  font-size: 1rem;
  font-weight: 700;
}

/* M11B Stage N (ADR-154) — BANNED chip + ban detail bar.
   Surfaced only when the viewer holds the `users` permission (= mod)
   or `is_admin` (= admin) — backend strips ban_* fields otherwise.
   Danger-red palette per memory feedback_destructive_ctas_uniform_red. */
.public-profile__banned-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.18rem 0.6rem;
  border-radius: 999px;
  background: rgba(var(--danger-rgb, 178, 38, 38), 0.14);
  color: var(--danger-text);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid rgba(var(--danger-rgb, 178, 38, 38), 0.28);
}

.public-profile__ban-detail {
  margin: 0.4rem 0 1rem;
  padding: 0.7rem 0.95rem;
  background: rgba(var(--danger-rgb, 178, 38, 38), 0.07);
  border: 1px solid rgba(var(--danger-rgb, 178, 38, 38), 0.22);
  border-left: 3px solid var(--danger);
  border-radius: 8px;
  color: var(--text-dark);
}
.public-profile__ban-detail-heading {
  margin: 0 0 0.45rem;
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--danger-text);
}
.public-profile__ban-detail-lines {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.2rem 0.7rem;
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.4;
}
.public-profile__ban-detail-lines dt {
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.75rem;
  align-self: center;
}
.public-profile__ban-detail-lines dd {
  margin: 0;
  color: var(--text-dark);
  overflow-wrap: anywhere;
}

.public-profile__flag {
  height: 18px;
  width: auto;
  border-radius: 2px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}

/* ADR-244 mechanism 1 — empty flag outline on the fan's OWN profile when no
   nationality is declared (self-view only). Sits in the real flag's slot at
   the same optical scale; muted by default, teal on hover (invitation, not
   alarm). Click opens the Profile dialog via [data-country-prefs]. */
.public-profile__flag-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: var(--text-muted);
  cursor: pointer;
}
.public-profile__flag-ghost svg {
  width: 22px;
  height: 22px;
}
.public-profile__flag-ghost:hover {
  color: var(--accent-turquoise);
}

.public-profile__registered {
  margin: 0.3rem 0 0;
  font-size: 0.85rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Counts + Focus button row. */
/* Stat strip (M13.X-MEDALLION, ADR-222) — one bordered strip, hairline-
   separated cells. Echoes count = magenta (--accent-mine, the "your" world);
   Songs rated = gold (--warning) so it reads as a tally of the rating stars
   below. Replaces the old loose count row. */
.public-profile__stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  margin-bottom: 1rem;
  background: var(--hairline-dark-lo);
  border: 1px solid var(--hairline-dark-lo);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.public-profile__stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0.8rem 0.6rem;
  background: var(--surface-tint);
}

.public-profile__stat-num {
  font-family: var(--font-display, Cormorant, Cormorant Garamond, serif);
  font-size: 1.65rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text-dark);
  font-variant-numeric: tabular-nums;
}
.public-profile__stat-num--mine { color: var(--accent-mine); }
.public-profile__stat-num--fav { color: var(--warning); }

.public-profile__stat-label {
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 700;
}

/* Section card (M13.X-MEDALLION) — bordered Noir card wrapping each content
   block (Song ratings / Recent echoes / Social), per the prototype. */
.public-profile__body {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.public-profile__card {
  background: var(--surface-tint);
  border: 1px solid var(--hairline-dark-lo);
  border-radius: var(--radius-md);
  padding: 0.8rem 0.95rem 0.85rem;
}

/* Card eyebrow heading — leading glyph + label + optional muted suffix.
   Soft-teal --label-color, the app's canonical section-eyebrow tone. */
.public-profile__card-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.7rem;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--label-color);
  white-space: nowrap;
}

.public-profile__card-head-icon {
  display: inline-flex;
  flex-shrink: 0;
}
.public-profile__card-head-icon svg {
  width: 13px;
  height: 13px;
  display: block;
}

.public-profile__card-head-count {
  color: var(--text-light-muted);
  font-weight: 600;
  letter-spacing: 0.08em;
}

/* Focus button — STYLE-M10E-006 (Stage 2 review): violet **solid** byl
 * mis-tier (= secondary stack) ale tlačítko je jediný CTA v counts row
 * = primary tier. Per design-system.md „Codified policy — Secondary
 * brand CTAs on cream context" (2026-05-15 z M10D Stage 2): primary =
 * brand hue gradient + glow + lift, secondary = solid. Upgrade na
 * primary CTA stack (violet gradient + glow + lift inset highlight). */
.public-profile__focus-btn {
  appearance: none;
  border: 1px solid var(--accent-violet-soft);
  border-radius: var(--radius-md);
  padding: 0.55rem 1rem;
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  background: linear-gradient(135deg,
    var(--accent-violet-bright) 0%,
    var(--accent-violet) 100%);
  color: var(--text-light);
  box-shadow:
    0 4px 12px rgba(var(--accent-violet-rgb), 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.14);
  transition: background 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .public-profile__focus-btn:hover:not(:disabled) {
    transform: translateY(-1px);
    /* Halo intensifies on hover for primary CTA lift feel. */
    box-shadow:
      0 6px 18px rgba(var(--accent-violet-rgb), 0.40),
      inset 0 1px 0 rgba(238, 240, 250, 0.18);
    border-color: var(--accent-violet-bright);
  }
}

.public-profile__focus-btn:focus-visible {
  /* STYLE-M10E-007 (Stage 2 review): outline cross-hue (turquoise) →
     same-hue (violet). Button bg je violet → focus halo by měl být
     same-hue tint, ne cross-hue outline (= „barevný cirkus" v jednom
     elementu per Petřin gusto bod 2). */
  outline: 2px solid var(--accent-violet-soft);
  outline-offset: 2px;
}

.public-profile__focus-btn:disabled {
  background: var(--text-muted);
  cursor: not-allowed;
  opacity: 0.7;
}

@media (prefers-reduced-motion: reduce) {
  .public-profile__focus-btn {
    transition: none;
  }
}

/* (`.public-profile__section-heading` removed M13.X-MEDALLION — section
   headings now render as the in-card eyebrow `.public-profile__card-head`.) */

/* Footer — explicit Close button per P-008 (UX-M10E-001 Stage 1 review).
   Right-aligned secondary button, visible textual dismissal path for users
   who don't intuit × icon / ESC / backdrop tap. */
.public-profile__footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--border-on-light);
}

/* Contact section — now a card in the body stack (M13.X-MEDALLION); the
   body's gap handles spacing, so no extra margin. */
.public-profile__contact {
  margin-bottom: 0;
}

.public-profile__platform-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.55rem;
}

.public-profile__platform-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.75rem;
  border-radius: var(--radius-sm);
  /* ADR-255 — chip wash/border = platform tone (hub tones; supersedes the
     blanket teal wash + the 2026-05-15 monochrome violet icon). */
  background: color-mix(in srgb, var(--platform, var(--accent-turquoise)) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--accent-turquoise)) 12%, transparent);
  color: var(--text-dark);
  font-size: 0.88rem;
  text-decoration: none;
  line-height: 1.3;
  transition: background 160ms ease, border-color 160ms ease;
}

.public-profile__chip-icon {
  font-size: 1rem;
  line-height: 1;
  /* Flex shrink-free aby SVG ikona zachovala intrinsic 1em × 1em
     velikost bez squashing v případě dlouhého handle. */
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
}

/* ── M13.X-PLATFORM-UNIFY (ADR-255) — ONE tint hook for every platform
   surface. JS sets data-platform="<display key>" on the row/chip/badge;
   these rules resolve it to the --platform custom property; component CSS
   consumes `var(--platform, <fallback>)`. Same mechanism the hub shipped
   (ADR-252) — promoted from .hub-link-scoped rules to app-wide.
   "other" = legacy coarse DB enum value still present on older markup. */
[data-platform="reddit"]    { --platform: var(--platform-reddit); }
[data-platform="discord"]   { --platform: var(--platform-discord); }
[data-platform="forum"]     { --platform: var(--platform-forum); }
[data-platform="youtube"]   { --platform: var(--platform-youtube); }
[data-platform="facebook"]  { --platform: var(--platform-facebook); }
[data-platform="telegram"]  { --platform: var(--platform-telegram); }
[data-platform="zoom"]      { --platform: var(--platform-zoom); }
[data-platform="spotify"]   { --platform: var(--platform-spotify); }
[data-platform="twitch"]    { --platform: var(--platform-twitch); }
[data-platform="instagram"] { --platform: var(--platform-instagram); }
[data-platform="x"]         { --platform: var(--platform-x); }
[data-platform="tiktok"]    { --platform: var(--platform-tiktok); }
[data-platform="web"],
[data-platform="other"]     { --platform: var(--platform-web); }

/* Shared brand SVG icon — used v public-profile chip rows + contacts-row
   v Settings → Contact, link chips, badges.
   M13.X-PLATFORM-UNIFY (ADR-255): colour = per-platform tint (hub tones,
   supersedes the 2026-05-15 monochrome violet pick per Petřin souhlas
   2026-06-04). Violet-soft zůstává jen jako fallback mimo [data-platform]
   scope (detected-preview řádky).
   Source: frontend/js/platformIcons.js (Simple Icons MIT/CC0 + Lucide
   globe ISC, sourced 2026-05-15). */
.platform-icon {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  color: var(--platform, var(--accent-violet-soft));
  vertical-align: middle;
}

.public-profile__chip-platform {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
  /* ADR-255 — CAPS label = platform tone (hub-link__platform parity). */
  color: var(--platform, var(--accent-turquoise));
}

.public-profile__chip-handle {
  color: var(--text-dark);
  word-break: break-all;
}

@media (hover: hover) and (pointer: fine) {
  .public-profile__platform-chip--clickable:hover,
  .public-profile__platform-chip--clickable:focus-visible {
    background: color-mix(in srgb, var(--platform, var(--accent-turquoise)) 14%, transparent);
    border-color: color-mix(in srgb, var(--platform, var(--accent-turquoise)) 22%, transparent);
    outline: none;
  }
}

.public-profile__contact-empty {
  margin: 0;
  font-size: 0.88rem;
  color: var(--text-muted);
  font-style: italic;
}

/* Anonymous nudge — link-look per memory `feedback_anon_nudge_link_look`.
 * Underline ALWAYS default, color shift on hover, NEVER button-look. */
.public-profile__signin-nudge {
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  margin-top: 0.4rem;
  font-family: inherit;
  font-size: 0.92rem;
  color: var(--accent-turquoise);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .public-profile__signin-nudge:hover,
  .public-profile__signin-nudge:focus-visible {
    color: var(--accent-violet-soft);
    outline: none;
  }
}

/* ── Song ratings list (M13.X-MEDALLION, ADR-222) ────────────────────────
   Title (Cormorant italic) + ONE gold star + numeric value (★ 16), never N
   glyphs (scale runs to ~20). Gold = --warning, matching the stat number. */
.public-profile__songs {
  display: flex;
  flex-direction: column;
}
.public-profile__song-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: 0.75rem;
  padding: 0.55rem 0;
}
.public-profile__song-row + .public-profile__song-row {
  border-top: 1px solid var(--hairline-dark-lo);
}
.public-profile__song-title {
  font-family: var(--font-display, Cormorant, Cormorant Garamond, serif);
  font-style: italic;
  font-weight: 600;
  font-size: 1.14rem;
  line-height: 1.2;
  color: var(--text-dark);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.public-profile__song-rating {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-shrink: 0;
}
.public-profile__song-star {
  display: inline-flex;
  color: var(--warning);
}
.public-profile__song-star svg { width: 16px; height: 16px; display: block; }
.public-profile__song-val {
  font-weight: 700;
  font-size: 1.02rem;
  line-height: 1;
  color: var(--warning);
  font-variant-numeric: tabular-nums;
}

/* ── Recent echoes list (M13.X-MEDALLION) ────────────────────────────────
   Rows separated by hairline (no zebra inside the card). Main column = song
   eyebrow (optional) + message (moment voice) + place caps; side column =
   date + chevron affordance. */
.public-profile__pin-list {
  display: flex;
  flex-direction: column;
  /* M13.X-MEDALLION — bounded box; Echoes scroll inside the card + load
     progressively (renderPinList sentinel), instead of growing the modal. */
  max-height: 320px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--hairline-dark) transparent;
}
.public-profile__pin-list::-webkit-scrollbar {
  width: 6px;
}
.public-profile__pin-list::-webkit-scrollbar-thumb {
  background: var(--hairline-dark);
  border-radius: var(--radius-pill);
}
.public-profile__pin-sentinel {
  height: 1px;
  flex-shrink: 0;
}
.public-profile__pins-empty {
  margin: 0;
  font-size: 0.88rem;
  color: var(--text-muted);
  font-style: italic;
}

.public-profile__pin-row {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 0.75rem;
  align-items: start;
  appearance: none;
  width: 100%;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.65rem 0.4rem;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  transition: background 160ms ease;
}
.public-profile__pin-row + .public-profile__pin-row {
  border-top: 1px solid var(--hairline-dark-lo);
}

@media (hover: hover) and (pointer: fine) {
  .public-profile__pin-row:hover,
  .public-profile__pin-row:focus-visible {
    background: rgba(var(--accent-violet-rgb), 0.06);
    outline: none;
  }
}

.public-profile__echo-main { min-width: 0; }

/* Song eyebrow — shares the one --song-name token (song/broadcast tone). */
.public-profile__echo-song {
  display: inline-flex;
  align-items: center;
  gap: 0.38rem;
  margin-bottom: 0.3rem;
  max-width: 100%;
  color: var(--song-name);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  /* M13.X-SONGS-TAB Fáze 3b — the eyebrow is a <button> now (click →
     public Chart). Strip UA button chrome; the row keeps the map-focus
     click, so the eyebrow needs its own subtle link affordance. */
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .public-profile__echo-song:hover .public-profile__echo-song-title {
    text-decoration: underline;
    text-underline-offset: 2px;
  }
}
.public-profile__echo-song:focus-visible {
  outline: 2px solid var(--accent-violet-bright);
  outline-offset: 2px;
  border-radius: 4px;
}
.public-profile__echo-song-icon { display: inline-flex; flex-shrink: 0; }
.public-profile__echo-song-icon svg { width: 13px; height: 13px; display: block; }
.public-profile__echo-song-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Message — same moment voice as popup/feed (tokens only, one system). */
.public-profile__echo-msg {
  display: block;
  color: var(--text-dark);
  overflow-wrap: anywhere;
}
.public-profile__echo-msg--hand {
  font-family: var(--font-moment);
  font-size: var(--fs-moment-hand);
  font-weight: var(--fw-moment-hand);
  line-height: var(--lh-moment-hand);
}
.public-profile__echo-msg--roman {
  font-family: var(--font-moment-long);
  font-size: var(--fs-moment-roman);
  line-height: var(--lh-moment-roman);
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.public-profile__echo-place {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.35rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-light-muted);
}
.public-profile__echo-flag {
  width: 16px;
  height: auto;
  border-radius: 2px;
  flex-shrink: 0;
  box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.3);
}
.public-profile__echo-where {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.public-profile__echo-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  padding-top: 2px;
  white-space: nowrap;
}
.public-profile__echo-date {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.public-profile__echo-chevron {
  display: inline-flex;
  color: var(--text-light-muted);
}
.public-profile__echo-chevron svg { width: 15px; height: 15px; display: block; }
.public-profile__pin-row:hover .public-profile__echo-chevron { color: var(--label-color); }

/* Loading + error states. */
.public-profile__loading,
.public-profile__error {
  text-align: center;
  padding: 2rem 1rem;
  color: var(--text-muted);
  font-size: 0.92rem;
}

/* Social links (M10E) — list-based UX per Petřin design 2026-05-15:
 * saved links shown as a stacked list above a single new-link input
 * + Add button. Empty when user has no links yet; the input remains
 * available below.
 *
 *   ┌──────────────────────────────────┐
 *   │ 📷  Instagram   tarja_fan    🗑️ │  ← saved row
 *   │ 🤖  Reddit      petaSk3      🗑️ │  ← saved row
 *   └──────────────────────────────────┘
 *   ┌──────────────────────────────────┐
 *   │ [paste link...........] [Add]    │  ← new-link input
 *   │  📷 Detected: Instagram → tarja  │  ← live preview
 *   └──────────────────────────────────┘
 */
/* M12.X-PROFILE-FIDELITY — saved links as compact CHIPS in a wrapping row
   (Petřin pokyn 2026-05-26 "takhle to má reddit"). Was a stacked list of
   field-looking rows with a standing trash box. */
.contacts-rows {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.6rem;
}

/* Chip — platform icon + handle pill; discrete × appears on hover. */
.contacts-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.28rem 0.4rem 0.28rem 0.55rem;
  border: 1px solid var(--border-on-light);
  border-radius: 999px;
  background: rgba(var(--accent-turquoise-rgb), 0.06);
}

.contacts-chip__icon {
  display: inline-flex;
  align-items: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}
.contacts-chip__icon svg {
  width: 18px;
  height: 18px;
}

.contacts-chip__handle {
  font-size: var(--fs-sm);
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* × — hidden until the chip is hovered/focused; muted → danger on hover.
   On touch (no hover) it stays visible so the link is removable. */
.contacts-chip__x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
  /* M12.X-PROFILE-FIDELITY — always visible (Petřin pokyn 2026-05-27);
     muted at rest, danger only on hover/focus. */
  transition: color 120ms ease;
}
.contacts-chip__x:hover,
.contacts-chip__x:focus-visible {
  color: var(--danger-text);
}
.contacts-chip__x-icon {
  width: 13px;
  height: 13px;
}

/* Trash button — standard delete affordance matching .pin-popup__row-delete
 * (M10B convention 🗑️ emoji + cream-sunken hover). Bigger than the old
 * × per Petřin postřeh 2026-05-15.
 * Specificity bumped via dialog[id^="auth-"] prefix to defeat the generic
 * `dialog[id^="auth-"] button:not(...) { border: 1px solid ... }` rule
 * (line ~4951) — Petřin postřeh 2026-05-15 odp. „odstranění rámečku
 * kolem koše". Trash je discrete chrome, ne form button. */
.contacts-row__remove,
dialog[id^="auth-"] .contacts-row__remove {
  appearance: none;
  background: transparent;
  border: 0;
  font-size: 1rem;
  cursor: pointer;
  padding: 2px 5px;
  border-radius: var(--radius-sm);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  min-height: 28px;
  font-weight: 400;
  transition: background 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .contacts-row__remove:hover,
  .contacts-row__remove:focus-visible {
    background: var(--cream-sunken);
    outline: none;
  }
}

.contacts-row__remove:focus-visible {
  /* Petřin pokyn 2026-05-15 odp. "proč máme rámeček kolem koše":
     replace solid outline ring s subtle background tint (= souznění
     s hover state, NE box-like outline). Keyboard a11y zachovaná
     přes border-radius highlight místo extra ringu. */
  outline: none;
  background: var(--cream-sunken);
}

/* M12.X-PROFILE-FIDELITY — trash glyph: muted at rest, danger on hover/focus
   (discrete chrome per Petřin pokyn 2026-05-15, no box). */
.contacts-row__remove-icon {
  color: var(--text-muted);
  transition: color 160ms ease;
}
.contacts-row__remove:hover .contacts-row__remove-icon,
.contacts-row__remove:focus-visible .contacts-row__remove-icon {
  color: var(--danger-text);
}

/* New-link input — single field + Add button. Manual select appears
 * inline when the input is ambiguous (bare handle, no URL pattern). */
/* M12.X-PROFILE-FIDELITY — input + Add pill side by side (Petřin pokyn
   2026-05-27: NOT stacked). The placeholder text was shortened so it fits
   next to the "+ Add Social Link" pill without truncation. */
.contacts-input {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "input  add"
    "select select"
    "preview preview";
  gap: 0.45rem 0.5rem;
  align-items: center;
}

.contacts-input__field {
  grid-area: input;
  width: 100%;
  min-width: 0;
}

.contacts-input__select {
  grid-area: select;
  width: 100%;
}

.contacts-input__preview {
  grid-area: preview;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.contacts-input__preview strong {
  color: var(--text-dark);
}

/* ==========================================================================
   .btn-link-pill — REUSABLE canonical pill for "add / attach a link"-style
   actions (teal outline pill). Defined as a shared style (Petřin pokyn
   2026-05-27: "udělej znovupoužitelný styl … budeme aplikovat i jinde, kde
   jsou odkazy"). Registered in docs/ui-standards.md button registry.
   Usage: <button class="btn-link-pill">+ Add Social Link</button>
   ========================================================================== */
.btn-link-pill {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.5);
  border-radius: 999px;
  padding: 0.45rem 1rem;
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  background: transparent;
  color: var(--accent-turquoise);
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease,
    transform 160ms ease, box-shadow 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .btn-link-pill:hover:not(:disabled) {
    transform: translateY(-1px);
    background: rgba(var(--accent-turquoise-rgb), 0.1);
    box-shadow: 0 4px 14px rgba(var(--accent-turquoise-rgb), 0.22);
  }
}

.btn-link-pill:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* Disabled = still clearly a teal pill, just dimmed — NOT a faded grey ghost
   (Petřin pokyn 2026-05-27: "disabled je až moc disabled"). Dimming the teal
   border + text reads as "same button, not yet actionable". */
.btn-link-pill:disabled {
  border-color: rgba(var(--accent-turquoise-rgb), 0.32);
  color: rgba(var(--accent-turquoise-rgb), 0.55);
  cursor: not-allowed;
}

@media (prefers-reduced-motion: reduce) {
  .btn-link-pill {
    transition: none;
  }
}

/* The social-links Add button uses .btn-link-pill for its look; this class
   now only places it in the input grid. */
.contacts-input__add-btn {
  grid-area: add;
}

.form-actions--secondary {
  justify-content: flex-start;
  margin-bottom: 0.5rem;
}

/* M10E — clickable nicks across pin popup + feed-list + sigplace popup.
 * Subtle teal underline that strengthens on hover, no button affordance
 * (the nick itself is the link surface, matching the social-network
 * pattern users already know). Anon nicks stay non-clickable plain text. */
.pin-popup__nick--clickable {
  cursor: pointer;
  /* M12.X (Petřin pokyn 2026-05-31) — NO resting underline. The hardcoded teal
     underline read as a random line under the magenta owner nick (it didn't
     follow the nick's tone). Highlighted usernames carry their colour, not an
     underline; the click affordance shows on hover only (colour-matched). */
  text-decoration: none;
  text-underline-offset: 2px;
  transition: text-decoration-color 160ms ease, color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .pin-popup__nick--clickable:hover,
  .pin-popup__nick--clickable:focus-visible {
    text-decoration: underline;
    text-decoration-color: currentColor;
    outline: none;
  }
}

@media (hover: hover) and (pointer: fine) {
  .pin-popup__nick--clickable:hover,
  .pin-popup__nick--clickable:focus-visible {
    color: var(--accent-turquoise);
    text-decoration-color: var(--accent-turquoise);
    outline: none;
  }
}

.feed-list__title--clickable {
  /* Browser-button reset, ale BEZ `font: inherit` — ten dříve forced
     dědičnost z body (Manrope) místo zachování Cormorant 700 z
     .feed-list__title na stejném elementu. Petřin postřeh 2026-05-15:
     username registrovaných (button) měl jiný font než anonymních
     (span). Teď oba dědí Cormorant 700 violet z .feed-list__title. */
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  /* `color` deliberately NOT set here — .feed-list__title violet
     z parent class na stejném elementu má vyhrát. `color: inherit` zde
     dříve dědil text-dark z DOM parent → registrovaný username vypadal
     tmavě turquoise místo violet (Petřin postřeh 2026-05-15 odp.). */
  cursor: pointer;
  text-decoration: underline;
  /* Underline matches violet text color (Petřin pokyn 2026-05-21 — bylo
     omylem hardcoded turquoise navzdory violet `.feed-list__title`). */
  text-decoration-color: rgba(var(--accent-violet-rgb), 0.35);
  text-underline-offset: 2px;
  transition: text-decoration-color 160ms ease, color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .feed-list__title--clickable:hover,
  .feed-list__title--clickable:focus-visible {
    color: var(--accent-turquoise);
    text-decoration-color: var(--accent-turquoise);
    outline: none;
  }
}

/* Sigplace attribution nick — M10E re-enables it as a live button.
 * Previous disabled `Public profile coming soon` styling no longer
 * applies; use the same clickable affordance as the other call sites. */
.sigplace-popup__attribution-nick {
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--accent-turquoise);
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: rgba(var(--accent-turquoise-rgb), 0.35);
  text-underline-offset: 2px;
  transition: text-decoration-color 160ms ease, color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .sigplace-popup__attribution-nick:hover,
  .sigplace-popup__attribution-nick:focus-visible {
    text-decoration-color: var(--accent-turquoise);
    outline: none;
  }
}

/* Desktop right-panel transformation. */
.events-panel__detail {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0.5rem 0.9rem 1rem;
}

.events-panel__back,
.event-detail-overlay__back {
  appearance: none;
  background: transparent;
  border: none;
  font-family: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.45rem 0.7rem;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--accent-turquoise);
  border-radius: var(--radius-sm);
  transition: background 160ms ease;
  align-self: flex-start;
  margin-bottom: 0.5rem;
}

.events-panel__back:hover,
.events-panel__back:focus-visible,
.event-detail-overlay__back:hover,
.event-detail-overlay__back:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.10);
  outline: none;
}

/* When the right panel is in detail mode, hide the tabs header + the
   M12 Noir top bar (toggle/close belong to the list view, not the
   single-item detail view which has its own Back button). */
.events-panel--detail-mode .events-panel__header,
.events-panel--detail-mode .events-panel__topbar {
  display: none;
}

/* Mobile full-screen overlay. */
.event-detail-overlay {
  position: fixed;
  inset: 0;
  z-index: 1200; /* above hero strip (1100) */
  background: var(--cream-surface);
  display: flex;
  flex-direction: column;
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

.event-detail-overlay[hidden] {
  display: none;
}

.event-detail-overlay__header {
  flex-shrink: 0;
  padding: 0.6rem 0.9rem 0.4rem;
  border-bottom: 1px solid var(--border-on-light);
}

.event-detail-overlay__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1rem 1.1rem 1.5rem;
}

/* Desktop: hide the mobile overlay element entirely. */
@media (min-width: 768px) {
  .event-detail-overlay {
    display: none !important;
  }
}

/* ================================================================
 * Event map markers + mini popup (partial D.4, Petřin pokyn 2026-05-17).
 * Distinct violet-halo divIcon with category emoji; popup mirrors the
 * detail layout in miniature. Visual is intentionally placeholder —
 * M11 final pass (ADR-096) will replace with SVG marker + polished
 * popup layout.
 * ================================================================ */

.event-marker {
  /* M11.MARKER-RENDERING (2026-05-20) — `position: relative` + explicit
     width/height was overriding Leaflet's default `.leaflet-marker-icon`
     position: absolute (this class is the Leaflet wrapper itself, set via
     divIcon `className`, not an inner element). Relative wrapper drops
     back into document flow, and even with the negative margin Leaflet
     applies for iconAnchor each subsequent event marker drifts south by
     a few pixels per DOM order — Petřin screenshot 2026-05-20: Praha +
     Plzeň visibly offset from their venue GPS. Width / height are also
     redundant — Leaflet sets them inline from iconSize. Pin and sigplace
     never had this issue because they wrap their SVG in an inner div
     and only style the inner element. */
  pointer-events: auto;
}

.event-marker__svg {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* M12 Noir PR5 — grounding via the in-SVG url(#pinShadow) ellipse at the
     base (the note glyph is baked into the SVG; the old 🎤 <span> is gone). */
}

/* M12 Noir PR5 — a currently-live concert. Mint "● LIVE" pill below the
   head + a slow breathing halo. Mint = app-wide LIVE token, distinct from
   the magenta "mine" hue. The Leaflet marker wrapper is position:absolute
   (a positioned ancestor) so these absolute children anchor to it without
   needing position:relative (which previously caused marker drift). */
.event-marker__live-pill {
  position: absolute;
  left: 50%;
  bottom: -6px;
  transform: translateX(-50%);
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--live);
  color: var(--navy-deep);
  font-family: "Manrope", system-ui, sans-serif;
  font-weight: 700;
  font-size: 9px;
  letter-spacing: 0.18em;
  white-space: nowrap;
  box-shadow: 0 0 14px rgba(var(--live-rgb, 77, 213, 168), 0.7);
  pointer-events: none;
  z-index: 3;
}
.event-marker--live::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 15px;
  width: 36px;
  height: 36px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--live-rgb, 77, 213, 168), 0.32) 0%, transparent 65%);
  filter: blur(2px);
  pointer-events: none;
  z-index: 0;
  animation: event-marker-breathe 5s ease-in-out infinite;
}
@keyframes event-marker-breathe {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 0.95; transform: translate(-50%, -50%) scale(1.18); }
}
@media (prefers-reduced-motion: reduce) {
  .event-marker--live::before { animation: none; }
}

/* Mini popup — Leaflet wraps content in .leaflet-popup-content; we
   give it a card-like feel with a cover image at the top, title link
   below, then meta + description excerpt. */
.event-popup-wrapper .leaflet-popup-content {
  margin: 0;
  padding: 0;
  /* M11.X-POPUP-LAYOUT v2 — DO NOT set `width: auto !important` here.
     Leaflet's _updateLayout measures content in `whiteSpace: nowrap`,
     caps at options.maxWidth (= 440) / floors at options.minWidth (=
     300), and writes the result as inline `style.width = Npx`. An
     `!important` rule overrides that inline width → popup collapses to
     the natural width of its narrowest child (a photo at width:100%
     becomes a feedback loop with parent width). Symptom: ~150 px popup
     instead of 440 px (screenshots 2026-05-20 18:16 / 18:17). The
     mobile clamp below still caps the inline 440 on narrow viewports. */
  max-width: min(440px, calc(100vw - 32px));
  /* NO height cap / inner scroll here (Petřin pokyn 2026-06-09): a capped
     popup showed a scrollbar that collided with the round close ×, and on
     short viewports it squashed the card. The popup is free to grow with its
     content. The "odskok" the beta cap was guarding against is already handled
     in JS — _SmartResponsivePopup (map.js) flags a user "More"/"Less" toggle
     and lets the card grow DOWNWARD in place without re-placing or panning the
     map. So we get no scrollbar AND no jump. (Trade-off: a very long
     description expanded while the popup sits low on screen can extend below
     the fold — accepted; add a one-time soft nudge if it ever bites.) */
}

.event-popup-wrapper .leaflet-popup-content-wrapper {
  /* M12 Noir PR5 ② — teal entity tone (concert). Mirrors the copper poi
     wrapper treatment: tone radial + tinted border + tone edges. */
  --popup-edge: var(--accent-turquoise);
  --popup-edge-rgb: var(--accent-turquoise-rgb);
  border-radius: 14px;
  padding: 0;
  overflow: hidden;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.12), transparent 60%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.28);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.18),
    0 4px 12px rgba(var(--accent-turquoise-rgb), 0.14);
}

/* ============================================================
 * M12 Noir PR5 ② — shared place popup (concert + landmark)
 * Source: mockups/18-popup-place.png + m12-popups.md §1 (PlacePopup).
 * One layout system for BOTH the concert popup (.popup--concert, teal tone)
 * and the landmark popup (.popup--landmark, copper tone). The Leaflet
 * content-wrapper supplies the dark card chrome (global + per-namespace
 * rules); this block lays out the inner painterly hero + body. `--place-tone`
 * drives the per-entity accent (date colour, hero wash). Background stays
 * SOLID dark (not the handoff's translucent glass) because our map is
 * intentionally light (CartoDB Voyager) — dark glass over a light map
 * hurts readability.
 * ============================================================ */
.popup--place {
  --place-tone: var(--accent-turquoise);
  --place-tone-rgb: var(--accent-turquoise-rgb);
  display: flex;
  flex-direction: column;
  font-family: var(--font-body);
  color: var(--text-dark);
}
.popup--landmark {
  --place-tone: var(--accent-copper);
  --place-tone-rgb: var(--accent-copper-rgb);
}

/* Painterly hero — 96px strip: plum gradient base + a tone wash so concert
   reads teal-tinted, landmark copper-tinted. */
.popup__hero {
  position: relative;
  /* WYSIWYG with the editor focal preview (imageFocalPicker.js PREVIEW_ASPECT
     320/96): drive the hero off the SAME aspect ratio, not a fixed height, so
     the crop stays proportionally identical to "how it looks on the map" at any
     popup width. A fixed 96px height went proportionally flatter than the
     preview on wider popups (Petřin nález 2026-06-09 — „v popupu kratší na
     výšku než v náhledu"). */
  aspect-ratio: 320 / 96;
  overflow: hidden;
  background:
    radial-gradient(ellipse 90% 120% at 18% 0%, rgba(var(--place-tone-rgb), 0.20), transparent 70%),
    linear-gradient(180deg, var(--surface-plum), var(--cream-surface));
}
/* Real cover / photo sits over the gradient, full-bleed. */
.popup__hero-cover {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.popup__hero-cover img,
.popup__hero-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Broken-image fallback (concert): JS swaps the <img> for an emoji span and
   adds --fallback to the wrap; let the hero gradient show through behind it. */
.popup__hero-cover.event-popup__cover--fallback {
  background: transparent;
}
.event-popup__cover-icon {
  font-size: 2.4rem;
  line-height: 1;
  filter: drop-shadow(0 4px 10px rgba(var(--accent-violet-rgb), 0.30));
}
/* Bottom scrim so the hero chips stay legible over a bright poster. */
.popup__hero::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  height: 56px;
  background: linear-gradient(180deg, transparent, rgba(var(--cream-base-rgb), 0.78));
  pointer-events: none;
}
/* Top scrim — concerts carry bright posters (yellow/white skies); the global
   close × is a LIGHT frosted disc + near-white glyph, which VANISHES over a
   bright hero (Petřin nález 2026-05-29 — „koncert popup nemá křížek"; render
   confirmed the × was invisible, not missing). A soft dark wash in the top-
   right corner restores the × contrast without touching the shared × disc (so
   moment/landmark popups, which have dark heroes, are unaffected). Mirrors the
   bottom scrim; sits under the × (z-index 2) but over the cover (z-index 0). */
.popup__hero::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 1;
  height: 56px;
  background: linear-gradient(
    200deg,
    rgba(var(--cream-base-rgb), 0.55) 0%,
    rgba(var(--cream-base-rgb), 0.18) 38%,
    transparent 62%
  );
  pointer-events: none;
}
/* Decorative dust sparks (no-cover hero) — fixed positions + per-spark tone
   via nth-child so the markup carries no inline style (CLAUDE.md + CSP). */
.popup__dust {
  position: absolute;
  z-index: 0;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--dust-tone, var(--accent-turquoise));
  box-shadow: 0 0 8px var(--dust-tone, var(--accent-turquoise));
}
.popup__dust:nth-child(1) { left: 18%; top: 30%; --dust-tone: var(--accent-turquoise); }
.popup__dust:nth-child(2) { left: 72%; top: 22%; --dust-tone: var(--accent-copper); }
.popup__dust:nth-child(3) { left: 44%; top: 54%; --dust-tone: var(--accent-violet); }
.popup__dust:nth-child(4) { left: 84%; top: 62%; --dust-tone: var(--accent-mine); }
.popup__dust:nth-child(5) { left: 30%; top: 70%; --dust-tone: var(--accent-violet-bright); }
.popup__hero-chips {
  position: absolute;
  left: var(--space-3);
  bottom: var(--space-2);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  flex-wrap: wrap;
}
/* Hero chips are FILLED, not outlined (mockup 18 — Petřin pokyn 2026-05-25:
   outline chips disappear on the dark hero). Deliberately a DIFFERENT look
   from the outlined .chip-uppercase in the side-panel tabs. Solid tone fill +
   dark ink text + soft tone glow. */
.popup__hero-chips .chip-uppercase {
  background: var(--chip-tone, var(--accent-turquoise));
  border-color: transparent;
  color: var(--surface-ink);
  box-shadow: 0 0 10px rgba(var(--chip-tone-rgb, 77, 213, 207), 0.45);
}
/* PAST chip rides the same FILLED system as its hero siblings (Petřin pokyn
   2026-06-04 — the 0.18 translucent white fill vanished on dark cover photos).
   Solid archive grey + dark ink, deliberately different from the low-key
   tinted .feed-list__badge--past in the side panel. */
.popup__hero-chips .chip-uppercase--muted {
  --chip-tone: var(--status-past);
  --chip-tone-rgb: var(--status-past-rgb);
}
/* FRISSON tour chip lives in popup hero-chips as a FILLED variant — ADR-140
   locked the fill at base --accent-violet (Petřin pokyn 2026-05-28: „chip s
   fialovou v popupu je už fajn"). The iris variant is otherwise on
   --accent-violet-soft so outlined uses (tour chips in side-panel filters,
   tab labels, etc.) read brighter; this override restores base only for the
   filled hero-chips context. */
.popup__hero-chips .chip-uppercase--iris {
  --chip-tone: var(--accent-violet);
  --chip-tone-rgb: var(--accent-violet-rgb);
}

/* Photo credit — bottom-right of the hero, opposite the chips (Petřin volba
   2026-06-09 „viditelný text vpravo dole"). CC BY attribution must stay fully
   visible, so no truncation. Rides the existing .popup__hero::after bottom
   scrim; a soft text-shadow keeps it legible on bright posters. */
.popup__hero-credit {
  position: absolute;
  right: var(--space-3);
  /* Sits low in the hero but not flush to the edge (Petřin pokyn 2026-06-09
     „trochu dolů, ne úplně k dolnímu okraji"). */
  bottom: var(--space-1);
  z-index: 2;
  max-width: 60%;
  font: 500 10px var(--font-body);
  line-height: 1.3;
  text-align: right;
  color: var(--text-light-muted);
  /* Double shadow = tight dark edge + soft halo so the credit reads on bright
     posters without a visible box (Petřin nález 2026-06-09 — „na mobilu trochu
     nečitelné"; box byl dřív zamítnut jako rušivý). */
  text-shadow:
    0 1px 2px rgba(var(--cream-base-rgb), 0.95),
    0 0 6px rgba(var(--cream-base-rgb), 0.85);
  pointer-events: none;
}

.popup__body {
  display: flex;
  flex-direction: column;
  padding: var(--space-3);
}
.popup__title {
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-1-5);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-lg);
  line-height: var(--lh-tight);
  color: var(--text-dark);
}
/* Concert title is a link to the detail modal — strip the Leaflet blue + the
   underline, keep it light Cormorant per mockup, teal hint on hover. The
   2-class chain (0,3,0) beats Leaflet's `.leaflet-container a` (0,1,1) without
   !important (supersede ADR-121 violet-link — Noir mockup 18 = light title). */
.popup--place .popup__title a {
  color: var(--text-dark);
  text-decoration: none;
  transition: color 140ms ease;
}
.popup--place .popup__title a:hover,
.popup--place .popup__title a:focus-visible {
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-underline-offset: 2px;
  outline: none;
}

/* Petřin readability review 2026-05-28 — velikost venue bumped from --fs-2xs
   (11px) to --fs-xs (12px) + letter-spacing --ls-loose → --ls-wider pro
   čitelnost dlouhých venue jmen. BARVY místa a datumu zachovány tak, jak
   byly (Petřin pokyn 2026-05-28 — venue keeps soft-gold --location-color via
   .popup--concert override níž; date keeps --text-muted). */
.popup__location {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  margin-top: var(--space-2);
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Concert location → soft-gold location tone (--location-color), same as the
   modal field labels + popup section labels. Petřin pokyn 2026-05-27 +
   re-confirmed 2026-05-28 — barva místa se NEMĚNÍ. */
.popup--concert .popup__location { color: var(--location-color); }
.popup__flag {
  width: 18px;
  height: 13px;
  object-fit: cover;
  border-radius: 2px;
  flex-shrink: 0;
}
.popup__date {
  display: flex;
  align-items: center;
  gap: var(--space-1-5);
  margin-top: var(--space-1);
  font-family: var(--font-body);
  font-weight: var(--fw-medium);
  font-size: var(--fs-xs);
  /* M12.X-DETAIL-POPUP (Petřin brief 2026-05-27) — date = secondary metadata →
     MUTED, not the entity accent. Accent is reserved for identity (chip) +
     actions (RSVP / watch). Was var(--place-tone) (teal concert / violet online)
     which made metadata noisy. */
  color: var(--text-muted);
}
.popup__date-icon { flex-shrink: 0; }
.popup__divider {
  margin: var(--space-2-5, 0.65rem) 0;
  height: 1px;
  border: 0;
  background: var(--border-on-light);
}
/* Description — shared across concert (.popup__desc) + landmark
   (.poi-popup__description); the 2-class chain neutralises the legacy POI
   horizontal padding now that the body owns the gutter. */
.popup__body .popup__desc,
.popup--place .poi-popup__description {
  margin: 0;
  padding: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--text-muted);
  overflow-wrap: anywhere;
  word-break: break-word;
}
.popup__desc .text-expandable p:first-child {
  margin-top: 0;
}
/* RSVP CTA row (concert popup, mockup m12-popups.md §1): primary "I'm going"
   + square heart Save. */
.popup__cta-row {
  display: flex;
  gap: var(--space-1-5);
  align-items: stretch;
  /* No own margin-top — spacing from the counts tile is the action-zone gap
     (12px). A second margin here made the desktop gap 20px (M12.X-RSVP-UNIFY). */
}
/* M12.X-RSVP-UNIFY (Petřin pokyn 2026-05-30) — the canonical attendance button:
   ONE spec shared by popup / detail modal / mobile sheet. INACTIVE = light outline
   + tint (recedes); .is-active = filled vertical gradient + glow + dark ink + ✓
   (dominant). Fixes the old "idle ≈ active" bug where .btn-primary painted both
   states filled and only a tiny check distinguished them. Tone = teal (concert) /
   azure (online, below). NOT .btn-primary (that is a single always-filled submit).
   🔴 HEIGHT = THIS popup is THE reference (Petřin pokyn 2026-05-30 "z TOHOTO
   budeme vycházet"): original .btn-primary box = padding 0.5rem 1.1rem + --fs-sm
   → 34px. Detail + mobile MATCH this; never the other way round. */
.popup__cta {
  flex: 1;
  appearance: none;
  font-family: inherit;
  padding: 0.5rem 1.1rem;
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  cursor: pointer;
  /* INACTIVE = calm but CLEARLY a button (Petřin pokyn 2026-05-31): subtle neutral
     surface + visible border + readable near-white label. NOT teal-glowing (would
     read as selected) but NOT barely-there either — must still look pressable.
     Accent (teal/azure) is reserved for .is-active only. */
  background: rgba(238, 240, 250, 0.07);
  border: 1px solid rgba(238, 240, 250, 0.30);
  color: var(--text-light);
  transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease, transform 180ms ease;
}
.popup__cta:hover,
.popup__cta:focus-visible {
  background: rgba(238, 240, 250, 0.12);
  border-color: rgba(238, 240, 250, 0.45);
  outline: none;
}
@media (hover: hover) and (pointer: fine) {
  .popup__cta:hover { transform: translateY(-1px); }
}
/* ACTIVE = filled gradient + glow + dark ink (mirrors .btn-primary canon paint,
   but reserved for the SELECTED state only). */
.popup__cta.is-active {
  background: linear-gradient(180deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise-deep) 100%);
  border-color: var(--accent-turquoise);
  color: var(--surface-ink);
  box-shadow:
    0 4px 14px rgba(var(--accent-turquoise-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}
.popup__cta:disabled {
  opacity: 0.55;
  cursor: wait;
}
/* M13Q — the heart is now a FRAMELESS like COUNTER (heart glyph + count), an
   INDEPENDENT affinity decoupled from "I'm going" (Petřin pokyn 2026-06-01:
   "už to nechci samostatně vedle I am going jako interested … bez rámečku,
   jen počítadlo"). Visual canon unchanged: clean magenta --accent-magenta
   (NOT a per-entity tone, NEVER red), outline + muted count when idle, FILLED
   #heart-shade + magenta count when liked (.is-liked). Bigger than the per-Echo
   .echo-like (same heart family, our concert heart stays larger). */
.popup__cta-row .popup__save {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 2px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: color 160ms ease, transform 140ms ease;
}
.popup__cta-row .popup__save .popup__save-glyph {
  display: inline-flex;
  align-items: center;
}
.popup__cta-row .popup__save svg {
  width: 26px;
  height: 26px;
}
.popup__cta-row .popup__save svg path {
  stroke-width: 1.4;
}
.popup__cta-row .popup__save-count {
  font: var(--fw-bold) 14px/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.popup__cta-row .popup__save:hover { color: var(--accent-magenta); }
.popup__cta-row .popup__save:hover .popup__save-count { color: var(--magentaLift); }
.popup__cta-row .popup__save:active {
  transform: scale(0.92);
}
.popup__cta-row .popup__save:focus-visible {
  color: var(--accent-magenta);
  outline: 2px solid var(--accent-magenta);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* Primary shows a ✓ when "going"/"attended". */
.popup__cta.is-active::after {
  content: "✓";
  margin-left: var(--space-1);
  font-weight: var(--fw-bold);
}
/* Liked = magenta heart + LIFTED-magenta count (no frame, no tile). The count
   uses --magentaLift so the digits read clearly on Noir; the glyph keeps the
   deeper --accent-magenta + #heart-shade fill. */
.popup__cta-row .popup__save.is-liked { color: var(--accent-magenta); }
.popup__cta-row .popup__save.is-liked .popup__save-count { color: var(--magentaLift); }
.popup__cta-row .popup__save.is-liked svg path {
  /* Depth via an internal gradient (lighter top → deeper bottom) like the
     marker body-shade — reads on the dark surface where a drop-shadow can't.
     Def in index.html width=0 <defs>. Stroke keeps the crisp magenta edge. */
  fill: url(#heart-shade);
  stroke: var(--accent-magenta);
}
.popup__cta-row .popup__save:disabled {
  opacity: 0.55;
  cursor: wait;
}

/* ============================================================
 * M12.X-DETAIL-POPUP (ADR-188) — desktop popup carries the full detail
 * compactly: counts + RSVP (small pills, NOT huge buttons) + outward stream
 * link + Reddit discussion. Source: m12-popups.md §1 shell + m12-event-detail-
 * modal.md content; enriched layout = approved render preview 2026-05-27.
 * DESKTOP ONLY — on phone (≤480px) the detail lives in the bottom sheet
 * (mobile has its own UX), so the whole block is hidden + the wiring skipped.
 * ============================================================ */
/* M12.X-DETAIL-POPUP unified hierarchy (Petřin brief 2026-05-27):
   ACTION zone (counts + RSVP + watch link) sits ABOVE the divider — fast to
   see + FIXED, so when the description (CONTENT zone, below the divider) later
   becomes collapsible it can grow/shrink without ever pushing the actions
   down. The divider is the action↔content boundary. */
/* M13.X-CHROME-REFLOW — lifted 480 → 767 to track the unified mobile detail
   routing (mobileDetailSheets.js PHONE_MQ is now 767): the popup action-zone +
   discussion slot are hidden across the whole range where taps open the bottom
   sheet instead of the desktop popup. */
@media (max-width: 767px) {
  /* Enriched bits are desktop-only (phone uses the bottom sheet); description
     stays so the closing-then-sheet path never flashes an empty card. */
  .popup__action-zone,
  .popup__content-zone .popup__discussion-slot { display: none !important; }
}
/* Petřin readability review 2026-05-28 — větší breathing space mezi header
   (venue + datum) a action zone, aby counts kartička seděla jako samostatný
   blok, ne jako přilepený pod datum. */
.popup__action-zone {
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  /* M12.X-RSVP-UNIFY — 12px rhythm between counts → CTA → link. Was space-2 (8px)
     PLUS .popup__cta-row margin-top space-3 (12px) = 20px counts→button gap, which
     read as "padding velký" (Petřin pokyn 2026-05-30). Single 12px gap now. */
  gap: var(--space-3);
}
.popup__action-zone .popup__rsvp-slot {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.popup__content-zone {
  display: flex;
  flex-direction: column;
}

/* "Where fans are talking" head — soft-teal label tone (--label-color) matching modal field
   labels (Petřin nález 2026-05-27 — má mít barvu jako labely v modalu) and
   spaced off the RSVP buttons above so it doesn't read as glued to them. */
.popup__links-head {
  margin-top: var(--space-3);
  margin-bottom: var(--space-1-5);
  font: 700 var(--fs-xs)/1.2 var(--font-body);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--label-color);
}

/* Compact RSVP inside the popup — counts TILE (attendance card) + heart pair.
   Petřin readability review 2026-05-28: 9.5px CAPS counts were unreadable;
   replaced with soft tile that mirrors mobile .fm-attendance pattern.
   - Numbers 14px BOLD, colour-coded per state (going = entity tone,
     interested = muted, attended = light muted).
   - Labels 12px lowercase (no caps, no letter-spacing).
   - Soft glass tile (0.04 bg + 0.08 border) so the counts read as a
     stat card, not prose.
   Same shape in concert + online; the going colour swaps to entity tone. */
.popup--place .rsvp__counts {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-bottom: 0;
  padding: 8px 10px;
  border-radius: 10px;
  background: rgba(238, 240, 250, 0.04);
  border: 1px solid rgba(238, 240, 250, 0.08);
  font: 500 var(--fs-xs) var(--font-body);
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
}
.popup--place .rsvp__count { display: inline-flex; align-items: baseline; gap: 4px; }
/* Numbers: 14px bold, colour BY CLASS (not nth-child — the avatar stack now sits
   as the first child of .rsvp__counts, so sibling-order would mis-colour). Going =
   entity tone (teal / azure); interested + attended = muted. */
.popup--place .rsvp__count strong {
  font: var(--fw-bold) 14px/1 var(--font-body);
  color: var(--accent-turquoise);
}
/* Count NUMBER = teal (data/number system colour) even on online events; azure
   stays the world tone for the edge/CTA, not the count digits. Petřin pokyn
   2026-06-01 "číslo počítadla tyrkysové, ne azure; systémová věc". */
.popup--online  .rsvp__count--going strong { color: var(--accent-turquoise); }
.popup--place .rsvp__count--interested strong,
.popup--online .rsvp__count--interested strong { color: var(--text-muted); }
.popup--place .rsvp__count--attended strong,
.popup--online .rsvp__count--attended strong { color: var(--text-light-muted); }
/* M12.X-RSVP-UNIFY — small avatar stack (18px, overlapping) = supplementary
   social proof in the counts tile; same as the mobile .fm-attendance__avatar. */
.popup--place .rsvp__avatars { display: inline-flex; flex-shrink: 0; align-items: center; }
.popup--place .rsvp__avatar {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  margin-left: -7px;
}
.popup--place .rsvp__avatar:first-child { margin-left: 0; }
/* Fallback mood-tone ring (count-driven, no real preview) — border-color set
   inline per tone; matches the mobile .fm-attendance__avatar--placeholder. */
.popup--place .rsvp__avatar--placeholder { border: 1.5px solid; }
/* Separators replaced by the tile gap. */
.popup--place .rsvp__sep { display: none; }
.popup--place .rsvp__nudge {
  padding: 0.42rem 0.8rem;
  font-size: 0.8rem;
  margin-top: 0;
}

/* Outward stream + discussion link rows — compact (light tint on dark card). */
.popup__link {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-dark);
  background: rgba(238, 240, 250, 0.05);
  transition: background 140ms ease;
  margin-top: var(--space-1-5);
}
.popup__link:hover { background: rgba(238, 240, 250, 0.10); }
.popup__link-ic {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 6px;
  background: rgba(238, 240, 250, 0.07);
  color: var(--text-muted);
}
.popup__link-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.popup__link-title {
  font: 600 12px/1.2 var(--font-body);
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.popup__link-platform {
  font: 700 8.5px/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-light-muted);
  margin-top: 2px;
}
.popup__link-chev { flex: 0 0 auto; color: var(--text-light-muted); display: inline-flex; }
/* Outward stream/ticket link = teal accent (concert + online entity tone). */
.popup__link--watch { background: color-mix(in srgb, var(--accent-turquoise) 16%, transparent); }
.popup__link--watch:hover { background: color-mix(in srgb, var(--accent-turquoise) 26%, transparent); }
.popup__link--watch .popup__link-ic { background: var(--accent-turquoise); color: var(--text-on-accent); }

/* M12.X-DISCUSSION-LINKS — community discussion links + add UI inside the
   popup. Rows reuse the .popup__link visuals; a discussion row wraps an <a>
   (.popup__link-main) + an optional × remove button (adder/owner). The add
   form is a paste-a-URL input + teal Add pill (profile-contacts pattern). */
.popup__discussion-list { display: flex; flex-direction: column; gap: 4px; }
.popup__link--discussion {
  padding: 0;
  background: transparent;
  gap: 4px;
}
.popup__link--discussion:hover { background: transparent; }
/* M13.X-PLATFORM-UNIFY (ADR-255) — discussion rows wear the platform tint
   (hub recipe: glyph in platform colour, wash + border color-mix of the same
   tone) instead of the former all-grey tile, so Reddit/YouTube/Discord rows
   read identically here and in the Community hub. Tile keeps its 22px size. */
.popup__link--discussion .popup__link-ic {
  color: var(--platform, var(--text-muted));
  background: color-mix(in srgb, var(--platform, var(--text-muted)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--text-muted)) 30%, transparent);
}
.popup__link--discussion .popup__link-platform {
  color: var(--platform, var(--text-light-muted));
}
.popup__link-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text-dark);
  background: rgba(238, 240, 250, 0.05);
  transition: background 140ms ease;
}
.popup__link-main:hover { background: rgba(238, 240, 250, 0.10); }
.popup__link-remove {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 8px;
  border: 1px solid var(--border-on-light);
  background: transparent;
  color: var(--text-light-muted);
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease;
}
.popup__link-remove:hover {
  color: var(--accent-magenta);
  border-color: rgba(var(--accent-magenta-rgb), 0.45);
}

.popup__discussion-add { display: flex; gap: 6px; margin-top: var(--space-1-5); }
.popup__discussion-input {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-on-light);
  background: rgba(238, 240, 250, 0.04);
  color: var(--text-dark);
  font: 500 12px/1.3 var(--font-body);
}
.popup__discussion-input:focus-visible { outline: none; border-color: var(--accent-turquoise); }
.popup__discussion-addbtn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--accent-turquoise);
  font: 600 11.5px/1 var(--font-body);
  cursor: pointer;
}
.popup__discussion-addbtn:hover { background: rgba(var(--accent-turquoise-rgb), 0.2); }
.popup__discussion-addbtn:disabled { opacity: 0.55; cursor: wait; }
.popup__discussion-error {
  margin-top: 6px;
  font: 500 11px/1.35 var(--font-body);
  color: var(--danger-text);
}
.popup__discussion-nudge { margin-top: var(--space-1-5); }

/* M12.X-ONLINE-ANCHOR — desktop virtual pebble ("ether sphere") for online
   events (no map coords). Temporary, focus-only node at map centre that the
   online popup anchors to. AZURE = the online entity tone (M12.X-DETAIL-POPUP,
   Petřin pokyn 2026-05-27 — recolour the bubble to match the new online colour).
   Reset the Leaflet div-icon default chrome (white box + grey border) first. */
.online-virtual-anchor-wrap { background: transparent; border: 0; }
.online-virtual-anchor {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  position: relative;
  background:
    radial-gradient(circle at 36% 30%, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.45) 16%, transparent 34%),
    radial-gradient(circle at 50% 55%, rgba(var(--accent-online-rgb), 1) 0%, var(--accent-online) 45%, var(--accent-online-deep) 100%);
  box-shadow:
    0 0 0 1px rgba(var(--accent-online-rgb), 0.55),
    0 0 0 7px rgba(var(--accent-online-rgb), 0.16),
    0 0 34px 8px rgba(var(--accent-online-rgb), 0.55),
    0 10px 28px rgba(0, 0, 0, 0.28);
  animation: online-anchor-breathe 5s ease-in-out infinite;
}
.online-virtual-anchor::before {
  content: "";
  position: absolute;
  inset: -22px;
  border-radius: 50%;
  background: radial-gradient(circle, transparent 50%, rgba(var(--accent-online-rgb), 0.28) 66%, transparent 82%);
  pointer-events: none;
}
.online-virtual-anchor__glyph {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  color: rgba(255, 255, 255, 0.95);
  display: grid;
  place-items: center;
}
.online-virtual-anchor__glyph svg {
  width: 26px;
  height: 26px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}
@keyframes online-anchor-breathe {
  0%, 100% { transform: scale(1); opacity: 0.95; }
  50% { transform: scale(1.06); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .online-virtual-anchor { animation: none; }
}

/* M12.X-DETAIL-POPUP — online popup = AZURE entity tone (Petřin pokyn
   2026-05-27, supersede the violet tone). Concerts stay teal; online no longer
   shares violet with moments. Accents that hardcode teal (RSVP pills, watch
   link, add button) get azure overrides SCOPED to .popup--online so the concert
   popup + the shared detail-modal RSVP stay teal. The date + counts colours are
   NOT overridden here anymore — they're muted/white globally now. */
.popup--online {
  --place-tone: var(--accent-online);
  --place-tone-rgb: var(--accent-online-rgb);
}
.event-popup-wrapper--online .leaflet-popup-content-wrapper {
  --popup-edge: var(--accent-online);
  --popup-edge-rgb: var(--accent-online-rgb);
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-online-rgb), 0.12), transparent 60%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-online-rgb), 0.28);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.18),
    0 4px 12px rgba(var(--accent-online-rgb), 0.14);
}
.popup--online .rsvp__btn {
  border-color: var(--accent-online);
  background: rgba(var(--accent-online-rgb), 0.12);
  color: var(--accent-online-bright);
}
/* M13.X-EVENT-GOING-LIST (Petřin pokyn 2026-06-03) — the online attendance
   confirm ("I'll attend" CTA + .rsvp__btn--going) NO LONGER wears azure: a
   confirm action is TEAL everywhere (HARD RULE 12; ui-standards "ATTENDANCE
   CONFIRM = TEAL VŠUDE"). The former `.popup--online` azure overrides for
   .popup__cta.is-active / .rsvp__btn--going.is-active (+ dead --attended /
   --interested rules — both buckets removed in M12.X-RSVP-STATES) were deleted
   so the buttons fall through to the teal base rules. Other online-azure
   surfaces (chip, watch link, edge, modal chrome) keep their azure. */
.popup--online .popup__link--watch { background: color-mix(in srgb, var(--accent-online) 16%, transparent); }
.popup--online .popup__link--watch:hover { background: color-mix(in srgb, var(--accent-online) 26%, transparent); }
.popup--online .popup__link--watch .popup__link-ic { background: var(--accent-online); color: var(--text-on-accent); }
/* "+ Add link" = teal everywhere (action role, Petřin pokyn 2026-05-31) — no
   online azure override; falls through to the teal .popup__discussion-addbtn base. */
.popup--online .popup__discussion-input:focus-visible { border-color: var(--accent-online); }
.popup--online .rsvp__nudge {
  border-color: var(--accent-online);
  background: rgba(var(--accent-online-rgb), 0.05);
  color: var(--accent-online-bright);
}

/* Attribution / action icons sit inside the padded body — drop the legacy POI
   horizontal margins; right-align the action icons like the concert popup. */
.popup__body .poi-popup__attribution {
  padding: var(--space-2) 0 0;
  margin: 0;
  border-top: 0;
}
.popup__body .poi-actions--popup {
  display: flex;
  justify-content: flex-end;
  margin: var(--space-2-5, 0.65rem) 0 0;
}

/* Past — archive cue WITHOUT dimming the card. A past event popup stays fully
   interactive (RSVP "I was there", add a discussion link, read detail), so the
   card must NOT fade — the whole-card opacity was a bug (Petřin pokyn 2026-05-27:
   „u past jsem pro ztlumení, ale nemělo to ovlivňovat celé popupy"). Past is now
   signalled by the PAST chip + the archived (grayscale) cover photo + the dimmed
   MAP MARKER (.event-marker--past). Body + entity chip keep full colour. */
.popup--past .popup__hero-cover { filter: grayscale(0.6); }
/* Cancelled — keep the card readable but mark it: strike the title + a danger
   halo on the hero (ui-standards: cancelled = strike + --halo-danger, "did not
   happen", must stay visible). */
.popup--cancelled .popup__title a,
.popup--cancelled .popup__title > span {
  text-decoration: line-through;
  text-decoration-color: rgba(var(--danger-rgb), 0.7);
}
.popup--cancelled .popup__hero { box-shadow: inset var(--halo-danger); }

/* Mobile parity — tighten hero + body on narrow viewports. */
@media (max-width: 480px) {
  /* Hero height now follows aspect-ratio 320/96 (= WYSIWYG with the focal
     preview); no fixed-height override — it auto-scales with the narrower
     popup width. */
  .popup__body { padding: var(--space-2-5, 0.7rem); }
  .popup__title { font-size: var(--fs-md); }
}

/* Event cover header — Petřin pokyn 2026-05-17 — every event detail
   (map popup + full detail) leads with a cover image. If a cover was
   picked, render the image (object-fit:contain to preserve aspect
   ratio, bounded max-height). If not, render a large microphone (or
   📡 for online) placeholder so the popup never feels empty. */
.event-detail__cover {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0 0 0.8rem;
  /* M12 PR6 Dávka B (m12-event-detail-modal.md §2C) — tone down the Frost-era
     violet/teal wash to a muted dark surface so the real cover photo dominates;
     placeholder glyph reads on the same dark ground. */
  background: var(--surface-navy);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.event-detail__cover-img {
  display: block;
  max-width: 100%;
  max-height: 220px;
  width: auto;
  height: auto;
  object-fit: contain;
}

.event-detail__cover-credit {
  margin: 0.3rem 0.6rem 0.4rem;
  font-size: 0.72rem;
  color: var(--text-light-muted);
  line-height: 1.3;
  text-align: center;
}

/* M11L (ADR-151) — cancellation banner in event detail (concert +
   online modals). Full-width red bar above the header, white text,
   bold label + reason paragraph. Padding matches the adjacent
   .event-detail__cover spacing so the banner reads as part of the
   same vertical rhythm. Reason text wraps freely; no clamp because
   fan-visible reasons are short (1-500 chars enforced server-side). */
.event-detail__cancelled-banner {
  margin: 0.4rem 0.6rem 0.6rem;
  padding: 0.65rem 0.85rem;
  background: var(--cancelled-red);
  color: #fff;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  box-shadow: 0 1px 4px rgba(var(--cancelled-red-rgb), 0.3);
}

.event-detail__cancelled-label {
  font-weight: 700;
  letter-spacing: 0.06em;
  font-size: 0.82rem;
  text-transform: uppercase;
}

.event-detail__cancelled-reason {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.4;
  color: rgba(238, 240, 250, 0.94);
}

/* M11L popup variant — same banner inside map popup, tighter padding
   for the narrower popup width (300/440 per ADR-139). */
/* M12 Noir PR5 ② — popup CANCELLED chip retired; the place popup now uses
   the canonical .chip-danger primitive in the hero-chips slot (mockup 18). */

/* M11L — feed-list CANCELLED chip (Concerts / Online tab + Yours panel
   parity with popup variant, per Petřin pokyn 2026-05-22 evening *„dala
   bych to jako chip i do záložek s koncerty"*). Sits inside the
   .feed-list__chips-row alongside tour-chip + TEST + RSVP chips. */
/* Outline danger variant — same tinted-outline language as the LIVE / Today
   pills in the title row + the .chip.deletion-category-chip pattern, so the
   cancelled state reads as a peer status chip rather than a solid alert block
   (Petřin pokyn 2026-05-25). The higher .chip.* specificity re-asserts the
   border that the later .chip baseline resets to transparent. */
.chip.feed-list__cancelled-chip {
  background: rgba(var(--danger-rgb), 0.12);
  color: var(--danger-text);
  border-color: rgba(var(--danger-rgb), 0.32);
}

/* Cancelled popup treatment now lives in the shared .popup--cancelled rules
   (M12 Noir PR5 ②): strike title + danger halo on the hero. */

.event-popup__cancelled-label {
  font-weight: 700;
  letter-spacing: 0.05em;
  font-size: 0.74rem;
  text-transform: uppercase;
}

.event-popup__cancelled-reason {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.35;
  color: rgba(238, 240, 250, 0.93);
}

/* M11L — admin row tint for cancelled events. Per Petřin pokyn
   2026-05-22 *„já jsem chtěla tuto barvu #9C0A00"*. Selectors include
   the explicit :nth-child(even) override so the zebra stripe at
   line ~8887 (.admin-table tbody tr:nth-child(even)) doesn't win
   the specificity tie on even rows. Background uses low-alpha so the
   text stays readable; cancelled-red border-left gives a distinct
   vertical signal that scans well alongside un-cancelled rows. */
.admin-table tbody tr.is-cancelled,
.admin-table tbody tr.is-cancelled:nth-child(even),
.admin-table tbody tr.is-cancelled:nth-child(odd) {
  background: rgba(var(--cancelled-red-rgb), 0.08);
  box-shadow: inset 3px 0 0 0 var(--cancelled-red);
}

@media (hover: hover) and (pointer: fine) {
  .admin-table tbody tr.is-cancelled:hover,
  .admin-table tbody tr.is-cancelled:nth-child(even):hover {
    background: rgba(var(--cancelled-red-rgb), 0.14);
  }
}

.admin-table tbody tr.is-cancelled .admin-row__title {
  color: var(--cancelled-red);
  font-weight: 600;
}

/* M11L — admin-only cancel control strip in event detail modal. Sits
   directly above the header (after the cover + below any CANCELLED
   banner) so admin/mod lands on it without scrolling to the footer
   action row. Per Petřin pokyn 2026-05-22 *„musí to být i v modalu,
   někde pěkně umístěne"*. */
.event-detail__admin-cancel-strip {
  margin: 0.5rem 0.6rem 0.4rem;
  display: flex;
  justify-content: flex-end;
}

.event-detail__admin-cancel-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

/* Active event → ghost outline in danger red (= signals destructive
   intent without being a loud filled button on a normal-looking event). */
.event-detail__admin-cancel-btn--cancel {
  background: transparent;
  color: var(--cancelled-red);
  border: 1px solid var(--cancelled-red);
}

.event-detail__admin-cancel-btn--cancel:hover {
  background: rgba(var(--cancelled-red-rgb), 0.1);
  color: var(--cancelled-red);
}

/* Cancelled event → restore action sits next to the red CANCELLED
   banner; neutral outline so it's distinct from the red banner above
   (= different role, different colour, no visual competition). */
.event-detail__admin-cancel-btn--restore {
  background: transparent;
  color: var(--text-dark);
  border: 1px solid var(--text-muted);
}

.event-detail__admin-cancel-btn--restore:hover {
  background: rgba(238, 240, 250, 0.06);
  border-color: var(--text-dark);
}

.event-detail__admin-cancel-icon {
  display: inline-flex;
  align-items: center;
}

/* M11L deferred toggle — pending state in edit form chip. Admin flipped
   the chip but hasn't clicked Save yet → filled/active visual reads as
   "will commit on Save". Click again to undo (returns to ghost outline). */
.event-detail__admin-cancel-btn--pending.event-detail__admin-cancel-btn--cancel {
  background: var(--cancelled-red);
  color: #fff;
  border-color: var(--cancelled-red);
}
.event-detail__admin-cancel-btn--pending.event-detail__admin-cancel-btn--cancel:hover {
  background: #7a0800;
  border-color: #7a0800;
}
.event-detail__admin-cancel-btn--pending.event-detail__admin-cancel-btn--restore {
  background: rgba(238, 240, 250, 0.14);
  color: var(--text-dark);
  border-color: var(--text-dark);
}
.event-detail__admin-cancel-btn--pending.event-detail__admin-cancel-btn--restore:hover {
  background: rgba(238, 240, 250, 0.2);
  border-color: var(--text-dark);
}

/* M11L — "Canceled" checkbox row inside the edit form. Mirrors
   `.test-data-field` styling so the two status flags read as a single
   related group, but uses `--cancelled-red` tint instead of danger.
   Per Petřin pokyn 2026-05-22 evening *„prostě canceled a buď ano nebo
   ne"* — simple binary checkbox, same visual vocabulary as test mode. */
.event-create__cancelled-field {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 0.6rem;
  margin-top: 0.75rem;
  padding: 0.6rem 0.75rem;
  background: rgba(var(--cancelled-red-rgb), 0.10);
  border: 1px solid rgba(var(--cancelled-red-rgb), 0.30);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease,
              box-shadow 120ms ease;
}

.event-create__cancelled-field:hover {
  background: rgba(var(--cancelled-red-rgb), 0.14);
  border-color: rgba(var(--cancelled-red-rgb), 0.45);
}

/* Pending state — admin flipped the checkbox but hasn't clicked Save.
   Subtle box-shadow + darker border so the row reads as "unsaved"
   without shouting. */
.event-create__cancelled-field--pending {
  box-shadow: 0 0 0 2px rgba(var(--cancelled-red-rgb), 0.25);
  border-color: var(--cancelled-red);
}

.event-create__cancelled-input {
  margin: 0.15rem 0 0;
  width: 1.1rem;
  height: 1.1rem;
  flex: 0 0 auto;
  accent-color: var(--cancelled-red);
  cursor: pointer;
}

.event-create__cancelled-text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.event-create__cancelled-label {
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--cancelled-red);
}

.event-detail__cover--placeholder {
  /* No image picked → microphone icon fills the area at a size that
     reads clearly in both the map popup preview and the full detail
     view (Petřin pokyn 'mikrofon v dostatečné velikosti'). */
  min-height: 140px;
  padding: 1.4rem 1rem;
  background: linear-gradient(135deg,
              rgba(var(--accent-violet-rgb), 0.14) 0%,
              rgba(var(--accent-turquoise-rgb), 0.10) 100%);
  justify-content: center;
}

.event-detail__cover-icon {
  font-size: 4rem;
  line-height: 1;
  display: inline-block;
  filter: drop-shadow(0 4px 12px rgba(var(--accent-violet-rgb), 0.25));
}

@media (max-width: 540px) {
  .event-detail__cover-img {
    max-height: 180px;
  }
  .event-detail__cover--placeholder {
    min-height: 110px;
  }
  .event-detail__cover-icon {
    font-size: 3.2rem;
  }
}

/* Shared inner detail content. Compact 3-line header per AI-S1-M10D-006
   (icon+title / status•date•flag-or-subcategory / venue) keeps the
   modal under P-001's section threshold without splitting visually. */
.event-detail__header {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin: 0 0 0.7rem;
  padding: 0 0 0.5rem;
  border-bottom: 1px solid rgba(238, 240, 250, 0.06);
}

.event-detail__header-title {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.event-detail__icon {
  /* M12 PR6 Dávka B — emoji → monochrome SVG (sized via this font-size, glyph
     is width:1em). Muted so the leading glyph stays a quiet marker next to the
     Cormorant title rather than competing with it. */
  display: inline-flex;
  font-size: 1.4rem;
  color: var(--text-muted);
}

.event-detail__title {
  /* M12 PR6 Dávka B — aligned to the canonical dialog title (ADR-179 shell,
     ui-standards §3): white Cormorant italic 700, 1.55rem. Mockups 27/28 show
     italic serif titles; supersedes the stale m12-event-detail-modal.md §1
     "Cormorant 600, don't touch" note (predates the Fáze 0 shell lock). */
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-modal-title); /* big content-modal tier (M13.X-TITLE-TIERS) */
  font-weight: 700;
  letter-spacing: 0.025em;
  color: var(--text-dark);
  line-height: 1.15;
}

.event-detail__header-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.event-detail__date {
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.event-detail__subcategory {
  /* Small teal pill so Online subcategory reads as a meta chip, not
     body text. Matches the feed-list subcategory styling for visual
     consistency between list-item and detail. */
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-pill, 999px);
  background: var(--accent-turquoise-tint, rgba(var(--accent-turquoise-rgb), 0.10));
  color: var(--accent-turquoise);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.event-detail__header-venue {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  font-size: 0.88rem;
  color: var(--text-muted);
}

/* M12 PR6 Dávka B — location pin glyph in the venue row (📍 → SVG). Inherits
   the row's muted colour; nudged 1px up to optically centre on the cap height. */
.event-detail__header-venue-icon {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--text-muted);
  transform: translateY(-0.5px);
}

/* Legacy `.event-detail__meta` + `.event-detail__venue` classes — kept
   so anything still referencing them doesn't lose styling. The header
   refactor renames them to `__header-meta` / `__header-venue`. */
.event-detail__meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.event-detail__venue,
.event-detail__url {
  margin: 0.45rem 0;
  font-size: 0.9rem;
}

.event-detail__url {
  display: inline-block;
  color: var(--accent-turquoise);
  font-weight: 600;
  text-decoration: none;
  padding: 0.35rem 0;
}

.event-detail__url:hover,
.event-detail__url:focus-visible {
  text-decoration: underline;
  outline: none;
}

.event-detail__description {
  margin: 0.7rem 0 1rem;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--text-dark);
  white-space: pre-wrap;
}

.event-detail__rsvp,
.event-detail__reddit {
  margin: 0.9rem 0;
}

.event-detail__open-full {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  display: block;
  width: 100%;
  margin-top: 1rem;
  padding: 0.65rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent-turquoise);
  /* M12 PR6 Dávka B — match canonical .btn-primary (mockup 08): VERTICAL
     gradient bright→deep + DARK text. Was the pre-Noir diagonal 135deg gradient
     + white text. */
  background: linear-gradient(180deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise-deep) 100%);
  color: var(--text-on-accent);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  transition: transform 180ms ease, box-shadow 180ms ease;
}

.event-detail__open-full:hover,
.event-detail__open-full:focus-visible {
  outline: none;
  box-shadow: 0 6px 18px rgba(var(--accent-turquoise-rgb), 0.32);
}
@media (hover: hover) and (pointer: fine) {
  .event-detail__open-full:hover {
    transform: translateY(-1px);
  }
}

.event-detail__loading,
.event-detail__error {
  padding: 1.2rem 0.5rem;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}

/* RSVP component (shared by Frisson Tour + Online). */
.rsvp__counts {
  font-size: 0.88rem;
  color: var(--text-dark);
  margin-bottom: 0.55rem;
  font-variant-numeric: tabular-nums;
}

.rsvp__count strong {
  font-weight: 700;
}

.rsvp__sep {
  color: var(--text-muted);
}

.rsvp__buttons {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* 3-tier turquoise hierarchy per AI-S2-M10D-021 Petřin volba A:
   - default = turquoise-tint ambient ("I can RSVP")
   - --going.is-active = gradient + glow + lift (drama-commitment)
   - --attended.is-active = solid (no glow, calmer past-action) */
.rsvp__btn {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  flex: 1 1 0;
  /* M11.X-NAVPIVOT S1 AI-009 (M-UX-NAVPIVOT-002) per P-059 (mobile):
     ADR-087 rozšíření 2 → 3 RSVP buttons; original 130px min-width
     (z 2-button era) způsobovalo flex-wrap do 2-3 řádků na 360px
     viewport (3×130 + 2×8 = 406px > 328px content area). Snížení
     na 92px = 3×92 + 16 = 292px ≤ 328px → 1-row fit. */
  min-width: 92px;
  /* M12.X-RSVP-UNIFY — match the popup reference (padding 0.5rem 1.1rem + --fs-sm
     → 34px) so detail = popup = mobile. INACTIVE = calm but CLEARLY a button:
     subtle neutral surface + visible border + near-white label (no teal glow —
     accent reserved for .is-active, Petřin pokyn 2026-05-31). */
  padding: 0.5rem 1.1rem;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(238, 240, 250, 0.30);
  background: rgba(238, 240, 250, 0.07);
  color: var(--text-light);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.01em;
  transition: background 180ms ease, color 180ms ease, transform 180ms ease, box-shadow 180ms ease;
}

.rsvp__btn:hover,
.rsvp__btn:focus-visible {
  background: rgba(238, 240, 250, 0.12);
  border-color: rgba(238, 240, 250, 0.45);
  outline: none;
}
@media (hover: hover) and (pointer: fine) {
  .rsvp__btn:hover {
    transform: translateY(-1px);
  }
}

/* Going active — drama gradient + glow + inset highlight. */
/* M12.X-RSVP-UNIFY — active state identical to popup .popup__cta.is-active +
   mobile .fm-rsvp__btn--selected: vertical bright→deep gradient + dark ink + glow
   + ✓ (was a 135° diagonal + light text = a per-surface variant). */
.rsvp__btn--going.is-active {
  background: linear-gradient(180deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise-deep) 100%);
  color: var(--surface-ink);
  box-shadow:
    0 4px 14px rgba(var(--accent-turquoise-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}
.rsvp__btn--going.is-active::after {
  content: "✓";
  margin-left: var(--space-1);
  font-weight: 700;
}
.rsvp__btn--going.is-active:hover,
.rsvp__btn--going.is-active:focus-visible {
  background: linear-gradient(180deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise) 100%);
}

/* Interested active — middle tier of the 3-tier turquoise hierarchy
   per AI-S2-NAVPIVOT-001 / ADR-087 :3490 (soft commitment signal).
   Tier ladder: gradient+glow (Going firm) > tint+outline+halo (Interested
   soft) > solid (Attended past). Without this tier the Interested toggle
   was visually identical to its idle state. */
.rsvp__btn--interested.is-active {
  background: rgba(var(--accent-turquoise-rgb), 0.18);
  color: var(--accent-turquoise);
  border-color: var(--accent-turquoise);
  box-shadow: 0 2px 8px rgba(var(--accent-turquoise-rgb), 0.16);
}
.rsvp__btn--interested.is-active:hover,
.rsvp__btn--interested.is-active:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.26);
}

/* Attended active — solid same-hue, no glow (calmer "past acknowledged"). */
.rsvp__btn--attended.is-active {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  box-shadow: 0 3px 10px rgba(var(--accent-turquoise-rgb), 0.22);
}
.rsvp__btn--attended.is-active:hover,
.rsvp__btn--attended.is-active:focus-visible {
  background: var(--accent-turquoise-deep);
}

/* M13Q — like heart = FRAMELESS counter (heart glyph + count), an INDEPENDENT
   affinity decoupled from "I'm going" (Petřin pokyn 2026-06-01). Idle = muted
   outline heart + muted count; liked = filled #heart-shade magenta + magenta
   count (.is-liked). Same magenta family + glyph + #heart-shade as the popup
   .popup__save so the heart reads identically everywhere; bigger than the
   per-Echo .echo-like (our concert heart stays larger). Magenta = affinity,
   independent of entity colour (feedback_event_ownership_never_magenta). */
.rsvp__heart {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 34px;
  padding: 0 2px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  transition: color 160ms ease, transform 140ms ease;
}
.rsvp__heart .rsvp__heart-glyph { display: inline-flex; align-items: center; }
.rsvp__heart svg { width: 26px; height: 26px; }
.rsvp__heart svg path { stroke-width: 1.4; }
.rsvp__heart-count {
  font: var(--fw-bold) 14px/1 var(--font-body);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.rsvp__heart:hover { color: var(--accent-magenta); }
.rsvp__heart:hover .rsvp__heart-count { color: var(--magentaLift); }
.rsvp__heart:focus-visible {
  color: var(--accent-magenta);
  outline: 2px solid var(--accent-magenta);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.rsvp__heart:active { transform: scale(0.92); }
.rsvp__heart.is-liked { color: var(--accent-magenta); }
.rsvp__heart.is-liked .rsvp__heart-count { color: var(--magentaLift); }
.rsvp__heart.is-liked svg path { fill: url(#heart-shade); stroke: var(--accent-magenta); }
.rsvp__heart:disabled { opacity: 0.55; cursor: wait; }

.rsvp__btn:disabled {
  opacity: 0.55;
  cursor: wait;
}

.rsvp__nudge {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  width: 100%;
  padding: 0.5rem 0.9rem;
  margin-top: 0.4rem;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.05);
  color: var(--accent-turquoise);
  font-size: 0.86rem;
  font-weight: 600;
  transition: background 160ms ease;
}

.rsvp__nudge:hover,
.rsvp__nudge:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  outline: none;
}

/* ============================================================
   Significant Place marker + popup (M10D — ADR-069)
   ============================================================
   Placeholder marker = 🏛️ emoji in a gold disc (Variant B per Petřin
   průchod scope otázka 4). Custom Tarja silhouette ikona arrives v M12.
*/
.sigplace-marker-wrap {
  background: transparent;
  border: none;
}

.sigplace-marker {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: linear-gradient(135deg, #f4d27c 0%, #c19642 100%);
  border: 2px solid var(--cream-surface);
  box-shadow:
    0 6px 16px rgba(193, 150, 66, 0.45),
    0 0 0 1px rgba(0, 0, 0, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.sigplace-marker::after {
  content: "";
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
  width: 10px;
  height: 10px;
  background: linear-gradient(135deg, #f4d27c 0%, #c19642 100%);
  border-right: 2px solid var(--cream-surface);
  border-bottom: 2px solid var(--cream-surface);
  z-index: -1;
}

.sigplace-marker__icon {
  font-size: 1.1rem;
  line-height: 1;
}

/* Sigplace popup — atmospheric cream surface, photo top, content below. */
.sigplace-popup .leaflet-popup-content-wrapper {
  background:
    radial-gradient(circle at 100% 0%, rgba(193, 150, 66, 0.10), transparent 60%),
    var(--cream-surface);
  border-radius: 14px;
  border: 1px solid rgba(193, 150, 66, 0.22);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.18),
    0 4px 12px rgba(193, 150, 66, 0.12);
}

.sigplace-popup .leaflet-popup-content {
  margin: 0;
  width: auto !important;
  padding: 0;
  color: var(--text-dark);
}

.sigplace-popup__photo {
  display: block;
  width: 100%;
  max-height: 160px;
  object-fit: cover;
  border-radius: 14px 14px 0 0;
}

.sigplace-popup__name {
  /* M12 rollout — Cormorant 600 SemiBold (consistent s popup nick + event detail) */
  margin: 0;
  padding: 0.7rem 0.9rem 0.3rem;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-dark);
  line-height: 1.25;
}

.sigplace-popup__description {
  margin: 0;
  padding: 0 0.9rem 0.5rem;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--text-dark);
}

.sigplace-popup__footer {
  margin: 0;
  padding: 0.4rem 0.9rem 0.55rem;
  font-size: 0.82rem;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.sigplace-popup__flag {
  width: 18px;
  height: 12px;
  object-fit: cover;
  border-radius: 2px;
}

.sigplace-popup__city {
  margin-left: 0.05rem;
}

.sigplace-popup__attribution {
  margin: 0;
  padding: 0.45rem 0.9rem 0.7rem;
  font-size: 0.78rem;
  color: var(--text-muted);
  border-top: 1px solid rgba(238, 240, 250, 0.06);
}

/* Disabled-button styling (AI-S1-M10D-004) — looks clickable, tooltip
   explains it isn't (yet). Becomes a real button trigger in M10E when
   the public profile modal lands. */
.sigplace-popup__attribution-nick {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--accent-violet-bright);
  font-weight: 600;
  cursor: not-allowed;
  text-decoration: underline dotted rgba(var(--accent-violet-rgb), 0.55);
  text-underline-offset: 2px;
  /* Per STYLE-M10D-025: full opacity for WCAG AA contrast.
     Disabled affordance = dotted underline + cursor: not-allowed only. */
}
.sigplace-popup__attribution-nick:hover,
.sigplace-popup__attribution-nick:focus-visible {
  opacity: 1;
  outline: none;
}

/* ============================================================
   Admin event/sigplace forms (M10D — Task #9)
   ============================================================
   2-column lat/lng rows inside the existing admin form
   pattern. Pre-launch styling — M12 polish revisits.
*/
.admin-event-fields__row {
  display: flex;
  gap: 0.6rem;
}

.admin-event-fields__row .field {
  flex: 1 1 0;
  min-width: 0;
}

.admin-event-fields--frisson,
.admin-event-fields--online {
  margin-top: 0.5rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
}

.admin-event-fields--frisson[hidden],
.admin-event-fields--online[hidden] {
  display: none;
}

/* Reddit link — pill, fits both "Read fan stories" and "Share".
   M13.X-PLATFORM-UNIFY (ADR-255): wears the Reddit platform tone via
   [data-platform="reddit"] (was an off-system violet pill + 💬 emoji). */
.reddit-link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.85rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--platform, var(--accent-violet)) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--accent-violet)) 30%, transparent);
  color: var(--platform, var(--accent-violet-bright));
  text-decoration: none;
  font-size: 0.86rem;
  font-weight: 600;
  transition: background 160ms ease, transform 160ms ease;
}

.reddit-link:hover,
.reddit-link:focus-visible {
  background: color-mix(in srgb, var(--platform, var(--accent-violet)) 16%, transparent);
  outline: none;
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift. */
@media (hover: hover) and (pointer: fine) {
  .reddit-link:hover {
    transform: translateY(-1px);
  }
}

.reddit-link__icon {
  font-size: 0.95rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
}

/* Logged-in user pill — cream pill with a small violet dot before
   the name to signal "you are signed in" with a touch of brand. */
.auth-header__user {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  /* M-UX-AUDIT-021 per P-036: min-height 44px for thumb zone. */
  padding: 0.55rem 0.95rem 0.55rem 0.75rem;
  min-height: 44px;
  border-radius: var(--radius-pill);
  /* Plastic depth — top-cluster parity (Petřin pokyn 2026-05-31): subtle
     vertical sheen on the dark pill (lighter navy top → deeper bottom). */
  background: linear-gradient(180deg, var(--navy-soft) 0%, var(--navy-deep) 100%);
  border: 1px solid var(--border-on-light);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-dark);
  /* Lifted-pill depth shared across the top cluster (Petra 2026-05-27 —
     buttons read flat). They float over the LIGHT map, so the drop must be
     a defined dark shadow (ambient + tight contact), not a faint tint. */
  box-shadow:
    0 6px 16px rgba(0, 0, 0, 0.32),
    0 2px 5px rgba(0, 0, 0, 0.26),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}

.auth-header__user::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent-violet-bright);
  box-shadow: 0 0 8px rgba(var(--accent-violet-rgb), 0.55);
}

/* M10B Stage 2 (STYLE-001) — Ghost button (Cancel) pattern from
   design-system.md "Component patterns". Used by the 3 custom div
   modals (delete-kotva, expand-overlay, add-pin-nearby, rename-kotva)
   for their Cancel buttons. Was referenced as `.btn-secondary` but
   the class didn't exist → buttons rendered as UA-default grey. */
.btn-secondary {
  appearance: none;
  background: transparent;
  color: var(--text-dark);
  border: 1px solid rgba(238, 240, 250, 0.36);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.95rem;
  font: inherit;
  font-weight: var(--fw-semibold);
  /* --fs-sm (13px) = same as .btn-primary / .btn-ghost / .btn-warn so Cancel
     and the confirm button in a footer row render the SAME height (was raw
     0.85rem, the one un-tokenised hold-out in the role-button family). */
  font-size: var(--fs-sm);
  letter-spacing: var(--ls-wide);
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease, transform 200ms ease;
}
.btn-secondary:hover,
.btn-secondary:focus-visible {
  background: rgba(238, 240, 250, 0.05);
  border-color: rgba(238, 240, 250, 0.5);
  outline: none;
}

/* M10B Stage 2 (STYLE-001) — Primary brand button (Save / commit).
   Turquoise gradient + glow + lift per design-system "CTA hierarchy".
   Used by renameKotvaDialog Save and any other M10B primary submit
   not already covered by the auth dialog button-family selectors. */
.btn-primary {
  appearance: none;
  /* Noir PR3 — visible VERTICAL teal gradient (light top → deep bottom) +
     DARK text per mockup 08-dialog-add-moment.png (Petřin pokyn 2026-05-24:
     "gradient měl být vespod, ne diagonální"). Deep teal pools at the bottom. */
  background: linear-gradient(180deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise-deep) 100%);
  color: var(--surface-ink);
  border: 1px solid var(--accent-turquoise);
  border-radius: var(--radius-sm);
  padding: 0.5rem 1.1rem;
  font: inherit;
  font-weight: 700;
  /* --fs-sm (13px) matches .btn-ghost / .btn-warn / .btn-secondary so every
     role button in a footer row renders the SAME height (M12 Dávka-A
     2026-05-25 — Cancel vs "Add your moment" mismatch). Was raw 0.85rem. */
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    0 4px 14px rgba(var(--accent-turquoise-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}
.btn-primary:hover,
.btn-primary:focus-visible {
  background: linear-gradient(180deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise) 100%);
  outline: none;
  box-shadow:
    0 6px 18px rgba(var(--accent-turquoise-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.34);
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift. */
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover {
    transform: translateY(-1px);
  }
}

/* Entity-tone variants of the primary CTA (Petřin pokyn 2026-05-31 — admin
   "+ New X" create buttons take the entity's accent like the popups:
   concert/tour = teal base, online = azure, landmark = gold. Supersedes
   ADR-078's uniform violet for the admin create toolbar). Mirror
   .btn-primary's vertical gradient + glow, swapping only the tone; dark
   label (--surface-ink) inherited from .btn-primary. Apply desktop + mobile. */
.btn-primary--online {
  background: linear-gradient(180deg, var(--accent-online-bright) 0%, var(--accent-online-deep) 100%);
  border-color: var(--accent-online);
  box-shadow:
    0 4px 14px rgba(var(--accent-online-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}
.btn-primary--online:hover,
.btn-primary--online:focus-visible {
  background: linear-gradient(180deg, var(--accent-online-bright) 0%, var(--accent-online) 100%);
  box-shadow:
    0 6px 18px rgba(var(--accent-online-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.34);
}
.btn-primary--gold {
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper-deep) 100%);
  border-color: var(--accent-copper);
  box-shadow:
    0 4px 14px rgba(var(--accent-copper-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}
.btn-primary--gold:hover,
.btn-primary--gold:focus-visible {
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
  box-shadow:
    0 6px 18px rgba(var(--accent-copper-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.34);
}

/* ADR-078 (2026-05-16) — Add/Create role: violet gradient + glow + lift.
   Used napříč: "+ New event" (admin), "+ New place" (admin), future
   Add new entity actions. Same atmospheric pattern jako .btn-primary
   ale violet (matching map identity + Add Pin family). Per Petřin pokyn
   2026-05-16: Add/Create CTAs MUSÍ být uniformly violet napříč systémem. */
.btn-violet {
  appearance: none;
  background: linear-gradient(135deg, var(--accent-violet-bright) 0%, var(--accent-violet) 100%);
  color: var(--text-on-accent);
  border: 1px solid var(--accent-violet-soft);
  border-radius: var(--radius-sm);
  padding: 0.5rem 1.1rem;
  font: inherit;
  font-weight: 700;
  /* --fs-sm — same height as the rest of the role-button family (was raw 0.85rem). */
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    0 4px 14px rgba(var(--accent-violet-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.18);
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}
.btn-violet:hover,
.btn-violet:focus-visible {
  background: linear-gradient(135deg, var(--accent-violet-bright) 0%, var(--accent-violet-bright) 100%);
  outline: none;
  box-shadow:
    0 6px 18px rgba(var(--accent-violet-rgb), 0.38),
    inset 0 1px 0 rgba(238, 240, 250, 0.22);
}
@media (hover: hover) and (pointer: fine) {
  .btn-violet:hover {
    transform: translateY(-1px);
  }
}

/* ==========================================================================
   Noir PR3 primitives — role buttons + chips + banners + segment + accordion.
   Scaffolding consumed by the PR4+ component restyle. Geometry matches the
   existing .btn-primary/.btn-violet/.btn-danger family.

   Role recap (per MOCKUPS + locked Noir color model 2026-05-24 — mockups win
   over old docs when they conflict, Petřin pokyn):
     Save / Add / primary submit → .btn-primary (teal)  ← incl. "Add your moment"
     Destructive                 → .btn-danger  (cihla --danger — Delete/Ban; NEVER magenta)
     System / warn               → .btn-warn    (gold)
     Tertiary                    → .btn-ghost   (transparent, link-like)
     Cancel                      → .btn-secondary (ghost)
   NO violet buttons (.btn-violet retired — Add is teal, not iris).
   Magenta (--accent-mine) reserved for "mine" only — never a button role,
   EXCEPT the "Add a moment" CTA (.add-pin-btn top-bar + mobile FAB): it
   creates YOUR moment, so it carries the mine identity (Petřin pokyn
   2026-05-27, mockup 01-map-hero; desktop now matches the magenta FAB).
   ========================================================================== */

/* Warn — system intervention (Mark as test, Clear name). Gold gradient. */
.btn-warn {
  appearance: none;
  /* Plastic depth — mirrors .auth-btn--primary (canonical gold button) so every
     gold CTA reads identically dimensional, never flat (Petřin pokyn 2026-05-31
     — žádná plochá tlačítka, ani v dialozích; ADR-204). */
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper-deep) 100%);
  color: var(--surface-ink);
  border: 1px solid var(--accent-copper);
  border-radius: var(--radius-sm);
  padding: 0.5rem 1.1rem;
  font: inherit;
  font-weight: 700;
  font-size: var(--fs-sm);
  letter-spacing: var(--ls-wide);
  cursor: pointer;
  box-shadow:
    0 4px 14px rgba(var(--accent-copper-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}
.btn-warn:hover,
.btn-warn:focus-visible {
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
  outline: none;
  box-shadow:
    0 6px 18px rgba(var(--accent-copper-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.34);
}
.btn-warn:disabled { opacity: 0.5; cursor: not-allowed; }
@media (hover: hover) and (pointer: fine) {
  .btn-warn:hover { transform: translateY(-1px); }
}

/* Ghost — tertiary / link-like. Transparent, hairline border, no fill.
   Lighter than .btn-secondary (which carries a hover wash). */
.btn-ghost {
  appearance: none;
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0.95rem;
  font: inherit;
  font-weight: 600;
  font-size: var(--fs-sm);
  letter-spacing: var(--ls-wide);
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease, color 200ms ease;
}
.btn-ghost:hover,
.btn-ghost:focus-visible {
  background: rgba(238, 240, 250, 0.04);
  border-color: var(--border-strong-light);
  color: var(--text-dark);
  outline: none;
}

/* Uppercase micro chip — tour names, platform tags, eyebrow pills.
   Pill shape; tone set via modifier (color = bright sibling, border tinted). */
.chip-uppercase {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  padding: var(--space-1) var(--space-2-5, 0.625rem);
  border-radius: var(--radius-pill);
  background: rgba(238, 240, 250, 0.04);
  border: 1px solid var(--chip-tone, var(--accent-violet));
  color: var(--chip-tone, var(--accent-violet));
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-loose);
  text-transform: uppercase;
  white-space: nowrap;
}
.chip-uppercase--iris    { --chip-tone: var(--accent-violet-soft); --chip-tone-rgb: var(--accent-violet-soft-rgb); }
.chip-uppercase--teal    { --chip-tone: var(--accent-turquoise); --chip-tone-rgb: var(--accent-turquoise-rgb); }
.chip-uppercase--gold    { --chip-tone: var(--accent-copper);    --chip-tone-rgb: var(--accent-copper-rgb); }
.chip-uppercase--magenta { --chip-tone: var(--accent-mine);      --chip-tone-rgb: var(--accent-mine-rgb); }
.chip-uppercase--online  { --chip-tone: var(--accent-online);    --chip-tone-rgb: var(--accent-online-rgb); }
/* Muted variant — STANDALONE / Past pills: low-key grey, reads as a neutral
   state next to the tone-coded tour/live/cancelled chips (M12 Noir PR5 ②). */
.chip-uppercase--muted {
  --chip-tone: var(--text-light-muted);
  color: var(--text-muted);
}

/* Live pill — mint solid with a soft glow (mockup 18 "● LIVE TONIGHT").
   App-wide LIVE treatment via --live; dark ink text for contrast. */
/* M12.X-LIVE-DURATION Stage 3 — the map-popup "Today" chip shares the LIVE
   chip's look + live token exactly (Petřin pokyn 2026-05-25 — same as live,
   just labelled Today; a timeless concert can't go live without a time). */
.chip-live,
.chip-today {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--live);
  color: var(--surface-ink);
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-loose);
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 0 10px rgba(var(--live-rgb), 0.5);
}

/* Danger pill — CANCELLED. Solid brick (--danger), white text (mockup 18
   cancelled variant). Distinct from the tone-outlined .chip-uppercase. */
.chip-danger {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: var(--text-light);
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-loose);
  text-transform: uppercase;
  white-space: nowrap;
}

/* Tone-coded callout banner — info / warn / danger / test / success. */
.banner {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  padding: var(--space-2-5, 0.625rem) var(--space-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--banner-edge);
  background: var(--banner-bg);
  font: var(--text-body);
  line-height: var(--lh-normal);
  color: var(--banner-fg);
}
.banner__icon { flex-shrink: 0; margin-top: 1px; }
.banner__body { flex: 1; }
.banner--info {
  --banner-bg: rgba(var(--accent-turquoise-rgb), 0.07);
  --banner-edge: rgba(var(--accent-turquoise-rgb), 0.20);
  --banner-fg: var(--accent-turquoise);
}
.banner--warn,
.banner--test {
  --banner-bg: rgba(var(--accent-copper-rgb), 0.08);
  --banner-edge: rgba(var(--accent-copper-rgb), 0.30);
  --banner-fg: var(--accent-copper);
}
.banner--danger {
  --banner-bg: rgba(var(--danger-rgb), 0.10);
  --banner-edge: rgba(var(--danger-rgb), 0.33);
  --banner-fg: var(--danger-bright);
}
.banner--success {
  --banner-bg: var(--live-wash);
  --banner-edge: rgba(77, 213, 168, 0.27);
  --banner-fg: var(--live);
}

/* Segmented control (2-3 mutually-exclusive options). */
.segment {
  display: inline-flex;
  gap: var(--space-1);
  padding: var(--space-1);
  border-radius: var(--radius-md);
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
}
.segment__btn {
  appearance: none;
  padding: var(--space-2) var(--space-3-5, 0.875rem);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-weight: var(--fw-medium);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 180ms ease, color 180ms ease, border-color 180ms ease;
}
.segment__btn--active {
  background: linear-gradient(180deg, var(--navy-soft), var(--cream-surface));
  color: var(--accent-turquoise);
  font-weight: var(--fw-bold);
  border-color: rgba(var(--accent-turquoise-rgb), 0.33);
}

/* Segment as a radiogroup — <label> wraps a visually-hidden radio. Active
   state follows :checked so the look matches .segment__btn--active without JS
   toggling a class. NOTE: the only live consumer ("What kind of moment?") was
   removed in ADR-181; this stays as a documented design-system primitive
   (see handoff m13-fullscreen-forms.md) for future segmented controls. */
.segment__btn:has(input[type="radio"]) {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.segment__btn input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.segment__btn:has(input:checked) {
  background: linear-gradient(180deg, var(--navy-soft), var(--cream-surface));
  color: var(--accent-turquoise);
  font-weight: var(--fw-bold);
  border-color: rgba(var(--accent-turquoise-rgb), 0.33);
}
.segment__btn:has(input:focus-visible) {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* Accordion — declarative <details>. Chevron rotates open ↔ closed. */
.accordion {
  border-radius: var(--radius-md);
  background: rgba(238, 240, 250, 0.05);
  border: 1px solid var(--border-on-light);
  overflow: hidden;
}
.accordion__head {
  display: flex;
  align-items: center;
  gap: var(--space-2-5, 0.625rem);
  padding: var(--space-3);
  cursor: pointer;
  list-style: none;
}
.accordion__head::-webkit-details-marker { display: none; }
.accordion__chevron {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--cream-sunken);
  display: grid;
  place-items: center;
  color: var(--accent-turquoise);
  transform: rotate(-90deg);
  transition: transform 200ms ease;
}
.accordion[open] .accordion__chevron { transform: rotate(0deg); }
.accordion__label {
  font-family: var(--font-body);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-sm);
  color: var(--text-dark);
}
.accordion__sub {
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  margin-top: 2px;
}
.accordion__body {
  padding: var(--space-1) var(--space-3) var(--space-3);
  border-top: 1px solid var(--border-on-light);
  display: flex;
  flex-direction: column;
  gap: var(--space-2-5, 0.625rem);
}

/* Dropdown chevron next to the username — must be visibly a chevron,
   not a dust speck. Petřin pokyn 2026-05-12 — the previous ▾ at
   inherited 0.85rem was almost invisible. ▼ is a bolder glyph; CSS
   pulls it up slightly to optical-center it against the cap height
   of "@username" and tints it muted so the name stays primary.
   M10B Stage 2 (STYLE-004) — token-only, fallback hex `#5b3b96`
   removed (it was wrong: actual `--accent-violet` is `#5a3a78`). */
.auth-header__user-chev {
  font-size: 0.7rem;
  line-height: 1;
  margin-left: 0.15rem;
  color: var(--accent-violet-soft);
  transform: translateY(-1px);
  display: inline-block;
  transition: transform 150ms ease;
}

.auth-header__user[aria-expanded="true"] .auth-header__user-chev {
  transform: translateY(-1px) rotate(180deg);
}

.auth-header__user:hover,
.auth-header__user:focus-visible {
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  outline: none;
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift.
   The .auth-header__user-chev translateY-1px (line 5334) is independent —
   that's the chev's optical-centering offset on a child element. */
@media (hover: hover) and (pointer: fine) {
  .auth-header__user:hover {
    transform: translateY(-1px);
  }
}

/* Sign in — solid deep copper (same color & text as Sign up).
   Hierarchy comes from atmospheric effects only: Sign in is flat solid,
   Sign up has gradient + glow + hover lift. */
.auth-btn {
  appearance: none;
  border: 1px solid var(--accent-copper);
  /* Plastic depth — top-cluster parity (Petřin pokyn 2026-05-31): copper top →
     deep copper bottom + glow. White text per the approved preview. */
  background: linear-gradient(180deg, var(--accent-copper) 0%, var(--accent-copper-deep) 100%);
  color: var(--text-light);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  /* M-UX-AUDIT-022 per P-036 (mobile): min-height 44px for Galaxy Fold
     360×640 thumb zone. Padding bumped vertical 0.5 → 0.7rem. */
  padding: 0.7rem 1.1rem;
  min-height: 44px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  /* Lifted-pill depth shared across the top cluster (Petra 2026-05-27 —
     buttons read flat). Float over the LIGHT map, so a defined dark drop
     (ambient + tight contact), not the old copper tint that vanished. */
  box-shadow:
    0 0 18px rgba(var(--accent-copper-rgb), 0.38),
    0 6px 16px rgba(0, 0, 0, 0.40),
    0 2px 5px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(238, 240, 250, 0.25);
  transition: background 200ms ease, border-color 200ms ease;
}

.auth-btn:hover,
.auth-btn:focus-visible {
  /* Keep the plastic gradient on hover (brighter top), not a flat fill. */
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
  border-color: var(--accent-copper-bright);
  outline: none;
}

/* Sign up — copper CTA, consistent with Add Pin and pin-form submit.
   Always the warm-glowing button across the whole app. */
.auth-btn--primary {
  /* Plastic depth — Sign up + Backstage (share this class). Brighter top stop
     than the Sign-in sibling so the primary still reads dominant. */
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper-deep) 100%);
  border-color: var(--accent-copper);
  color: var(--text-light);
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow:
    0 4px 14px rgba(var(--accent-copper-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}

.auth-btn--primary:hover,
.auth-btn--primary:focus-visible {
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
  border-color: var(--accent-copper-bright);
  color: var(--text-light);
  box-shadow:
    0 8px 22px rgba(201, 139, 75, 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.22);
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift. */
@media (hover: hover) and (pointer: fine) {
  .auth-btn--primary:hover {
    transform: translateY(-1px);
  }
}

/* All auth dialogs (login, register, verify, profile, MFA setup/disable/challenge)
   share the same light Frisson look. The ID-prefix selector covers the whole family. */
dialog[id^="auth-"] {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 1.7rem 1.5rem 1.5rem;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  width: min(440px, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

/* When open, stack top-level children vertically. The Profile dialog has
   <h2> + form + section + close — without flex they would render flush. */
dialog[id^="auth-"][open] {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* Profile-family band header (Petřin pokyn 2026-06-04) — the dialog's own
   flex gap supplies the head→body rhythm; the header's base 1rem bottom
   margin would double it (both <768px and with the desktop band). */
dialog[id^="auth-"] .event-create__header {
  margin-bottom: 0;
}
/* The header already reserves padding-right for the ✕ — the generic auth-h2
   2.5rem reservation on top of it would push long titles ("Two-factor
   authentication") into wrapping early. */
dialog[id^="auth-"] .event-create__header h2 {
  padding-right: 0;
}

dialog[id^="auth-"]::backdrop {
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

dialog[id^="auth-"] h2 {
  /* M12 PR6 Fáze 0 — kanonický dialog titul: bílá Cormorant italic 700,
     1.55rem (ADR-179). Gradient byl zrušen už v title sweep; tady jen
     sjednocena velikost (2.3rem → 1.55rem) + opraven stale komentář. */
  font-family: var(--font-display);
  font-size: var(--fs-modal-title); /* big content-modal tier (M13.X-TITLE-TIERS) */
  font-style: italic;
  font-weight: 700;
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  /* M12 PR6 Dávka C — reserve room for the canonical 48px close ✕ (absolute
     top-right, auto-injected by applyModalCloseGuards) so longer titles
     ("Set up two-factor authentication") don't run under it. */
  padding-right: 2.5rem;
  color: var(--text-dark);
}

/* Verify form isn't a flex container (it keeps per-element margins from
   the original signup design), so the h2 needs an explicit gap to the
   rest of the form. Login/Register h2 moved into the band header
   (account-modals 2026-06-04) — the band owns the gap there. */
#auth-verify-form h2 {
  margin-bottom: 1rem;
}

/* Desktop field rhythm — the legacy Register/Login forms aren't flex+gap
   (they keep per-element margins), but the .field rows themselves carried no
   margin, so the gaps between Email / Nickname / Password / Confirm / consent
   were uneven. The gap BETWEEN field groups must be clearly larger than the
   gap WITHIN a group (label→input→hint = .field gap 0.4rem ≈ 6px) so each
   label reads as the start of its own field, not a continuation of the
   previous field's hint (Gestalt proximity). --space-6 (1.5rem = 24px) ≈ 4× the
   intra gap — separates groups without sprawl. M12.X auth review 2026-05-31
   (Petřin pokyn: 16px too tight → over-corrected to 32px "přehnal" → settled on
   24px, the middle, confirmed by an adversarial review agent + mockup measure.
   First pass also wrongly used the undefined --space-lg token = 0). */
#auth-register-form > .field,
#auth-login-form > .field,
#auth-magic-form > .field {
  margin-bottom: var(--space-6);
}

/* Slightly tighter label→input→hint inside auth fields (Petřin pokyn 2026-05-31:
   label sat a touch far from its field). Scoped to NON-checkbox rows so the
   checkbox's box→label gap (0.55rem) stays untouched. */
#auth-register-form > .field:not(.field--checkbox),
#auth-login-form > .field:not(.field--checkbox),
#auth-magic-form > .field:not(.field--checkbox) {
  gap: var(--space-1);
}

/* Inline validation message hugs the field above instead of floating near the
   action row (Petřin pokyn 2026-06-08 — "Wrong email or password" sat too far
   from the Password field). Counteracts the field's --space-6 margin-bottom;
   only the visible one (success XOR error) is ever in flow. */
#auth-login-form > .form-error,
#auth-login-form > .form-success {
  margin-top: calc(var(--space-1) - var(--space-6));
  margin-bottom: var(--space-4);
}

/* Magic dialog: the confirmation/error sits BETWEEN the email field and the
   action row with balanced breathing room on both sides (Petřin pokyn
   2026-06-08 — it was hugging the buttons), not pinned to either. */
#auth-magic-form > .form-success,
#auth-magic-form > .form-error {
  margin-top: calc(var(--space-4) - var(--space-6));
  margin-bottom: var(--space-4);
}

/* The age + consent checkboxes are ONE logical group — keep them tight, not the
   full inter-field gap. The 44px tap target already adds row height, so the
   24px margin on top read as a huge empty hole between the two boxes (Petřin
   pokyn 2026-05-31). Small gap = they read as one block. */
#auth-register-form > .field--checkbox,
#auth-login-form > .field--checkbox {
  margin-bottom: var(--space-2);
  /* The base .field--checkbox forces min-height: 44px (tap target) which, on the
     single-line "16+" row, left ~20px dead space below the text and inflated the
     gap to the consent box into a big hole. Let the row size to its content —
     still a full-width tap row well above WCAG AA (24px). Petřin pokyn 2026-05-31. */
  min-height: 0;
  padding: var(--space-1) 0;
}

/* M16 GDPR — the composer's 16+ row + privacy caution read as ONE block
   (same fix family as the sign-up rule above; Petřin pokyn 2026-06-07
   „obrovská mezera"): kill the 44px tap-target dead space on the
   single-line row and pull the caution up through the body's 0.9rem
   flex gap — but ONLY while the checkbox is visible (anonymous create).
   Registered creators see the caution alone, at normal rhythm. */
#pin-form-age16 {
  min-height: 0;
  padding: var(--space-1) 0 0;
}
#pin-form-age16:not([hidden]) ~ #pin-form-privacy-note {
  margin-top: -0.55rem;
}

dialog[id^="auth-"] h3 {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  margin: 0.4rem 0 0.5rem;
}

/* Forms inside the newer auth dialogs (Profile + MFA family) need explicit
   vertical spacing — they don't have hand-tuned per-element margins like the
   older Register/Login forms do, so without flex+gap their fields render flush. */
#auth-profile-form,
#auth-profile-preferences,
#auth-mfa-setup-form,
#auth-mfa-disable-form,
#auth-mfa-challenge-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;   /* M12 PR6 Fáze 0 — kanonická dialog section gap (ADR-179) */
  margin: 0;
}

dialog[id^="auth-"] input[type="text"],
dialog[id^="auth-"] input[type="email"],
dialog[id^="auth-"] input[type="password"] {
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: var(--cream-sunken);
  color: var(--text-dark);
  font-size: 1rem;
  font-family: inherit;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}

dialog[id^="auth-"] input:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

/* Native <select> — modern flat dropdown with custom turquoise chevron.
   Used for "Where are you from?" (country picker) inside Profile. */
dialog[id^="auth-"] select {
  width: 100%;
  padding: 0.7rem 2.6rem 0.7rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background-color: var(--cream-sunken);
  color: var(--text-dark);
  font: inherit;
  /* M-UX-AUDIT-006 — iOS Safari auto-zoom fix per P-033 (mobile). */
  font-size: 1rem;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  /* SVG chevron in turquoise (#0d7575). Inline data URI = no extra request. */
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none' stroke='%230d7575' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='3 5 7 9 11 5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.85rem center;
  background-size: 14px 14px;
  transition: border-color 200ms ease, box-shadow 200ms ease, background-color 200ms ease;
}

dialog[id^="auth-"] select:focus {
  outline: none;
  background-color: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

dialog[id^="auth-"] select:hover {
  border-color: rgba(var(--accent-turquoise-rgb), 0.35);
}

/* 6-digit codes (email verify, MFA setup confirm, MFA login challenge,
   forgot password reset) — large, spaced, centered for easy typing. */
#auth-verify-dialog input[name="code"],
#auth-mfa-setup-dialog input[name="code"],
#auth-mfa-challenge-dialog input[name="code"],
#auth-forgot-reset-dialog input[name="code"] {
  font-size: 1.4rem;
  letter-spacing: 0.4em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Buttons inside auth dialogs. Default = secondary ghost (Cancel/Close).
   Excludes link-style and Google brand buttons, which keep their own look. */
/* M12.X-PROFILE-FIDELITY — added :not(.dialog-close):not(.contacts-row__remove)
   :not(.contacts-chip__x):not(.contacts-input__add-btn) so this ghost fallback
   stops bleeding its 10px-square border onto the canonical round close button
   (×), the discrete social-link trash icon, the chip × and the teal "Add link"
   pill. All have their own canonical styling; the fallback was overriding it
   via higher specificity (long :not chain). */
dialog[id^="auth-"] button:not(.btn-link):not(.auth-google-btn):not(.auth-magic-btn):not(.btn-primary):not(.btn-violet):not(.btn-danger):not(.btn-warn):not(.password-field__toggle):not(.dialog-close):not(.contacts-row__remove):not(.contacts-chip__x):not(.contacts-input__add-btn):not(.btn-link-pill) {
  appearance: none;
  background: transparent;
  color: var(--text-dark);
  border: 1px solid var(--border-strong-light);
  border-radius: 10px;
  padding: 0.65rem 1.3rem;
  font: inherit;
  /* Canonical button text size (= .btn-primary "Remove from gathering"). */
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background 200ms ease, border-color 200ms ease, transform 200ms ease;
}

dialog[id^="auth-"] button:not(.btn-link):not(.auth-google-btn):not(.auth-magic-btn):not(.btn-primary):not(.btn-violet):not(.btn-danger):not(.btn-warn):not(.password-field__toggle):not(.dialog-close):not(.contacts-row__remove):not(.contacts-chip__x):not(.contacts-input__add-btn):not(.btn-link-pill):hover,
dialog[id^="auth-"] button:not(.btn-link):not(.auth-google-btn):not(.auth-magic-btn):not(.btn-primary):not(.btn-violet):not(.btn-danger):not(.btn-warn):not(.password-field__toggle):not(.dialog-close):not(.contacts-row__remove):not(.contacts-chip__x):not(.contacts-input__add-btn):not(.btn-link-pill):focus-visible {
  background: rgba(238, 240, 250, 0.05);
  border-color: var(--text-dark);
  outline: none;
}

dialog[id^="auth-"] button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Primary action — copper CTA submit, plus MFA Enable button in Profile.
   Solid deep copper = the canonical .auth-btn map "Sign in" treatment
   (mockups 30/31 + Petřin pokyn 2026-05-27: "stejný token jako Sign in na
   mapě"). The :not() chain MIRRORS the ghost rule above (8958) verbatim, so
   the extra [type="submit"] attribute makes this strictly win — earlier the
   ghost rule had grown more :not() clauses than this one and silently
   reclaimed the submit button (navy ghost regression). */
dialog[id^="auth-"] button[type="submit"]:not(.btn-link):not(.auth-google-btn):not(.auth-magic-btn):not(.btn-primary):not(.btn-violet):not(.btn-danger):not(.btn-warn):not(.password-field__toggle):not(.dialog-close):not(.contacts-row__remove):not(.contacts-chip__x):not(.contacts-input__add-btn):not(.btn-link-pill),
#auth-mfa-enable {
  /* Plastic depth — mirrors .auth-btn--primary so the dialog submit (Reset
     password, Verify, Enable 2FA…) reads dimensional like every other CTA,
     never a flat solid block (Petřin pokyn 2026-05-31 — žádná plochá tlačítka
     ani v dialozích; supersedes the 2026-05-27 "solid like map Sign in"; ADR-204). */
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper-deep) 100%);
  color: var(--surface-ink);
  border: 1px solid var(--accent-copper);
  font-weight: 700;
  /* Match the canonical .btn-primary text treatment (Petřin pokyn 2026-05-31,
     ref „Remove from gathering" button) — same Manrope 13px as every other
     button, so auth dialog CTAs read as part of the same product. */
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  padding: 0.7rem 1.5rem;
  box-shadow:
    0 4px 14px rgba(var(--accent-copper-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}

dialog[id^="auth-"] button[type="submit"]:not(.btn-link):not(.auth-google-btn):not(.auth-magic-btn):not(.btn-primary):not(.btn-violet):not(.btn-danger):not(.btn-warn):not(.password-field__toggle):not(.dialog-close):not(.contacts-row__remove):not(.contacts-chip__x):not(.contacts-input__add-btn):not(.btn-link-pill):hover,
dialog[id^="auth-"] button[type="submit"]:not(.btn-link):not(.auth-google-btn):not(.auth-magic-btn):not(.btn-primary):not(.btn-violet):not(.btn-danger):not(.btn-warn):not(.password-field__toggle):not(.dialog-close):not(.contacts-row__remove):not(.contacts-chip__x):not(.contacts-input__add-btn):not(.btn-link-pill):focus-visible,
#auth-mfa-enable:hover,
#auth-mfa-enable:focus-visible {
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
  border-color: var(--accent-copper-bright);
  outline: none;
}

/* Destructive primary — Disable 2FA (red). Applies both to the trigger in
   Profile and to the confirm button in the disable-confirmation dialog. */
#auth-mfa-disable-dialog button[type="submit"],
#auth-mfa-disable {
  background: linear-gradient(180deg, var(--danger) 0%, var(--danger-deep) 100%);
  color: var(--text-light);
  border-color: var(--danger);
  font-weight: 700;
  box-shadow:
    0 4px 14px rgba(var(--danger-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}

#auth-mfa-disable-dialog button[type="submit"]:hover,
#auth-mfa-disable-dialog button[type="submit"]:focus-visible,
#auth-mfa-disable:hover,
#auth-mfa-disable:focus-visible {
  border-color: var(--danger-bright);
  box-shadow:
    0 6px 18px rgba(var(--danger-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.34);
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift. */
@media (hover: hover) and (pointer: fine) {
  #auth-mfa-disable-dialog button[type="submit"]:hover,
  #auth-mfa-disable:hover {
    transform: translateY(-1px);
  }
}

.field--checkbox {
  flex-direction: row;
  align-items: flex-start;
  gap: 0.55rem;
  /* M-UX-AUDIT-036 per P-036 (mobile): expand row hit area to 44px so
     tapping the label / margin counts as toggling the checkbox. Native
     16×16 box stays — just the surrounding row grows. */
  min-height: 44px;
  padding: 0.4rem 0;
  cursor: pointer;
  font-size: 0.875rem;
  line-height: 1.4;
}

.field--checkbox input[type="checkbox"] {
  margin-top: 0.2rem;
  flex-shrink: 0;
  /* WCAG 2.5.5: visible target ≥ 24x24 (Level AA) — bumping checkbox
     itself stays subtle, hit area expansion handled by parent. */
  width: 18px;
  height: 18px;
}

.field--checkbox a {
  /* M12 PR6 Dávka C — was hardcoded #6aa9ff (blue); handoff
     m12-auth-dialogs.md §2 specs inline checkbox links = underlined teal,
     same family as the Google consent line. Tokenized (no #hex). */
  color: var(--accent-turquoise);
  text-decoration: underline;
}

.field__hint {
  /* margin-top 0.3rem REMOVED 2026-05-15 večer (Petřin postřeh „poznámku
     o maximálním počtu znaků username dej blíže ke spodní hraně toho
     fieldu") — parent .field má `gap: 0.4rem`, dodatečný 0.3rem margin
     dělal celkový input→hint odstup 0.7rem (~11px). S removed margin
     je odstup čistě gap 0.4rem (~6px) — tighter, hint visibly bound
     k inputu. */
  display: block;
  font-size: 0.78rem;
  color: var(--text-muted);
  line-height: 1.4;
}

/* M14 unify (Petřin pokyn 2026-06-05) — the POI "AI-drafted" hint is an
   AI info line, so it speaks the canonical AI whisper language (sparkle
   + --text-ai-note + 0.8rem), NOT the generic grey field hint. Scoped
   via [data-desc-hint] so ordinary hints stay untouched. */
.field__hint[data-desc-hint] {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--text-ai-note);
  line-height: 1.35;
}
.field__hint[data-desc-hint]::before {
  content: "✦ ";
}

/* In map popups the AI line stands ALONE under the expandable text
   (after Less/More) — give it breathing room from the toggle (Petřin
   pokyn 2026-06-05, POI i koncert). Detail dialogs keep the inline
   placement right after the paragraph. */
.poi-popup__description .ai-badge,
.event-popup__desc .ai-badge {
  display: block;
  margin: 0.4rem 0 0;
}

/* Gathering-name AI note sits INSIDE the .field (which already spaces
   children via its gap) — drop the note's own top margin so it hugs the
   input („kousek pod fieldem", Petřin pokyn 2026-06-05). */
#pin-form-gathering-name-ai-note {
  margin-top: 0;
}

/* Soft variant — even more subdued, used for the privacy-disclosure
   blurb under the pin-form location preview. */
.field__hint--soft {
  color: var(--text-muted);
  font-style: italic;
}

/* Inline hint used inside privacy-level radio labels, sitting after the
   <strong> name — keeps the radio compact while still explaining each
   option (e.g. "Paris — Eiffel Tower"). */
.field__hint--inline {
  display: block;
  /* M12.X-PROFILE-FIDELITY (Petřin pokyn 2026-05-27) — was thin Cormorant
     (var(--font-display)) italic at 0.78rem muted → illegible under the
     radios. Switched to the body font (Manrope), slightly larger + roomier
     line-height so the example reads cleanly. Keeps light italic for the
     "flavor" the primitives spec wanted, without sacrificing legibility. */
  font-family: inherit;
  font-style: italic;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--text-muted);
  margin-top: 0.2rem;
}

/* Privacy chooser dialog — M10A-11 non-modal-look: transparent backdrop
   so the map preview underneath stays visible while the user clicks
   through the radio options. Positioned bottom-left to keep the
   right-hand side of the map free for the preview rectangle. */
.pin-privacy-chooser-dialog::backdrop {
  background: transparent;
}

.pin-privacy-chooser-dialog {
  position: fixed;
  top: auto;
  bottom: 1.5rem;
  left: 1.5rem;
  right: auto;
  margin: 0;
  max-width: 380px;
  width: calc(100% - 3rem);
  padding: 1.25rem 1.2rem;
  /* M12 PR6 Fáze 0 — sdílený dialog chrome (ADR-179): border 0.15, radius
     token, kanonický 2-rohový mesh. Kotvený box-shadow (vlevo dole, malý
     anchored chooser) zůstává lehčí než centrovaný modal trio. */
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 16px 40px rgba(0, 0, 0, 0.30),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.06);
}

.pin-privacy-chooser-dialog h2 {
  /* Title sizing/weight/family owned by the unified dialog-title rule
     (canonical white Cormorant italic 1.55rem). Only spacing local. */
  margin-bottom: 0.4rem;
}

.pin-privacy-chooser-dialog h3 {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  margin: 0.4rem 0 0.3rem;
}

/* Cancel + Continue buttons inside privacy chooser. */
.pin-privacy-chooser-dialog .form-actions {
  display: flex;
  gap: 0.6rem;
  justify-content: flex-end;
  margin-top: 0.8rem;
}

/* M12 Noir PR6-A — Cancel/Continue use the canonical button system now
   (ghost Cancel + teal .btn-primary Continue) per the locked dialog shell
   (ui-standards.md §2 footer) + m12-dialogs.md rule #4 "Cancel = vždy
   .btn-ghost". The old Frost-era twin-teal override (solid teal Cancel
   beside teal-gradient Continue) made the two actions indistinguishable. */

/* Radio rows — bigger tap targets + hover/selected highlight. */
/* M12.X-PROFILE-FIDELITY — padded rounded rows, hover, selected tint for
   the add-moment chooser. (The Preferences dialog used to share these; it
   now has its own .precision-tile treatment below — account-modals
   redesign 2026-06-04.) */
.pin-privacy-chooser-dialog .field--radio {
  padding: 0.5rem 0.65rem;
  border-radius: 10px;
  transition: background 150ms ease;
}

.pin-privacy-chooser-dialog .field--radio:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.06);
}

.pin-privacy-chooser-dialog .field--radio:has(input:checked) {
  background: var(--accent-turquoise-tint);
}

/* ── Precision tiles (Preferences) — account-modals redesign 2026-06-04.
   Every privacy option is a uniform selectable tile: hairline border at
   rest, teal border + tint + soft ring when selected. Label left, example
   right as a quiet Cormorant-italic column (desktop); on mobile the
   example wraps under the label (see media query below). ── */
/* Doubled class (.field--radio.precision-tile) — the base .field--radio
   rule sits LATER in this sheet and would win the same-specificity tie,
   stripping the tile's horizontal padding / gap / centering (caught on
   the render pass). */
.field--radio.precision-tile {
  align-items: center;
  gap: 0.8rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--border-on-light);
  border-radius: 12px;
  transition: background 150ms ease, border-color 150ms ease,
    box-shadow 150ms ease;
}

.field--radio.precision-tile:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.06);
}

.field--radio.precision-tile:has(input:checked) {
  border-color: var(--accent-turquoise);
  background: var(--accent-turquoise-tint);
  box-shadow: 0 0 0 3px rgba(var(--accent-turquoise-rgb), 0.1);
}

.field--radio.precision-tile input[type="radio"] {
  /* Center with the single-line label (base .field--radio top-aligns
     for multi-line radio rows). */
  margin-top: 0;
}

.precision-tile__label {
  flex-shrink: 0;
  font-weight: 600;
  color: var(--text-dark);
}

.precision-tile:has(input:checked) .precision-tile__label {
  font-weight: 700;
}

/* Example column — Cormorant italic, right-aligned, brighter than the old
   under-label hint (the handoff explicitly bumped size + contrast for
   legibility; no-dim-grey rule). */
.precision-tile__eg {
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: var(--fs-base); /* Cormorant runs small — base, not --fs-sm */
  line-height: 1.3;
  color: var(--text-muted);
}

/* Narrow screens: the long examples ("Paris — Avenue de la Bourdonnais")
   don't fit beside the label — wrap them onto their own line, left-aligned
   under the label text (indent = radio width + tile gap). */
@media (max-width: 767px) {
  .field--radio.precision-tile {
    flex-wrap: wrap;
    row-gap: 0.15rem;
  }
  .precision-tile__eg {
    flex-basis: 100%;
    text-align: left;
    padding-left: calc(13px + 0.8rem);
  }
}

/* Radio option that's currently unavailable (e.g. POI exact at a spot
   without a real landmark). The radio is disabled at the DOM level;
   this just dims the label so the state is visually obvious. */
.field--radio.field--radio--disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.field--radio.field--radio--disabled input {
  cursor: not-allowed;
}

/* "Add a moment" CTA — top-right, next to the auth header.
   Magenta (--accent-mine) per Petřin pokyn 2026-05-27 + mockup 01-map-hero:
   it creates YOUR moment, so it carries the "mine" identity — matching the
   mobile FAB (already magenta) and the own-moment markers. Pill shape like
   its row-mates (Sign in/up, user pill, Backstage). Was violet (ADR-048
   "match the pin color") — superseded; magenta now == own-moment everywhere. */
.add-pin-btn {
  /* Lives inside .top-right-cluster next to the auth header. */
  padding: 0.55rem 1.05rem;
  border: 1px solid var(--accent-mine);
  border-radius: var(--radius-pill);
  /* Plastic depth — parity with the mobile FAB (Petřin pokyn 2026-05-31):
     light magenta top → deep wine bottom + white text + glow. */
  background: linear-gradient(180deg, var(--accent-mine) 0%, var(--accent-mine-deep) 100%);
  color: var(--text-light);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  /* Magenta glow (own-moment identity) + shared lifted-pill depth (defined
     dark drop for the LIGHT map), matching the rest of the top cluster
     (Petra 2026-05-27). */
  box-shadow:
    0 0 20px rgba(var(--accent-mine-rgb), 0.46),
    0 6px 16px rgba(0, 0, 0, 0.40),
    0 2px 5px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(238, 240, 250, 0.25);
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}

/* M12.X-I18N-AUDIT: the FAB-only short label (.add-pin-btn__fab-label,
   key addPin.fabLabel) lives in the DOM for the mobile FAB; on desktop the
   button shows its full ".add-pin-btn__text" label, so hide the FAB word. */
.add-pin-btn__fab-label {
  display: none;
}

.add-pin-btn:hover,
.add-pin-btn:focus-visible {
  transform: translateY(-1px);
  border-color: var(--accent-mine-bright);
  outline: none;
  box-shadow:
    0 8px 22px rgba(var(--accent-mine-rgb), 0.55),
    0 0 0 4px rgba(var(--accent-mine-rgb), 0.20),
    inset 0 1px 0 rgba(var(--accent-mine-rgb), 0.38);
}

.add-pin-btn:active {
  transform: translateY(0);
}

/* M12.X-MAP-ACTIONS (2026-05-28) — legacy ≤767px round 48px FAB block
   removed. mobile.css now owns the mobile Add-pin FAB skin (capsule
   "+ MOMENT" per map-actions-handoff/m14-capsule-fab.md). The old
   block painted a round 48px button + ::before "+" + hid all children
   via `> * { display:none }` — directly colliding with the new capsule
   markup (duplicate +, oversized button, hidden icon span). */
   /* The .map-awaiting-click cursor switch applies the crosshair
   to <body> so the cursor changes over the whole map area. */
body.map-awaiting-click,
body.map-awaiting-click .leaflet-container {
  cursor: crosshair !important;
}

/* Add Pin chooser dialog — canonical dialog shell. Quick/single-decision
   width tier = 440px per ui-standards.md §2 (mockup 07 native is 440). */
dialog#add-pin-dialog {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 1.5rem 1.4rem 1.3rem;
  max-width: 95vw;
  width: 440px;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

/* Cancel button inside Add Pin chooser uses the canonical .btn-ghost
   primitive (M12 Noir PR6-A①) — no per-dialog button override. */

dialog#add-pin-dialog::backdrop {
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* Add Pin dialog title uses the global Aurora-gradient dialog-h2
   treatment (defined at the end of the stylesheet). The old 2-stop
   teal→copper override here was dead — the later Aurora rule shadowed it
   at equal specificity — so it was removed in M12 Noir PR6-A①. */

/* M12 Noir PR6-A① — location-choice cards (mockup 07-dialog-location-chooser,
   m12-dialogs.md §1). Three stacked cards tone-coded by role: magenta =
   own data (GPS), teal = brand (map), iris = community (search). The first
   card (GPS) is highlighted as the default. Replaces the legacy
   .add-pin-option grid cards + emoji glyphs. */
.location-choices {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  margin: 0.6rem 0 1rem;
}

.location-choice {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
  width: 100%;
  padding: 0.85rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);   /* M12 PR6 — byl 14px literál → nejbližší token */
  background: rgba(238, 240, 250, 0.05);
  color: var(--text-dark);
  text-align: left;
  cursor: pointer;
  appearance: none;
  font: inherit;
  transition: transform 200ms ease, background 160ms ease,
              border-color 160ms ease, box-shadow 200ms ease;
}

.location-choice--gps    { --choice-tone: var(--accent-mine); }
.location-choice--map    { --choice-tone: var(--accent-turquoise); }
.location-choice--search { --choice-tone: var(--accent-violet); }

/* Default-highlighted + interactive states all share the tone-tinted look. */
.location-choice--selected,
.location-choice:hover,
.location-choice:focus-visible {
  background: color-mix(in srgb, var(--choice-tone) 9%, transparent);
  border-color: color-mix(in srgb, var(--choice-tone) 40%, transparent);
  outline: none;
}
/* STYLE-M10-005 parity — hover-only lift on precise pointers. */
@media (hover: hover) and (pointer: fine) {
  .location-choice:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 22px color-mix(in srgb, var(--choice-tone) 18%, transparent);
  }
}

.location-choice__glyph {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: 10px;
  display: grid;
  place-items: center;
  color: var(--choice-tone);
  background: radial-gradient(circle at 30% 30%,
    color-mix(in srgb, var(--choice-tone) 33%, transparent),
    var(--cream-surface));
  border: 1px solid color-mix(in srgb, var(--choice-tone) 33%, transparent);
  box-shadow: 0 0 10px color-mix(in srgb, var(--choice-tone) 20%, transparent);
}

.location-choice__body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.location-choice__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 0.9375rem;
  line-height: 1.15;
  color: var(--text-dark);
}

.location-choice__desc {
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--text-muted);
}

.add-pin-search-box {
  margin: 0.5rem 0 1rem;
}

.add-pin-search-box input[type="search"] {
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  background: var(--cream-sunken);
  color: var(--text-dark);
  /* M-UX-AUDIT-008 — iOS Safari auto-zoom fix per P-033 (mobile). */
  font-size: 1rem;
  box-sizing: border-box;
  transition: border-color 200ms ease, box-shadow 200ms ease;
}

.add-pin-search-box input[type="search"]:focus {
  outline: none;
  border-color: var(--accent-turquoise);
  background: var(--cream-surface);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

/* Floating map search bar — input + dropdown styling. Positioning
   for the top-level container `#map-search.map-search` is defined
   higher up (single source of truth, no more duplicate definitions).
   M10E sub-blok 2/4 polish: input wrapper (.map-search__input-wrap)
   houses the input + custom × clear button so we can position the ×
   absolutely without affecting the dropdown below. */
.map-search__input-wrap {
  position: relative;
  display: flex;
  align-items: stretch;
}

.map-search__input {
  width: 100%;
  padding: 0.7rem 2.5rem 0.7rem 1rem; /* right-padding for × clear button */
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  /* Polish iteration 2 (Petřin postřeh 2026-05-15): light Frost theme
     instead of the dark navy gradient leftover from M10A. Atmospheric
     mesh wash matches the M10D modal pattern (cream-surface + multi-
     color halo) so the search bar reads as part of the same surface
     family, not as a dark outlier on the otherwise light app. */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 60%),
    radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 60%),
    var(--cream-surface);
  color: var(--text-dark);
  font-family: var(--font-body);
  /* M-UX-002 (Stage 1 review): 0.95rem (~15.2px) → 1rem (16px). iOS Safari
     auto-zooms viewport when an input gains focus with font-size < 16px;
     bumping to 16px keeps the search bar inside the visible area. */
  font-size: 1rem;
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.12),
    inset 0 1px 0 rgba(238, 240, 250, 0.7);
  box-sizing: border-box;
  transition: border-color 200ms ease, box-shadow 200ms ease;
}

/* Suppress browser-native search clear (we have a custom × per design system). */
.map-search__input::-webkit-search-cancel-button,
.map-search__input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.map-search__input:focus {
  outline: none;
  border-color: var(--accent-turquoise);
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.18),
    0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.16),
    inset 0 1px 0 rgba(238, 240, 250, 0.7);
}

/* Custom × clear button — visible only when input has a value (JS toggles
   the [hidden] attribute). Positioned right-side inside the input. */
.map-search__clear {
  position: absolute;
  top: 50%;
  right: 0.25rem;
  transform: translateY(-50%);
  /* M-UX-AUDIT-019 per P-036 (mobile): 44×44 tap target. */
  min-width: 44px;
  min-height: 44px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 1.25rem;
  line-height: 1;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 150ms ease, color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .map-search__clear:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.10);
    color: var(--accent-turquoise);
  }
}

.map-search__clear:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
}

/* Mobile toggle button — visible only <768px when collapsed (data-state).
   Tap = expand the bar inline; blur on empty input = collapse back.

   ADR-085 (2026-05-16) — Frost glass pebble recipe per P-051 chrome
   family rule. Sibling pair s .menu-toggle (identical treatment, opačná
   strana). Cream-glass + blur(16px) + corner mesh teal + halo-teal +
   turquoise-tint border. */
.map-search__toggle {
  display: none; /* default hidden, JS unhides on mobile collapse */
  width: 44px;
  height: 44px;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.12);
  border-radius: 50%;
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 60%),
    rgba(var(--cream-surface-rgb), 0.78);
  color: var(--text-dark);
  font-size: 1.15rem;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow:
    0 0 14px rgba(var(--accent-turquoise-rgb), 0.18),
    0 4px 14px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(238, 240, 250, 0.55);
  transition: border-color 200ms ease, box-shadow 200ms ease;
}

.map-search__toggle:focus-visible {
  outline: none;
  border-color: var(--accent-turquoise);
  box-shadow:
    0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18),
    0 0 14px rgba(var(--accent-turquoise-rgb), 0.18),
    0 4px 14px rgba(0, 0, 0, 0.10);
}

/* Mobile collapse behaviour. <768px the bar starts collapsed (icon-only
   pill); JS toggles data-state on tap / blur. Desktop ≥768px is always
   "expanded" and the toggle stays hidden. */
@media (max-width: 767px) {
  #map-search.map-search[data-state="collapsed"] {
    width: auto;
  }
  #map-search.map-search[data-state="collapsed"] .map-search__toggle {
    display: inline-flex;
  }
  #map-search.map-search[data-state="collapsed"] .map-search__input-wrap,
  #map-search.map-search[data-state="collapsed"] .map-search__results {
    display: none;
  }
}

/* Specificity bump via #map-search parent: the shared `.geocoder-results`
   rule (used by Preferences default-location search + admin Photon search)
   is declared LATER in this file with a dark navy background, so without
   the ID prefix it would win cascade order. ID parent moves specificity
   to (1,1,0) vs (0,1,0) — wins regardless of order. Same trick applied
   to .map-search__result, :hover, .is-active below. */
#map-search .map-search__results {
  margin-top: 0.3rem;
  /* Same atmospheric mesh family as the input above, slightly stronger
     halo so the dropdown reads as "lifted" over the map. */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 55%),
    var(--cream-surface);
  border-color: var(--border-on-light);
  color: var(--text-dark);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.16);
}

/* Enriched result row — flag + type icon + primary (Cormorant violet
   identifier per Frost typography memory) + secondary admin path.
   Replaces the plain text label from the M10A minimal implementation.
   Polish iteration 2 (2026-05-15): light Frost theme. */
/* #map-search prefix here too — see comment on .map-search__results above. */
#map-search .map-search__result {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem 0.85rem;
  cursor: pointer;
  border-bottom: 1px solid var(--border-on-light);
  color: var(--text-dark);
  transition: background 150ms ease, color 150ms ease;
}

#map-search .map-search__result:last-child {
  border-bottom: none;
}

@media (hover: hover) and (pointer: fine) {
  #map-search .map-search__result:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.08);
    color: var(--text-dark);
  }
}

#map-search .map-search__result.is-active,
#map-search .map-search__result:focus {
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--text-dark);
  outline: none;
}

.map-search__result-flag {
  width: 22px;
  height: 16px;
  border-radius: 2px;
  flex-shrink: 0;
  object-fit: cover;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.10);
  background: rgba(238, 240, 250, 0.04);
}

.map-search__result-flag--placeholder {
  font-size: 0.9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.map-search__result-icon {
  font-size: 0.95rem;
  line-height: 1;
  flex-shrink: 0;
  opacity: 0.78;
  /* Reserve the icon column width even when typeIcon() resolved to an
     empty string — keeps rows with and without an icon vertically
     aligned (Petřin postřeh 2026-05-15). */
  min-width: 1.25rem;
  text-align: center;
}

.map-search__result-content {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1;
}

.map-search__result-primary {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  /* Turquoise semibold on cream = ~5.4:1 (WCAG AA) + heavier weight
     pro lepší čitelnost než violet 500 (Petřin postřeh 2026-05-15 odp.).
     Sjednoceno s tooltip search highlight rodinou. */
  color: var(--accent-turquoise);
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.map-search__result-secondary {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Loading / empty state inside the dropdown — non-clickable status row. */
.map-search__status {
  padding: 0.8rem 0.9rem;
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--text-muted);
  font-style: italic;
  text-align: center;
  cursor: default;
}

/* Recent searches header — sits at top of dropdown when input is empty
   and recent items exist. */
.map-search__recent-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.45rem 0.85rem;
  font-family: var(--font-body);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-on-light);
  cursor: default;
}

.map-search__recent-clear {
  appearance: none;
  background: none;
  border: none;
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  font-family: var(--font-body);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: none;
  cursor: pointer;
  padding: 0;
}

@media (hover: hover) and (pointer: fine) {
  .map-search__recent-clear:hover {
    color: var(--accent-violet-soft);
  }
}

.map-search__recent-clear:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* Brief pulse animation — applied when the user picks "Search for a place"
   in the Add Pin dialog. Draws the eye to the existing search input
   instead of duplicating one inside the modal (Petřin UX call M10A-9). */
@keyframes mapSearchPulse {
  0%, 100% {
    box-shadow:
      0 8px 24px rgba(0, 0, 0, 0.30),
      inset 0 1px 0 rgba(61, 214, 200, 0.08);
  }
  50% {
    box-shadow:
      0 0 0 6px rgba(61, 214, 200, 0.40),
      0 8px 24px rgba(0, 0, 0, 0.35),
      inset 0 1px 0 rgba(61, 214, 200, 0.12);
  }
}

.map-search--pulsing .map-search__input {
  animation: mapSearchPulse 0.6s ease-in-out 4;
}

/* Transient "you picked X" marker dropped on the map after a search
   result is chosen (Petřin postřeh 2026-05-15 "ja chapu, ze mi to našlo
   helsinki, ale ja to nepoznam"). Distinct from the Frisson pin avatars
   so users don't mistake it for a user-placed pin — turquoise dot with
   a brief pulsing ring + a permanent (until auto-clear) tooltip label. */
.search-highlight-marker {
  pointer-events: none;
}

.search-highlight-marker__dot {
  display: block;
  width: 15px;
  height: 15px;
  margin: 4px;
  border-radius: 50%;
  /* M12 Noir 2026-05-28 — deep magenta (--accent-mine-deep, Petřin výběr) replaces
     the neon cyan; a moon rim + dark grounding shadow lift it off the LIGHT
     CartoDB map so it stays legible (the cyan dot used to vanish over water). */
  background: var(--accent-mine-deep);
  border: 2px solid var(--text-light);
  box-shadow:
    0 0 0 3px rgba(var(--accent-mine-rgb), 0.22),
    0 2px 8px rgba(10, 13, 30, 0.50);
  position: relative;
}

.search-highlight-marker__dot::after {
  content: "";
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  border: 2px solid var(--accent-mine-deep);
  opacity: 0;
  animation: searchHighlightPulse 1.4s ease-out 3;
}

@keyframes searchHighlightPulse {
  0% {
    transform: scale(0.6);
    opacity: 0.7;
  }
  100% {
    transform: scale(1.8);
    opacity: 0;
  }
}

/* Reduced-motion guard per WCAG 2.3.3 — no pulse for users who opted out. */
@media (prefers-reduced-motion: reduce) {
  .search-highlight-marker__dot::after {
    animation: none;
  }
}

.search-highlight-tooltip {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600; /* semibold per Petřin postřeh — cream tile maps need contrast */
  letter-spacing: 0.01em;
  /* M12 Noir 2026-05-28 — deep magenta chip sdílí PŘESNĚ barvu dot markeru
     (Petřin pokyn "tečka i background popisku stejné"). Light text on the deep
     plum (--accent-mine-deep) = high contrast on the light map; replaces neon teal. */
  color: var(--text-light);
  background: var(--accent-mine-deep);
  border: 1px solid rgba(238, 240, 250, 0.18);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.75rem;
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
  white-space: nowrap;
}

/* Tooltip arrow — Leaflet renders an ::before pseudo for the directional
   notch. Match the turquoise chip background. */
.search-highlight-tooltip.leaflet-tooltip-top::before {
  border-top-color: var(--accent-mine-deep);
}

/* Input with a leading glyph (account-modals redesign 2026-06-04) — wraps
   an <input> and overlays a quiet icon inside the well (Preferences
   default-location search = teal magnifier). The wrapper is a span so it
   slots into .field's flex column without extra structure. */
.input-affix {
  position: relative;
  display: block;
  /* Consumed by the global .field input rule (style.css ~1666) — a literal
     padding-left here always lost that rule's specificity and the text slid
     under the glyph (fixed 2026-06-07). The var inherits into the input. */
  --input-pad-left: 2.2rem;
}
.input-affix__icon {
  position: absolute;
  left: 0.7rem;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--accent-turquoise);
  opacity: 0.85;
}
.input-affix > input {
  width: 100%;
  /* Fallback for affix inputs OUTSIDE a .field wrapper (none today). */
  padding-left: var(--input-pad-left, 2.2rem);
}

/* Plain hairline between form blocks (Preferences: location vs precision).
   Mirrors the .auth-divider fade without the OR text. */
.form-rule {
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    var(--border-on-light) 18%,
    var(--border-on-light) 82%,
    transparent
  );
}

/* Photon autocomplete dropdown. Used by the Preferences default-location
   search and the floating map search bar. */
.geocoder-results {
  list-style: none;
  margin: 0.3rem 0 0;
  padding: 0;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid rgba(61, 214, 200, 0.16);
  border-radius: var(--radius-md);
  background: var(--navy-deep);
  color: var(--text-light);
}

.geocoder-result {
  padding: 0.6rem 0.9rem;
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--text-light);
  border-bottom: 1px solid var(--border-on-dark);
  transition: background 150ms ease, color 150ms ease;
}

.geocoder-result:last-child {
  border-bottom: none;
}

.geocoder-result:hover,
.geocoder-result:focus {
  background: rgba(61, 214, 200, 0.12);
  color: var(--accent-turquoise-bright);
  outline: none;
}

/* General-purpose info paragraph above a group of fields. Used e.g. for the
   password rule that applies to both Password and Confirm password fields. */
.form-info {
  font-size: 0.75rem;
  color: var(--text-muted);
  line-height: 1.35;
  margin: 0.4rem 0 0.2rem;
}

.dialog-hint {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin-bottom: 1rem;
  line-height: 1.4;
}

.btn-link {
  appearance: none;
  background: none;
  border: none;
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 600;
  padding: 0.4rem 0;
}

.btn-link:hover,
.btn-link:focus-visible {
  color: var(--accent-copper);
  outline: none;
}

/* ==========================================================================
   Auth — federated login (Google) button + divider
   Google brand: white background, dark grey text, official "G" logo.
   ========================================================================== */
.auth-google-btn {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  width: 100%;
  background: #ffffff;
  color: #1f1f1f;
  border: 1px solid #dadce0;
  border-radius: 6px;
  padding: 0.6rem 1rem;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

.auth-google-btn:hover,
.auth-google-btn:focus-visible {
  background: #f7f8f8;
  border-color: #c4c7c5;
  outline: none;
}

.auth-google-btn__logo {
  flex-shrink: 0;
}

/* Magic-link (passwordless) sign-in pill — M17.X-MAGIC-LINK / ADR-280.
   Petřin pokyn 2026-06-08: same width/size as the Google button, but NOT
   loud (no teal — teal is reserved for prominent actions) and NOT looking
   disabled. White is the Google brand chip only, so this is a neutral
   ghost pill: readable --text-dark label + a clear hairline border (the
   same neutral treatment Cancel gets), sized to match .auth-google-btn. */
.auth-magic-btn {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  width: 100%;
  background: transparent;
  color: var(--text-dark);
  /* Mildly stronger than --border-strong-light (0.32) so the pill never
     reads as disabled — derived from --text-dark so it stays token-based
     (Petřin pokyn 2026-06-08 „zvýraznit border, ale fakt mírně"). */
  border: 1px solid color-mix(in srgb, var(--text-dark) 50%, transparent);
  border-radius: 6px;
  padding: 0.6rem 1rem;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  margin-top: 0.55rem;
  transition: background 160ms ease, border-color 160ms ease;
}

.auth-magic-btn:hover,
.auth-magic-btn:focus-visible {
  background: rgba(238, 240, 250, 0.05);
  border-color: var(--text-dark);
  outline: none;
}

.auth-magic-btn__icon {
  flex-shrink: 0;
}

.auth-google__consent {
  font-size: 0.75rem;
  color: var(--text-muted);
  line-height: 1.35;
  margin-top: 0.4rem;
  margin-bottom: 0.9rem;
  text-align: center;
  /* Balance the centered lines so the longer 16+ wording doesn't leave the
     Privacy · Terms links orphaned on their own line (degrades to normal
     wrapping on older browsers). */
  text-wrap: balance;
}

.auth-google__consent a {
  color: var(--accent-turquoise);
  text-decoration: underline;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.4rem 0 0.9rem;
  color: var(--text-muted);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  /* M12 PR6 Dávka C — handoff m12-auth-dialogs.md §1: hairlines fade to
     transparent on the outside (gradient, not a solid rule). */
  background: linear-gradient(to right, transparent, var(--border-on-light));
}

.auth-divider::after {
  background: linear-gradient(to left, transparent, var(--border-on-light));
}

/* Profile dialog — Username form and Security (2FA) section as separate
   "cards" with a hairline divider between them. */
.profile-section {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--border-on-light);
}

.profile-section:first-of-type {
  /* M12.X-PROFILE-FIDELITY — comfortable title→first-field gap (Petřin pokyn
     2026-05-27, after the Preferences subtitle was removed the 0.2rem read
     cramped). Consistent across Profile / Preferences / Security dialogs. */
  padding-top: 0.7rem;
}

.profile-section:last-of-type {
  padding-bottom: 0.2rem;
  border-bottom: none;
}

.profile-section h3 {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  margin: 0 0 0.5rem;
}

.profile-section__status {
  font-size: 0.875rem;
  color: var(--text-dark);
  margin-bottom: 0.7rem;
  line-height: 1.45;
}

/* M12.X-PROFILE-FIDELITY — email value uses the standard value token
   (.profile-section__status → --text-dark); the earlier teal was reverted
   2026-05-27 once field labels themselves took the --label-color accent (value stays the
   primary text colour, label carries the --label-color accent). Keep margin-bottom: 0
   so the canonical 0.9rem form gap alone governs spacing to the next field. */
#auth-profile-email {
  margin-bottom: 0;
}

/* When followed by a field hint (e.g. email + "Not visible in your public
   profile"), drop the bottom margin — default 0.7rem + .field__hint
   margin-top 0.3rem dělalo "strašně odsazený" gap (Petřin postřeh
   2026-05-15 odp.). */
.profile-section__status + .field__hint {
  margin-top: 0.1rem;
}
.profile-section__status:has(+ .field__hint) {
  margin-bottom: 0;
}

/* Right-aligned standalone Close button at the bottom of the Profile dialog. */
.form-actions--close {
  justify-content: flex-end;
  margin-top: 0.2rem;
}

/* QR code area in the Setup 2FA dialog. White background is required so the
   QR scans reliably in any authenticator app. */
.mfa-qrcode {
  display: flex;
  justify-content: center;
  background: #ffffff;
  padding: 0.85rem;
  border-radius: 8px;
}

.mfa-qrcode img,
.mfa-qrcode svg {
  display: block;
  max-width: 220px;
  height: auto;
}

/* "Can't scan? Show secret" disclosure. */
.mfa-manual {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.mfa-manual summary {
  cursor: pointer;
  padding: 0.2rem 0;
  color: var(--accent-turquoise);
  font-weight: 600;
}

.mfa-manual summary:hover,
.mfa-manual summary:focus-visible {
  color: var(--accent-copper);
  outline: none;
}

.mfa-secret {
  display: block;
  margin-top: 0.4rem;
  padding: 0.6rem 0.8rem;
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  word-break: break-all;
  user-select: all;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.88rem;
  color: var(--text-dark);
  letter-spacing: 0.06em;
}

/* ==========================================================================
   Pin popup — Report button + actions row.
   ========================================================================== */

.pin-popup__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.35rem;
  margin-top: 0.6rem;
  padding-top: 0.55rem;
  border-top: 1px solid var(--border-on-light);
}

/* Edit / Delete (only own pin) — small ghost buttons inside the popup.
   Keep them subtle by default, color reveals intent on hover. */
.pin-popup__edit,
.pin-popup__delete,
.pin-popup__report,
.pin-popup__test-data {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0.3rem 0.6rem;
  border-radius: var(--radius-sm);
  transition: color 150ms ease, background 150ms ease, border-color 150ms ease;
}

/* Flask test-data toggle (Petřin pokyn 2026-06-08) — same on/off highlight as
   the feed row flask (.admin-actions__icon--test-on/off): real data = muted
   ghost, test data = gold active fill (--warning). Click flips the whole
   gathering server-side; the audit log captures every flip. */
.pin-popup__test-data--off {
  color: var(--text-muted);
}
.pin-popup__test-data--on {
  color: var(--warning);
  background: rgba(var(--warning-rgb), 0.08);
}
.pin-popup__test-data--off:hover,
.pin-popup__test-data--off:focus-visible,
.pin-popup__test-data--on:hover,
.pin-popup__test-data--on:focus-visible {
  color: var(--warning);
  background: rgba(var(--warning-rgb), 0.18);
  border-color: rgba(var(--warning-rgb), 0.22);
  outline: none;
}

/* Edit — turquoise tone on hover (constructive action). */
.pin-popup__edit:hover,
.pin-popup__edit:focus-visible {
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border-color: rgba(var(--accent-turquoise-rgb), 0.20);
  outline: none;
}

/* Delete — M12 Noir PR5 ③ (Petřin pokyn 2026-05-25): unified with the concert
   popup toolbar (.event-actions__btn--delete). Borderless ghost muted at rest,
   danger-red tint on hover. The popup toolbar is an icon-only row where every
   action shares the ghost-at-rest / tone-on-hover language; the destructive
   signal lives in the red HOVER, matching the concert popup the user pointed
   at as the reference (supersedes the Frost solid-red treatment here). */
.pin-popup__delete {
  background: transparent;
  color: var(--text-muted);
  border-color: transparent;
}
.pin-popup__delete:hover,
.pin-popup__delete:focus-visible {
  color: var(--danger-text);
  background: rgba(var(--danger-rgb), 0.10);
  border-color: rgba(var(--danger-rgb), 0.30);
  outline: none;
}

/* M11B Stage M follow-up 2026-05-22 evening (Petřin pokyn) — report
   ikona NENÍ destructive akce z user pohledu (user signalizuje problém,
   neničí obsah). Agresivní red outline + filled hover čte jako "click
   this and something bad happens" — což je opak: reportování = bezpečná
   submission. Neutralizováno na muted ghost s decentním red wash na
   hover (= drobný signál destructive intent, ne jako celé tlačítko).
   Předchozí STYLE-M10-004 pravidlo (uniform-red pro report) reverted
   po Petřině feedback "vizuálně příliš agresivní, neodpovídá zbytku
   ikonografie aplikace". Delete tlačítko zůstává solid danger red
   (= ono opravdu destructive). */
.pin-popup__report {
  color: var(--text-muted);
  border-color: transparent;
}
.pin-popup__report:hover,
.pin-popup__report:focus-visible {
  color: var(--danger-text);
  background: var(--danger-soft);
  border-color: rgba(var(--danger-rgb), 0.22);
  outline: none;
}

/* M11B Stage M follow-up 2026-05-22 evening (Petřin pokyn) — reported
   pin highlight: a moderator sees the flag in red on any pin that
   carries at least one pending report. Color signals "this needs your
   attention"; clicking opens the moderation popup (Approve / Dismiss)
   instead of the report form. Non-mod viewers never receive the
   `.is-reported` class (frontend gate in pinAdminActions:isPinReportedSync,
   backend gate via `pins` permission on /admin/reported-pin-ids). */
/* M12D.X-REPORT-INDICATORS (bod 3 — Petřin pokyn 2026-05-30): the moderator's
   "this pin IS reported" indicator must be the STRONGEST state, not the weakest.
   Previously `.is-reported` resting was a faint 10% wash + thin border — visually
   indistinguishable from the (deliberately subtle, 2026-05-22) hover of a normal
   report button, so a flagged pin read DIMMER than a mouse-over. Inverted: resting
   reported = SOLID danger fill + halo (loud, scannable at a glance, same "must stay
   visible" treatment cancelled gets, ui-standards §180). Hover is now just a minor
   brighten, no longer the only loud state. The plain report affordance (filing a
   report) stays muted per the 2026-05-22 decision — only the reported STATE shouts. */
.pin-popup__report.is-reported {
  color: var(--text-on-accent);
  border-color: var(--danger);
  background: var(--danger);
  box-shadow: var(--halo-danger);
}
.pin-popup__report.is-reported:hover,
.pin-popup__report.is-reported:focus-visible {
  color: var(--text-on-accent);
  background: var(--danger-bright);
  border-color: var(--danger-bright);
  box-shadow: var(--halo-danger);
  outline: none;
}
.pin-popup__row-report.is-reported {
  color: var(--text-on-accent);
  background: var(--danger);
  box-shadow: var(--halo-danger);
}

/* M11.X-ANCHOR-AUDIT (2026-05-22) — text labels swapped for SVG icons.
   Square padding + flex centering so every button in the action row
   renders as a consistent ~30x30 icon target. SVG inherits currentColor
   so per-button color rules (turquoise edit, red delete, danger report)
   carry through to the stroke. */
.pin-popup__edit,
.pin-popup__delete,
.pin-popup__report,
.pin-popup__add-child,
.pin-popup__mod-delete {
  padding: 0.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}
/* M12 Noir PR5 ③ (Petřin pokyn 2026-05-25) — popup footer icons unified with
   the concert popup toolbar (.event-actions--popup): BORDERLESS ghost at rest
   (transparent, muted), tone tint only on hover. No resting tile/border. */
.pin-popup__edit svg,
.pin-popup__delete svg,
.pin-popup__report svg,
.pin-popup__add-child svg,
.pin-popup__mod-delete svg,
.pin-popup__test-data svg {
  display: block;
}

/* M11.X-ANCHOR-AUDIT (2026-05-22) — "+" Add child button. Constructive
   (= positive intent), turquoise hue parita s .pin-popup__edit (= same
   "constructive action" hierarchy). Anon viewers never render this
   button (owner-gated in buildPopupHtml). */
.pin-popup__add-child {
  appearance: none;
  background: none;
  border: 1px solid transparent;
  font-family: inherit;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color 150ms ease, background 150ms ease, border-color 150ms ease;
}
.pin-popup__add-child:hover,
.pin-popup__add-child:focus-visible {
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border-color: rgba(var(--accent-turquoise-rgb), 0.20);
  outline: none;
}

/* ─────────────────────────────────────────────────────────────────────
   M12.X-OPEN-GATHERINGS (ADR-184) — "Allow others to join?" form opt-in +
   open-gathering popup surfaces (badge, participant avatars, join CTA,
   per-row author). Tones reuse the canon: iris = fan/other, magenta = mine.
   ───────────────────────────────────────────────────────────────────── */
/* Form opt-in card — mirrors the .test-data-field idiom (a bordered toggle
   row with a label + hint) so the form reads as one system. */
.open-gathering {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.open-gathering__toggle {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
}
.open-gathering__toggle input[type="checkbox"] {
  margin-top: 2px;
  flex: 0 0 auto;
  /* Teal to match the rest of this flow (Save CTA + other form checkboxes),
     not the iris/magenta gathering tones (Petřin pokyn 2026-05-28). */
  accent-color: var(--accent-turquoise);
}
.open-gathering__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-0-5);
}
.open-gathering__label {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text-dark);
}
.open-gathering__hint {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  line-height: 1.4;
}
.open-gathering__name-field {
  /* slight inset so the revealed field reads as a child of the toggle */
  margin-left: calc(var(--space-2) + 1rem);
}
.open-gathering--in-dialog {
  margin: var(--space-3) 0;
}

/* M13.X-GATHERING-NAME (ADR-245) — the ~1-2s "AI is thinking of a name"
   state. Purely a placeholder treatment (the input never locks): the hint
   text breathes gently so the wait reads as activity, not a frozen form.
   Shared by the composer field and the rename dialog. */
.field__input--ai-thinking::placeholder {
  animation: ai-thinking-pulse 1.2s ease-in-out infinite;
}
@keyframes ai-thinking-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

/* "Open gathering" chip in the popup header. */
.pin-popup__open-badge {
  /* M12.X (Petřin pokyn 2026-05-30) — the Open chip now sits INLINE next to the
     gathering name + TEST chip, as a sibling chip of the SAME size. Metrics match
     `.test-data-pill` exactly (font-size / padding / line-height / tracking) so
     the two chips read as one set; only the tone differs (teal vs gold). */
  display: inline-block;
  vertical-align: middle;
  margin-left: 0.5rem;
  padding: 0.1rem 0.5rem;
  border-radius: var(--radius-pill);
  /* M12.X (Petřin pokyn 2026-05-30) — match `.test-data-pill` EXACTLY. The chip
     sits inside the Cormorant-italic name span, so it MUST pin font-family +
     font-style normal or it inherits the serif italic (the regression Petra
     flagged). Same metrics as TEST: body sans, upright, 0.56rem / 0.10em / 700. */
  font-family: var(--font-body);
  font-style: normal;
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  line-height: 1.5;
  text-transform: uppercase;
  /* M12 gathering cleanup (Petřin pokyn 2026-05-28) — "Open" is a STATUS, not
     an ownership signal, so it no longer flips magenta on your own gathering.
     One consistent teal live/open tone on every viewer. */
  color: var(--live);
  background: rgba(var(--live-rgb), 0.14);
  border: 1px solid rgba(var(--live-rgb), 0.34);
}

/* Stacked participant avatars under the header identity. M12 gathering cleanup
   (Petřin pokyn 2026-05-28) — a "Joined" label leads the stack so it reads as
   "these fans joined", not a floating orb. */
.pin-popup__participants {
  display: flex;
  align-items: center;
  margin-top: var(--space-2);
}
.pin-popup__participants-label {
  margin-right: var(--space-2);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.pin-popup__participant-avatar {
  /* ADR-192 — bare orb, per-avatar glow via shared "Avatar unification"
     rule. The navy border + glass plate were the Frost-era plate
     treatment; on Noir they fought with the avatar's own colour. */
  width: 22px;
  height: 22px;
  border-radius: 50%;
  margin-left: -6px;
  object-fit: cover;
}
.pin-popup__participant-avatar:first-child,
.pin-popup__participants-label + .pin-popup__participant-avatar {
  margin-left: 0;
}
.pin-popup__participant-more {
  margin-left: var(--space-1);
  font-size: var(--fs-2xs);
  font-weight: 700;
  color: var(--text-muted);
}

/* Join CTA — non-owner on an open gathering. Compact magenta PILL (Petřin pokyn
   2026-05-30 — the old full-width slab dominated the popup over the echoes and
   got pushed off-screen as the list grew). Keeps the --accent-mine identity +
   gradient/glow, but shrinks to its content and centres under the scrollable
   list, which is now the visual lead. */
/* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — the gathering's anchored
   add/join CTA: a full-width MAGENTA primary button ABOVE the action toolbar
   (magenta = adding an echo TO a gathering; the standalone "leave a moment" stays
   teal via the toolbar "+"). Same height/shape as the popup's primary CTA
   (.popup__cta / the "I'm going" button) — padding 0.5rem 1.1rem ≈ 34px,
   --radius-sm, --fs-sm bold, filled vertical gradient + glow + dark ink — and
   mirrors the mobile .fm-gathering-cta so popup ↔ sheet match. */
.pin-popup__cta-add {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  margin: var(--space-3) 0 0;
  padding: 0.5rem 1.1rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent-mine) 90%, white 10%) 0%,
    color-mix(in srgb, var(--accent-mine) 70%, black) 100%);
  color: var(--surface-ink);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    0 4px 14px color-mix(in srgb, var(--accent-mine) 42%, transparent),
    inset 0 1px 0 rgba(var(--moon-rgb, 238, 240, 250), 0.34);
  transition: transform 200ms ease, box-shadow 200ms ease;
}
.pin-popup__cta-add svg { width: 18px; height: 18px; flex-shrink: 0; }
@media (hover: hover) and (pointer: fine) {
  .pin-popup__cta-add:hover { transform: translateY(-1px); }
}
/* Anonymous sign-in nudge — link-look (underline default), per
   feedback_anon_nudge_link_look. */
.pin-popup__join-nudge {
  display: inline-block;
  margin-top: var(--space-3);
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.pin-popup__join-nudge:hover,
.pin-popup__join-nudge:focus-visible {
  color: var(--accent-turquoise-bright);
  outline: none;
}

/* (Removed: .pin-popup__join / __join-row / __join-stat — the compact join pill +
   fan-count row were replaced by the single anchored .pin-popup__cta-add above.
   Petřin pokyn 2026-05-31.) */

/* Per-row author line in an open gathering (whose moment is this). M12 gathering
   cleanup (Petřin pokyn 2026-05-28) — the author AVATAR now leads the row (its
   own grid column); this line is just the @nick, sitting ABOVE the comment so
   identity reads first. Tone is VIEWER-RELATIVE: violet by default (someone
   else's moment), magenta when it's the viewer's own (--mine) — the same moment
   reads magenta to its author and violet to everyone else. */
.pin-popup__preview-author {
  display: inline-flex;
  align-items: center;
  /* M12 — explicit body font so the @nick never inherits the roman moment
     serif used by the sibling .pin-popup__preview-text (Petřin pokyn 2026-05-28
     "username začal přebírat font roman"). */
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.1;
  color: var(--nick-color);
}
.pin-popup__preview-author--mine {
  color: var(--accent-mine);
}
/* M12.X (Petřin pokyn 2026-05-31) — the ✦ sparkle is the gathering FOUNDER's
   mark: it sits in front of the @nick of the root user who STARTED the gathering,
   identically to the header author line. It is NOT a "mine" mark — a participant's
   own echo carries no ✦, and the founder's echoes carry it for every viewer. So
   it is gated on `--founder`, kept SEPARATE from the `--mine` colour tone (the ✦
   inherits whatever tone the row already has: magenta when the founder is also the
   viewer, violet otherwise — no colour added). Mirrors `.pin-popup__by-nick::before`.
   Supersedes the 2026-05-30 `--mine::before` call, which wrongly tied ✦ to the viewer. */
.pin-popup__preview-author--founder::before {
  content: "✦";
  margin-right: 0.3rem;
  font-size: 0.65rem;
  vertical-align: 1px;
}
.pin-popup__preview-author-nick {
  line-height: 1;
}
/* Leading author avatar for open-gathering moment rows (replaces the note glyph
   when authors differ per row). The shared "Avatar unification" glow rule above
   gives it the standard halo (Petřin pokyn 2026-05-28 "avatar nemá tu zář"). */
.pin-popup__preview-avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  margin-top: 1px;
  flex-shrink: 0;
}

/* Move-pin banner: shown after the user clicks "Move pin" inside the
   edit dialog. Anchors to the top of the viewport, above the map so the
   user always sees the instruction while picking the new spot. */
/* Instruction banner — navy with turquoise hairline + Cancel link.
   Drama on purpose: the user is in "pick a new location" mode and the
   banner is the only thing telling them what to do. */
.pin-move-banner {
  position: fixed;
  top: 1rem;
  left: 50%;
  transform: translateX(-50%);
  background: linear-gradient(180deg, var(--navy-deep) 0%, var(--navy-soft) 100%);
  color: var(--text-light);
  padding: 0.75rem 1.2rem;
  border: 1px solid rgba(61, 214, 200, 0.20);
  border-radius: var(--radius-pill);
  box-shadow:
    0 12px 30px rgba(0, 0, 0, 0.40),
    0 0 0 1px rgba(61, 214, 200, 0.06);
  display: flex;
  align-items: center;
  gap: 1rem;
  z-index: 1100;
  font-size: 0.9rem;
  font-weight: 500;
}

.pin-move-banner .btn-link {
  color: var(--accent-turquoise-bright);
  font-size: 0.85rem;
  padding: 0.1rem 0.4rem;
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  font-weight: 600;
}

.pin-move-banner .btn-link:hover,
.pin-move-banner .btn-link:focus-visible {
  color: var(--accent-copper-bright);
}

/* Crosshair the cursor while the user is picking the new pin location.
   Applied to <body> so it covers the map and any UI bleed. */
body.pin-move-cursor,
body.pin-move-cursor .leaflet-container {
  cursor: crosshair !important;
}

/* M11.X-MINE-HIGHLIGHT (ADR-152) Stage 4 — .my-pins-list / .my-pin-card*
   rules removed alongside the auth-mypins-dialog. Pin management surface
   moved into the Pins tab (.feed-list__item--mine permanent tint +
   .feed-list__inline-actions ghost icon block). */

/* Edit-only fields inside #pin-form-dialog */
#pin-form-location {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

#pin-form-location-value {
  /* Sans-serif inherited from parent for legibility; tabular-nums keeps
     GPS coordinates digit-aligned without needing monospace. */
  font-variant-numeric: tabular-nums;
  font-size: 0.95rem;
  /* Read-only — location is fixed by the Add-a-Moment flow, not typed here.
     This ID rule wins over .pin-form__location-value, so the token lives here. */
  color: var(--text-readonly);
  margin: 0;
}

#pin-form-move-btn {
  align-self: flex-start;
  margin-top: 0.2rem;
}

/* ==========================================================================
   Report dialog — Frisson light theme. Submit stays red (destructive action).
   ========================================================================== */

dialog#report-form-dialog {
  /* M12 PR6 Dávka D — canonical dialog shell (ADR-179). The Frost-era red-
     tinted chrome (danger border/mesh/shadow + literal 20px radius) is
     retired; in Noir the destructive intent rides on the red "Send report"
     button (.btn-danger), not the whole frame. Teal hairline + teal/violet
     mesh + atmospheric halo, identical to every other dialog. */
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 0;
  max-width: 95vw;
  width: 440px;
  max-height: 90dvh;
  /* App-wide sweep 2026-05-19 (per pin-form-dialog dual-scrollbar fix) —
     #report-form already declares overflow-y:auto for its own scroll
     region; suppress the UA default dialog overflow so only one
     scrollbar renders. */
  overflow: hidden;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

dialog#report-form-dialog::backdrop {
  /* Canonical night-sky backdrop (ADR-179) — teal radial + black, blur 6px. */
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

#report-form {
  padding: 1.5rem 1.4rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  max-height: inherit;
  overflow-y: auto;
}

#report-form h2 {
  /* M12 PR6 Dávka D — canonical dialog title (ADR-179): Cormorant italic 700.
     padding-right clears the auto-injected 48px .dialog-close. Report = small
     quick-action tier (M13.X-TITLE-TIERS — Petřin revidovaný výběr 2026-06-03,
     supersedes the earlier same-day "Report stays big" call). */
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-dialog-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0 0 0.2rem;
  padding-right: 2.8rem;
  color: var(--text-dark);
}

#report-form textarea {
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  color: var(--text-dark);
  font: inherit;
  /* M-UX-AUDIT-007 — iOS Safari auto-zoom fix per P-033 (mobile). */
  font-size: 1rem;
  width: 100%;
  resize: vertical;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}

#report-form textarea:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

/* M12 PR6 Dávka D — the bespoke ghost/red button rules that scope-bled onto
   every #report-form button (incl. a hardcoded red literal) are gone. Cancel
   uses the canonical .btn-secondary, "Send report" the canonical .btn-danger
   (red role color preserved, now from the shared class). */

/* Reason radio rows — compact, full-width tap targets. */
.field--radio {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  font-size: 0.9rem;
  line-height: 1.4;
  padding: 0.4rem 0;
  cursor: pointer;
}

.field--radio input[type="radio"] {
  margin-top: 0.2rem;
  flex-shrink: 0;
  accent-color: var(--accent-turquoise);
}

/* ==========================================================================
   Turnstile widget mount slot — used in pin-form and report-form dialogs.
   The Cloudflare iframe sets its own dimensions; we just give it room.
   ========================================================================== */

.turnstile-slot {
  display: flex;
  justify-content: center;
  /* Compact wrapper per Petřin feedback 2026-05-14 ("cloudfare ma obrovsky
     prostor"). Turnstile widget is a Cloudflare iframe at fixed ~300×65px;
     we scale down the visual footprint without breaking the widget.
     The transform-origin keeps the iframe centred. */
  margin: 0.3rem 0;
}

.turnstile-slot > * {
  transform: scale(0.85);
  transform-origin: top center;
  margin-bottom: -10px; /* compensate the scaled-empty space below */
}

.turnstile-slot[hidden] {
  display: none;
}

/* ==========================================================================
   Destructive action buttons (Delete account, Delete forever) — red.
   Reuses the auth dialog button base; just overrides colours.
   ========================================================================== */

/* Destructive action button (Delete my account) — red gradient with glow.
   Same pattern as report-form submit + Disable 2FA. */
.btn-danger,
dialog button.btn-danger {
  appearance: none;
  background: linear-gradient(180deg, var(--danger) 0%, var(--danger-deep) 100%);
  color: var(--text-light);
  border: 1px solid var(--danger);
  /* Match .btn-primary geometry — round corners + Frost padding so the
     button doesn't read as a flat UA-default block inside frost-modal. */
  border-radius: var(--radius-sm);
  padding: 0.5rem 1.1rem;
  font: inherit;
  font-weight: 700;
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow:
    0 4px 14px rgba(var(--danger-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
  transition: border-color 200ms ease, box-shadow 200ms ease, transform 200ms ease;
}

.btn-danger:hover,
.btn-danger:focus-visible,
dialog button.btn-danger:hover,
dialog button.btn-danger:focus-visible {
  border-color: var(--danger-bright);
  box-shadow:
    0 6px 18px rgba(var(--danger-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.34);
}
/* STYLE-M10-005 (M10 closure Stage 2 audit 2026-05-16): hover-only lift. */
@media (hover: hover) and (pointer: fine) {
  .btn-danger:hover,
  dialog button.btn-danger:hover {
    transform: translateY(-1px);
  }
}

/* Warning banner inside delete-confirm dialog. Red-tinted background +
   border so the user clearly registers "this is the danger zone". */
.dialog-hint--danger {
  background: rgba(var(--danger-rgb), 0.12);
  border: 1px solid rgba(var(--danger-rgb), 0.4);
  color: var(--danger-text);
  padding: 0.7rem 0.9rem;
  border-radius: 8px;
  font-size: 0.875rem;
  line-height: 1.45;
  margin-bottom: 1rem;
}

/* Inline code chip used in the "Type DELETE to confirm" prompt. */
.confirm-token {
  display: inline-block;
  padding: 0.05rem 0.35rem;
  margin: 0 0.15rem;
  background: var(--surface-ink);
  border: 1px solid var(--border-on-light);
  border-radius: 4px;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.85em;
  color: var(--danger-text);
  letter-spacing: 0.05em;
}

/* ──────────────────────────────────────────────────────────────────
   Admin page — /admin.html moderation queue
   ────────────────────────────────────────────────────────────────── */

/* Override base `html, body { overflow: hidden; height: 100dvh }` for admin
   views. Both <html> AND <body> need to relax — body alone leaves the html
   element clipped (= can't scroll). :has() selector targets only the admin
   page's html, leaving the main app's fixed-viewport unchanged.

   M15.X-ADMIN-HEADER fix (2026-06-05): body must be `overflow: visible`,
   NOT `auto` — with auto the body counts as a scroll container, so every
   position:sticky inside it (page header, mobile tab strip, the new pane
   bands) pins to the BODY scrollport… which never scrolls (height:auto =
   body grows with content; the actual scrolling happens on the viewport).
   Result: sticky chrome silently scrolled away since BS-2. html keeps
   `auto` (it IS the scrolling element). */
html:has(body.admin-body) {
  overflow: auto !important;
  height: auto !important;
}
body.admin-body {
  overflow: visible !important;
  height: auto !important;
}

.admin-body {
  /* M12 rollout — Frisson Frost light atmospheric (= consistent s main app),
     ne dark theme. Po Petřin "musis predelat vsechno". */
  background: var(--cream-base);
  background-image:
    radial-gradient(at 12% 8%, var(--frost-wash-teal) 0%, transparent 50%),
    radial-gradient(at 88% 25%, var(--frost-wash-plum) 0%, transparent 55%),
    radial-gradient(at 50% 95%, var(--frost-wash-copper) 0%, transparent 60%);
  background-attachment: fixed;
  color: var(--text-dark);
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.admin-header {
  /* M10 closure Session #7 admin polish (2026-05-16) — Petřin canonical spec:
     „Na samostatných stránkách je Back to Map vždycky vlevo, nadpis uprostřed."
     3-col grid layout (back-link left / title center / spacer right) drží
     title strictly centered vis-a-vis viewport i když back-link != spacer width.
     (Modaly nepoužívají tento pattern — mají vlastní close × top-right.) */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 1.2rem 1.5rem;
  background: rgba(var(--cream-surface-rgb), 0.85);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-on-light);
  gap: 1rem;
  position: sticky;
  top: 0;
  z-index: 10;
}

.admin-header__title {
  /* M12 PR6 — bílá Cormorant italic 700, žádný gradient (sjednoceno per
     hard rule §1.2). Page header (ne dialog) → velikost 2.2rem zůstává.
     Center column of 3-col grid — visually centered ve viewport-u. */
  margin: 0;
  font-family: var(--font-display);
  font-size: 2.2rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: 1.1;
  text-align: center;
  font-style: italic;
  color: var(--text-dark);
}

.admin-header__back {
  justify-self: start;
  color: var(--accent-turquoise);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.4rem 0.8rem;
  border-radius: var(--radius-sm);
  transition: background 160ms ease;
}

.admin-header__back:hover,
.admin-header__back:focus-visible {
  background: var(--accent-turquoise-tint);
  outline: none;
}

.admin-header__spacer {
  /* Mirrors back-link column width — drží title strictly centered ve viewportu. */
}

.auth-header__admin {
  /* Backstage link in the main app header — anchor element styled as
     a button. Strip default underline so it matches sibling auth-btn
     <button> elements (Petřin pokyn 2026-05-22). */
  text-decoration: none;
}

.admin-main {
  flex: 1;
  padding: 1.25rem;
  max-width: 1280px;
  margin: 0 auto;
  width: 100%;
  box-sizing: border-box;
}

.admin-gate {
  text-align: center;
  padding: 3rem 1.25rem;
  color: var(--text-muted);
  font-size: 1rem;
}

.admin-gate p {
  margin: 0 0 1.25rem;
}

.admin-toolbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}

.admin-toolbar__filter {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.admin-toolbar__filter select {
  background: var(--cream-surface);
  color: var(--text-dark);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.7rem;
  font-size: 0.9rem;
  font-family: var(--font-body);
}

.admin-toolbar__filter select:focus {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
  border-color: var(--accent-turquoise);
}

/* .admin-toolbar__count removed (M15.X-ADMIN-HEADER, ADR-263) — every tab
   count now lives in the sticky pane band as .admin-pane__band-count. */

.admin-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
}

@media (min-width: 960px) {
  .admin-layout {
    grid-template-columns: 1.4fr 1fr;
  }
}

.admin-list {
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-teal) 0%, transparent 50%),
    radial-gradient(at 0% 100%, var(--frost-wash-plum) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}

/* Admin empty state — M10 closure Session #7 admin polish (2026-05-16).
   FIX: Pre-fix bare <p> text-only. Post-fix: soft atmospheric Frost wash +
   Cormorant italic accent + larger breathing room. Aligned s composite empty
   state pattern (P-019) used v events panel. */
.admin-empty {
  padding: 3rem 1.5rem;
  text-align: center;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.1rem;
  margin: 0;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

/* ============================================================
   Admin classic pagination — top + bottom of each admin table
   (per ADR-142 R2, M11.X-ADMIN-VIEW-PAGING). Pager auto-mounts
   as a sibling of <table.admin-table>; the design-system polish
   beyond this functional baseline lands in M12C.
   ============================================================ */
.admin-pagination {
  display: flex;
  align-items: center;
  /* Drop justify-content: space-between (= caused „Per page" to appear
     in the middle when both nav + info exist but stick to the left edge
     when nav is hidden). Petřin pokyn 2026-05-21 — unified position
     across all tables. Now: nav + selector are on the left in flow order,
     info is pushed right via `margin-left: auto` on .admin-pagination__info.
     Single-page or multi-page: selector stays in the same spot. */
  gap: 1rem;
  /* Horizontal padding bumped 0.25rem → 1.25rem (Petřin pokyn 2026-05-21
     audit log smoke — „tlačítka a popisky nalepené k okrajům"). Matches
     the visual breathing room of admin-row cells (1rem) plus a touch. */
  padding: 0.85rem 1.25rem;
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--text-muted);
  flex-wrap: wrap;
}

.admin-pagination__nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex-wrap: wrap;
}

.admin-pagination__btn {
  appearance: none;
  border: 1px solid var(--border-on-light);
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-weight: 500;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.55rem;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .admin-pagination__btn:hover:not(.admin-pagination__btn--disabled):not(.admin-pagination__btn--active) {
    background: var(--cream-mist);
    border-color: var(--text-dark);
  }
}

.admin-pagination__btn--active {
  background: var(--text-dark);
  color: var(--cream-mist);
  border-color: var(--text-dark);
  cursor: default;
}

.admin-pagination__btn--disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.admin-pagination__gap {
  padding: 0 0.25rem;
  color: var(--text-muted);
  user-select: none;
}

.admin-pagination__info {
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  color: var(--text-muted);
  white-space: nowrap;
  /* Push to right edge regardless of which siblings precede it (nav
     present or hidden). Petřin pokyn 2026-05-21 — unified layout. */
  margin-left: auto;
}

/* Per-page selector (ADR-145) — small inline label + native <select>.
   Native dropdown UI on iOS/Android = friendly + accessible; we style
   only the border + font, not the picker chrome itself. */
.admin-pagination__size {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  white-space: nowrap;
}
.admin-pagination__size-label {
  font-family: var(--font-body);
}
.admin-pagination__size-select {
  appearance: auto;
  border: 1px solid var(--border-on-light);
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-weight: 500;
  height: 2rem;
  padding: 0 0.5rem;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: border-color 140ms ease;
}
.admin-pagination__size-select:hover {
  border-color: var(--text-dark);
}
.admin-pagination__size-select:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
}

@media (max-width: 640px) {
  .admin-pagination {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
  }
  .admin-pagination__nav {
    justify-content: center;
  }
  .admin-pagination__info {
    text-align: center;
  }
}

.admin-table thead th {
  /* Column header — Petřin pokyn 2026-05-21 round 8 (still nečitelné po
     boost-u 0.95 → 1.02 rem). Root cause: Cormorant Garamond display
     font + all-small-caps rendering = optical small + serif noise even
     at boosted weight. Switching to Manrope (= var(--font-body), our
     UI label face) with plain uppercase + bold gives a much higher
     stroke density per character. Per typography system: display font
     for display text, body font for identifiers/labels — headers ARE
     identifiers. */
  text-align: left;
  padding: 0.95rem 1rem;
  background: var(--cream-mist);
  color: var(--text-dark);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-variant-caps: normal;
  border-bottom: 2px solid var(--border-on-light);
}

/* App-wide admin row baseline (Petřin pokyn 2026-05-21 — sjednotit
   vzhled napříč všemi admin tabulkami podle audit log). Cell padding
   + vertical-align + dark text + bottom border on every tbody td so
   tables that don't add `.admin-row` per-row (= audit + tours) get
   the same polish as Reports / Concerts / Online / POI. Border on td
   (not tr) so border-collapse model keeps spacer-row overrides simple. */
.admin-table tbody td {
  padding: 0.75rem 1rem;
  vertical-align: top;
  color: var(--text-dark);
  border-bottom: 1px solid rgba(238, 240, 250, 0.05);
}

/* Hover transition app-wide. Spacer rows opt out via .admin-table__spacer-row
   rule below (pointer-events: none blocks the hover state entirely). */
.admin-table tbody tr {
  transition: background 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .admin-table tbody tr:hover {
    background: var(--accent-turquoise-tint);
  }
}

/* .admin-row stays as the opt-in "this row is clickable" marker (used by
   Reports for click-to-detail). Other tables that don't open a detail
   pane skip the class — they still get padding + hover from the baseline
   above. */
.admin-row {
  cursor: pointer;
}

.admin-row--resolved td,
.admin-row--dismissed td {
  color: var(--text-muted);
}

/* Zebra striping app-wide across every admin table per Petřin pokyn
   2026-05-21 ("nad všemi tabulkami v administraci chybí zebra").
   Subtle navy tint on even rows so the eye groups long lists without
   competing with the row hover turquoise highlight or the
   .admin-row--resolved / --dismissed muted colour. */
.admin-table tbody tr:nth-child(even) {
  background: rgba(238, 240, 250, 0.028);
}
@media (hover: hover) and (pointer: fine) {
  .admin-table tbody tr:nth-child(even):hover {
    background: var(--accent-turquoise-tint);
  }
}

/* Invisible spacer rows — appended by loadAudit() when the current page
   has fewer than AUDIT_PAGE_SIZE data rows, so the table keeps a stable
   height across pages and the top + bottom pagers don't collapse together
   on short last pages. They participate in tbody layout (= reserve row
   height) but render no visible content, zebra stripe, border, or hover
   highlight. Petřin pokyn 2026-05-21. */
.admin-table tbody tr.admin-table__spacer-row,
.admin-table tbody tr.admin-table__spacer-row:nth-child(even) {
  background: transparent;
  pointer-events: none;
}
.admin-table tbody tr.admin-table__spacer-row td {
  border-bottom: 1px solid transparent;
  color: transparent;
}

/* Empty-state row INSIDE tbody (Petřin pokyn 2026-05-21 — Reports
   headers looked subordinate because the .admin-empty paragraph sibling
   above the table dominated). Single colspan cell keeps the table
   header as the primary visual anchor. */
.admin-table tbody tr.admin-table__empty-row,
.admin-table tbody tr.admin-table__empty-row:nth-child(even) {
  background: transparent;
  pointer-events: none;
}
.admin-table tbody tr.admin-table__empty-row td.admin-table__empty-cell {
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1rem;
  border-bottom: none;
}

.admin-status {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* STYLE-M10-001 Option A (M10 closure Stage 2 audit 2026-05-16):
   Status badges Frost-tokenized — warning/success/muted families match
   --status palette on cream-base. Was sub-AA on light surface (dark-theme
   hex on cream wash). */
.admin-status--pending {
  background: rgba(184, 134, 45, 0.14);
  color: var(--warning);
}

/* ADR-267 R7 — inline flag in the concert row's TOUR cell: this pending
   concert proposed a NEW tour (one approval decides both). Same warning
   tone as .admin-status--pending, text-only (not a badge). */
.admin-tour-pending-flag {
  color: var(--warning);
  font-size: 0.78rem;
  white-space: nowrap;
}

.admin-status--resolved {
  background: rgba(45, 138, 92, 0.14);
  color: var(--success);
}

.admin-status--dismissed {
  background: rgba(238, 240, 250, 0.08);
  color: var(--text-muted);
}

/* ----------------------------------------------------------------
   Admin Events table polish — title link-look + stacked WHEN +
   icon-only Delete (M11.X-EVENTS-BOOTSTRAP follow-up 2026-05-18).
   Concerts / Online / Tours tables share these classes.
   ---------------------------------------------------------------- */

/* Title — turquoise underlined link-look (clickable since 2026-05-19:
   opens the Edit modal as a quasi-preview; Petřin pokyn). */
.admin-row__title {
  color: var(--accent-turquoise);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  cursor: pointer;
}
.admin-row__title:hover {
  color: var(--accent-turquoise-bright);
}

/* Inline meta column (Tour name in Concerts, Subcategory in Online,
   description in Tours). Replaces inline-style muted spans. */
.admin-row__meta {
  color: var(--text-muted);
  font-size: 0.9em;
}

/* M12D — Reports card meta labels + status pill are MOBILE-ONLY surfaces
   (the phone card has no column headers / status filter to lean on). On
   desktop the table columns + status filter already convey both, so hide
   them here; mobile.css re-shows them inside the ≤480px card reflow.
   The status pill carries the canonical `.pin-type-chip` class for its size,
   so its hide must out-specify `.pin-type-chip { display:inline-flex }` — scope
   it to the type cell (0,2,0 > 0,1,0). */
.admin-cell-label {
  display: none;
}
.admin-reports__type .admin-status-pill {
  display: none;
}

/* WHEN column — stacked date + time. Date keeps body weight, time drops
   to muted + smaller as secondary metadata. tabular-nums keeps digits
   aligned across rows. */
.admin-row__when {
  white-space: nowrap;
}
.admin-when__date {
  display: block;
  color: var(--text-dark);
  font-size: 0.9rem;
  line-height: 1.3;
}
.admin-when__time {
  display: block;
  color: var(--text-muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  margin-top: 0.1rem;
}

/* Actions cell flex container — gap separates icon buttons. */
.admin-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  /* Icons stay on a single line — `width: 1%` on the actions column
     means the column would otherwise force flex children to wrap.
     Petřin pokyn 2026-05-25 — Actions RIGHT-aligned so a row with one
     gated-away icon keeps its remaining icon on the right edge (no
     left-shift when the test flask is hidden), aligned with multi-icon
     rows. The column's 1rem cell padding (below) preserves the gap from
     the table's right edge — icons never touch it. */
  flex-wrap: nowrap;
  justify-content: flex-end;
  white-space: nowrap;
}

/* Unified Actions column behavior across every admin table: ALWAYS the
   last column, narrow (= fits the icon row + breathing room), RIGHT-aligned.
   Petřin pokyn 2026-05-25 (supersedes 2026-05-21 centered) — actions vpravo,
   ale s zachovaným paddingem od pravého kraje (= cell padding 1rem, ne
   nalepené na okraj). Applied to ANY trailing column via :last-child so
   tables without an explicit class still match — consistent across all
   admin tabs. Per-class rules below kept for explicit audit / test-data. */
.admin-table thead th:last-child {
  text-align: right;
}
.admin-table tbody td:last-child {
  text-align: right;
  white-space: nowrap;
}
.admin-table__col-actions,
.admin-test-data__col-actions {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

/* Sortable header pattern (adminSortableTable.js, Petřin pokyn 2026-05-21):
   clickable th + ↑↓ indicator next to label. Neutral "↕" hint shown on
   un-sorted sortable columns; active column gets accent-turquoise indicator. */
.admin-table__col--sortable {
  cursor: pointer;
  user-select: none;
}
.admin-table__col--sortable:hover {
  color: var(--accent-turquoise);
}
.admin-table__col-sort-indicator {
  display: inline-block;
  margin-left: 0.3em;
  font-size: 0.85em;
  color: var(--text-muted);
  opacity: 0.5;
  transition: color 140ms ease, opacity 140ms ease;
}
.admin-table__col--sorted .admin-table__col-sort-indicator {
  color: var(--accent-turquoise);
  opacity: 1;
  font-weight: 700;
}

/* ----------------------------------------------------------------
   M11.X-ADMIN-EVENTS-REDESIGN Stage 2 (ADR-113 Rozhodnutí 2) —
   ghost icon action pattern. Replaces b7b94ba square-red Delete.
   Default = no bg / no border / muted color. Row hover lifts neutral
   icons to text-dark. Icon hover = bg tint + role color shift.
   ADR-078 amendment: Delete keeps --danger color in default state
   (= rozpoznatelnost signal preserved), only mass is reduced.
   ---------------------------------------------------------------- */

.admin-actions__icon {
  width: 32px;
  height: 32px;
  min-height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition:
    background 160ms ease,
    color 160ms ease,
    border-color 160ms ease,
    transform 160ms ease;
}

.admin-actions__icon > svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  pointer-events: none;
}

/* Row hover — neutral Edit icon intensifies; role-colored icons (Delete,
   Approve, Reject) stay full role color (overridden below per-variant). */
.admin-row:hover .admin-actions__icon {
  color: var(--text-dark);
}

/* M15 [BUG] (Petřin verdict 2026-06-06, real data wins) — test→real flip
   blocked by a real duplicate. aria-disabled (NOT `disabled`, d7af2c8
   gate pattern) so the tap still fires and the reason toasts on mobile;
   hover stays neutralised. Beats the per-variant + row-hover rules. */
.admin-actions__icon[aria-disabled="true"],
.admin-row:hover .admin-actions__icon[aria-disabled="true"],
.admin-actions__icon--test-on[aria-disabled="true"],
.admin-actions__icon--test-on[aria-disabled="true"]:hover {
  color: var(--text-muted);
  opacity: 0.45;
  cursor: not-allowed;
  background: transparent;
  border-color: transparent;
  transform: none;
}

/* Default-state role hints — danger / success colors visible without
   hover so admin can scan-identify destructive vs constructive action. */
.admin-actions__icon--delete {
  color: var(--danger-text);
}
/* M11B Moderators UX pivot 2026-05-22 evening — Promote-to-admin icon
   uses violet/plum (per memory `feedback_cta_color_uniform_per_role`:
   Promote/Add/Create = violet across the app). Crown glyph + violet
   hue together signal "elevate to admin/founder override". */
.admin-actions__icon--promote {
  color: var(--accent-violet-soft);
}
.admin-row:hover .admin-actions__icon--promote,
.admin-moderators__row:hover .admin-actions__icon--promote {
  color: var(--accent-violet-bright);
}
.admin-actions__icon--approve {
  color: rgba(45, 138, 92, 0.78);
}
.admin-actions__icon--reject {
  color: rgba(var(--danger-rgb), 0.78);
}
.admin-row:hover .admin-actions__icon--delete {
  color: var(--danger-text);
}
.admin-row:hover .admin-actions__icon--approve {
  color: var(--success);
}
.admin-row:hover .admin-actions__icon--reject {
  color: var(--danger-text);
}

/* Icon hover / focus — bg tint + sharpen color */
.admin-actions__icon--edit:hover,
.admin-actions__icon--edit:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--accent-turquoise);
  outline: none;
}
.admin-actions__icon--delete:hover,
.admin-actions__icon--delete:focus-visible {
  background: rgba(var(--danger-rgb), 0.12);
  color: var(--danger-text);
  outline: none;
}
.admin-actions__icon--approve:hover,
.admin-actions__icon--approve:focus-visible {
  background: rgba(45, 138, 92, 0.14);
  color: var(--success);
  outline: none;
}
.admin-actions__icon--reject:hover,
.admin-actions__icon--reject:focus-visible {
  background: rgba(var(--danger-rgb), 0.12);
  color: var(--danger-text);
  outline: none;
}

/* M11.X-TEST-ROWFLIP (ADR-138) — per-row "Mark as test data" ghost icon.
   Two states share the same icon button class (Petřin pokyn 2026-05-24:
   gold test-data hue, not danger red):
     --test-off = record is real data → muted grey ghost (Edit-icon family)
     --test-on  = record is test data → gold active fill (--warning)
   Click flips state; backend audit log captures every flip. */
.admin-actions__icon--test-off {
  color: var(--text-muted);
}
.admin-actions__icon--test-on {
  color: var(--warning);
  background: rgba(var(--warning-rgb), 0.08);
}
.admin-row:hover .admin-actions__icon--test-off {
  color: var(--text-dark);
}
.admin-row:hover .admin-actions__icon--test-on {
  color: var(--warning);
}
.admin-actions__icon--test-off:hover,
.admin-actions__icon--test-off:focus-visible {
  background: rgba(var(--warning-rgb), 0.10);
  color: var(--warning);
  outline: none;
}
.admin-actions__icon--test-on:hover,
.admin-actions__icon--test-on:focus-visible {
  background: rgba(var(--warning-rgb), 0.18);
  color: var(--warning);
  outline: none;
}

@media (hover: hover) and (pointer: fine) {
  .admin-actions__icon:hover {
    transform: translateY(-1px);
  }
}

/* M11.X-TEST-ROWFLIP — admin-only inline action cluster for pin rows
   in the Pins panel. Mirrors the trailing-cluster pattern used by the
   admin events queue but lives inside the user-facing panel item, so
   it sits absolute-positioned right of the meta block. Hidden by
   default; the renderPinItem JS only mounts it when isAdmin(). Hover
   lift on the row reveals the icons (parity with .admin-row pattern).
   ---------------------------------------------------------------- */
.feed-list__admin-actions {
  position: absolute;
  top: 0.55rem;
  right: 0.6rem;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
  z-index: 2;
}
.feed-list__item:hover .feed-list__admin-actions,
.feed-list__item:focus-within .feed-list__admin-actions {
  opacity: 1;
  pointer-events: auto;
}
@media (hover: none) {
  /* Touch devices — no hover state; reveal always so admin can tap. */
  .feed-list__admin-actions {
    opacity: 1;
    pointer-events: auto;
  }
}

/* ----------------------------------------------------------------
   M11.X-MINE-HIGHLIGHT (ADR-152) — own pin row in the Pins feed.
   • feed-list__item--mine — PERMANENT (= toggle-INdependent) jemný
     copper bg tint nad violet violet-on-cream surface; signál „toto
     je tvůj pin" jediným pohledem napříč chronologickým seznamem.
   • feed-list__inline-actions — pozičně mirror admin actions hover
     row, ale jeden řádek níž (bottom-right), aby admin-akce + mine-
     Delete nekolidovaly u admina který má vlastní pin. Hover-only
     reveal pattern stejný jako admin actions.
   • feed-list__inline-action--danger — Delete ghost icon, default
     muted text color → --danger červená na hover (per
     feedback_destructive_ctas_uniform_red).
   ---------------------------------------------------------------- */
/* ADR-193 (M12.X-IDENTITY-SORT-SPLIT) — class is painted by renderPinItem
   for EVERY own moment (permanent magenta identity, no longer gated by the
   pill). The pill is "My moments first" = sort only.
   "highlight musí fungovat NAD zebra systémem, ne místo něj" still applies:
   - `background-image` (linear-gradient = uniform rust-red wash) is a
     SEPARATE layer from `background-color` — the zebra `:nth-child(even)`
     turquoise tint set on `.feed-list__item` still paints UNDER our
     image overlay, so alternating-row affordance survives.
   - `box-shadow: inset` left stripe (4px rust-red accent) carries the
     primary "this is yours" signal so the row reads as own-pin even
     when the underlying zebra tint dominates the bg-color slot. */
/* M12 Noir PR4 (Petřin pokyn 2026-05-24: "magenta proužek u mých je moc
   subtilní") — stronger own-moment card: a bold 5px magenta left stripe,
   a magenta-tinted card fill + border so "this is yours" reads at a
   glance against the neutral cards around it. The stripe is an inset
   box-shadow so it follows the card's rounded corners. */
.feed-list__item--mine {
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24 "podbarvení je moc výrazné") —
     the magenta SIGNAL is the left stripe; the card wash stays very faint
     (mockup 20). Was a strong 0.14 wash that flooded the card.
     ADR-193 smoke iter 2026-05-28 — Petřin pokyn "proužek moc široký na
     levé hraně" (permanent identita ho zviditelnila pernamentně, ne jen
     při toggle ON). Strip narrowed 5px → 3px (decentnější, ale stále
     primary "this is yours" signal nad zebra bg). */
  background: rgba(var(--accent-mine-rgb), 0.05);
  border-color: rgba(var(--accent-mine-rgb), 0.30);
  box-shadow: inset 3px 0 0 var(--accent-mine);
  position: relative;
}
@media (hover: hover) and (pointer: fine) {
  .feed-list__item--mine:hover {
    background: rgba(var(--accent-mine-rgb), 0.10);
    border-color: rgba(var(--accent-mine-rgb), 0.45);
    /* Keep the magenta stripe on hover (base hover box-shadow would drop it).
       Width mirrors base 3px (ADR-193 smoke iter 2026-05-28). */
    box-shadow: inset 3px 0 0 var(--accent-mine), var(--shadow-overlay-bar);
  }
}

.feed-list__inline-actions {
  position: absolute;
  bottom: 0.45rem;
  right: 0.6rem;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
  z-index: 2;
}
.feed-list__item:hover .feed-list__inline-actions,
.feed-list__item:focus-within .feed-list__inline-actions {
  opacity: 1;
  pointer-events: auto;
}
@media (hover: none) {
  .feed-list__inline-actions {
    opacity: 1;
    pointer-events: auto;
  }
}

.feed-list__inline-action {
  background: transparent;
  border: 0;
  border-radius: 6px;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted, #6c6963);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.feed-list__inline-action:hover,
.feed-list__inline-action:focus-visible {
  background: rgba(238, 240, 250, 0.06);
  outline: none;
}
.feed-list__inline-action--danger:hover,
.feed-list__inline-action--danger:focus-visible {
  color: var(--danger-text);
  background: rgba(184, 53, 53, 0.10);
}
.feed-list__inline-action:disabled {
  opacity: 0.55;
  cursor: progress;
}

/* ----------------------------------------------------------------
   M11.X-MODERATION-PIVOT (ADR-112) — admin queue 3-section pattern
   inside Concerts + Online tabs. Section header rows live inside the
   <tbody>, styled as a soft divider. Pending is always-on with an
   orange tint (= attention magnet); rejected is collapsible.
   Pending = #e8821f (= reuse existing upcoming-status orange);
   rejected = var(--danger). Approved = no badge / no tint.
   ---------------------------------------------------------------- */

.admin-mod-section th {
  text-align: left;
  padding: 0.65rem 1rem;
  /* Manrope (body), NOT Cormorant display — matches the fan "Yours" section
     header (.events-section__title) so admin + fan read as one type system.
     Cormorant's old-style figures rendered the "(32)" count tiny + unreadable
     even at 13px (Petřin pokyn 2026-05-31 — "stejně velká jako v Yours"). */
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-top: 1px solid var(--border-on-light);
  border-bottom: 1px solid var(--border-on-light);
  color: var(--text-dark);
}

.admin-mod-section--pending th {
  background: rgba(232, 130, 31, 0.08);
  color: #b85f15;
  border-top-color: rgba(232, 130, 31, 0.35);
  border-bottom-color: rgba(232, 130, 31, 0.35);
}

.admin-mod-section--approved th {
  background: rgba(238, 240, 250, 0.04);
  color: var(--accent-turquoise);
}

.admin-mod-section--rejected th {
  background: rgba(var(--danger-rgb, 178, 38, 38), 0.06);
  color: var(--danger-text);
}

.admin-mod-section--collapsible th {
  cursor: pointer;
  user-select: none;
}

.admin-mod-section__icon {
  margin-right: 0.4em;
  font-size: 1em;
}

.admin-mod-section__count {
  margin-left: 0.5em;
  font-weight: 600;
  opacity: 0.75;
}

.admin-mod-section__chevron {
  /* Bumped 0.8em → 1.1em + accent-turquoise color so the collapse
     affordance reads at admin-pill level (Petřin pokyn 2026-05-21 —
     trojúhelníčky byly příliš malé a špatně viditelné). */
  float: right;
  font-size: 1.1em;
  color: var(--accent-turquoise);
  margin-top: 0.1em;
  transition: transform 200ms ease;
}

.admin-mod-section--collapsible[data-collapsed="false"] .admin-mod-section__chevron {
  transform: rotate(180deg);
}

/* ADR-129 — rejected section subtitle: small muted text below the label,
   explains why rows lack Delete/Edit (author owns dismissal + auto-expire).
   COLOR is its own token — inheriting the bar's danger currentColor made
   it red-on-red and unreadable (Petřin nález 2026-06-05, desktop i mobil);
   mist tone per m14-per-tab §7, size/italic unchanged. */
.admin-mod-section__hint {
  display: block;
  margin-top: 0.2em;
  font-size: 0.78em;
  font-weight: 400;
  /* Same token as .admin-pane__subtitle — Petřina volba 2026-06-05
     („tuhle barvu, na mobil i desktop"): čitelné, ne křiklavě bílé. */
  color: var(--text-muted);
  font-style: italic;
}

/* Inline title-row badge — pending row + rejected row only. Approved
   row renders nothing (= default state). */
.admin-mod-badge {
  display: inline-block;
  margin-left: 0.4em;
  padding: 0.1em 0.55em;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  vertical-align: middle;
  white-space: nowrap;
}

.admin-mod-badge--pending {
  background: rgba(232, 130, 31, 0.14);
  color: #b85f15;
  border: 1px solid rgba(232, 130, 31, 0.32);
}

.admin-mod-badge--rejected {
  background: rgba(var(--danger-rgb, 178, 38, 38), 0.10);
  color: var(--danger-text);
  border: 1px solid rgba(var(--danger-rgb, 178, 38, 38), 0.32);
  /* Tooltip cursor cue — reason text lives in title attr (ADR-112
     Petřin add: rejected reason visible k autorovi přes tooltip). */
  cursor: help;
}

/* STYLE-M10-001 Option A (M10 closure Stage 2 audit 2026-05-16):
   admin-detail panel Frost-migrated per Petřinou volbou "migrate to Frost"
   (= consistent s admin-body + admin-list). Was dark island #141722/#20242f
   breaking all-or-nothing rollout rule. Atmospheric mesh + cream-surface
   matches the .admin-list pattern for visual continuity. */
.admin-detail {
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-plum) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  box-shadow: var(--shadow-card);
}

.admin-detail__title {
  margin: 0;
  font-size: 1.05rem;
  color: var(--text-dark);
  font-weight: 700;
}

.admin-detail__fields {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1rem;
  margin: 0;
  font-size: 0.9rem;
}

.admin-detail__fields dt {
  color: var(--text-muted);
  font-weight: 500;
}

.admin-detail__fields dd {
  margin: 0;
  color: var(--text-dark);
  word-break: break-word;
}

/* M11B Stage M follow-up 2026-05-22 evening — Reports table cell that
   truncates a long pin comment with title-attr tooltip showing the
   full value. The unabridged content lives in the detail dialog. */
.admin-cell-content {
  display: inline-block;
  max-width: 28ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.admin-cell-content--empty {
  color: var(--text-muted);
  font-style: italic;
}

/* Inside the detail dialog the full pin content is shown with
   preserved newlines so multi-line user comments stay readable. */
.admin-detail__content {
  white-space: pre-wrap;
  word-break: break-word;
}

/* Report detail dialog — wider than the default 440 px form modal so
   the mini-map + multi-line pin content have room to breathe. */
.frost-modal--detail {
  width: 560px;
}

.frost-modal--detail .frost-modal__body {
  padding: 1.5rem 1.4rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;   /* M12 PR6 Dávka E — canonical dialog section gap (ADR-179) */
  max-height: inherit;
  overflow-y: auto;
}

.frost-modal--detail .admin-detail__map {
  height: 200px;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--border-on-light);
}

.form-actions--end {
  display: flex;
  justify-content: flex-end;
}

.admin-detail__map {
  width: 100%;
  height: 220px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--cream-sunken);
}

.admin-detail__actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.admin-detail__actions button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Admin tabs (Reports / Audit log / Events / Sig places) — M10 closure Session #7
   admin polish (2026-05-16). FIX: previous Stage 2 Option C nastavila Cormorant
   700 ALE Petřin pokyn 2026-05-14 (code comment v .events-panel__tab line 2928)
   explicitně říká *„v záložkách stejné písmo + tučnost + velikost + barva jako
   v popupu u času a názvu místa"* = Manrope 800 caps. Audit Option C byl wrong
   analogy — confused section TITLES (Cormorant) vs switching TABS (Manrope).
   Full parity s .events-panel__tab pattern: Manrope 800 0.7rem caps 0.10em
   tracking + 3-stop gradient bar indicator instead of border-bottom-color. */
.admin-tabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--border-on-light);
}

.admin-tab {
  appearance: none;
  font-family: var(--font-body);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0.95rem 1.1rem;
  font-size: 0.7rem;
  font-weight: 800;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  letter-spacing: 0.10em;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: background 160ms ease, color 160ms ease;
  position: relative;
}

/* M11.X-EVENT-ACTIONS (ADR-127 R4) — request queues sitting above the
   moderation table in admin Concerts + Online tabs. Token-only — drift fixed
   M11.X-EVENT-ACTIONS DESIGN polish 2026-05-19 (--ink-2/--ink-3/--page-bg
   neexistovaly, fallbacks aktivní = hardcoded hex; nyní semantic tokens). */
.admin-request-queue {
  margin: 0.5rem 0 1.25rem;
  padding: 0.8rem 1rem 1rem;
  border-radius: var(--radius-sm);
}
.admin-request-queue--suggestion {
  background: rgba(var(--accent-copper-rgb), 0.06);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.18);
}
.admin-request-queue--deletion {
  background: rgba(var(--danger-rgb), 0.06);
  border: 1px solid rgba(var(--danger-rgb), 0.20);
}
.admin-request-queue__header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.admin-request-queue__icon {
  font-size: 1.1rem;
  /* No drop-shadow / animation — purely informational glyph (per
     `feedback_clarity_over_decoration`). */
}
.admin-request-queue__title {
  margin: 0;
  /* Section-header treatment that matches `.admin-table thead th` typography
     (Cormorant small-caps) so the queue header reads as part of the admin
     section family, not an ad-hoc card. */
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.05em;
  color: var(--text-dark);
}
.admin-request-queue__count {
  color: var(--text-muted);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.85rem;
  /* No caps shift here — count is a plain numeric badge against the
     Cormorant small-caps title. */
  font-variant-caps: normal;
}
.admin-request-queue__table {
  width: 100%;
  margin: 0;
}
.admin-request-queue__text {
  /* Italic for the suggestion / reason body — Manrope has italic axis loaded
     via Google Fonts (`/variable/Manrope`); falls back to system italic if not.
     Color uses --text-dark for AAA readability (the queue panel is small +
     tinted, muted would lose contrast on the rgba 6% wash). */
  font-style: italic;
  color: var(--text-dark);
  max-width: 30rem;
  line-height: 1.45;
}

/* M11.X-EVENT-ACTIONS (ADR-127 R6) — red dot indicator. Pseudo-element
   ::before stays out of conflict with the existing ::after active
   underline. Decorated by adminTabIndicators.js via `data-pending="N"`.
   Token drift fixed M11.X-EVENT-ACTIONS DESIGN polish 2026-05-19
   (--page-bg neexistoval, fallback #fdfbf7 vs real --cream-base #f4f1ea
   = warm halo na cream-base bg admin tabs).
   Per Petřin "NE plný badge s číslem — jen dot" (scope M11.X-EVENT-ACTIONS § F). */
.admin-tab[data-pending]::before {
  content: "";
  position: absolute;
  top: 0.7rem;
  right: 0.55rem;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  /* M12 Noir (Petřin pokyn 2026-05-24 "platí pro všechny tečky", admin
     dot missed in PR4 sweep, fixed 2026-05-25) — luminous soft-rose glow,
     NO dark ring. The old `0 0 0 2px var(--cream-base)` ring read as a
     black border around the dot on the Noir dark bar. Mirrors the Yours
     tab dot rule below. */
  background: var(--danger-bright);
  box-shadow: 0 0 7px 2px rgba(var(--danger-rgb), 0.9);
  pointer-events: none;
}

/* M11.X-YOURS-TAB (ADR-128 R5) — red dot indicator (Petřin amendment
   2026-05-19: same red as admin tabs, ne copper). Three surfaces share
   the rule: hero strip pill (mobile + desktop top), panel tab header
   (desktop expanded sidebar), and rail label (desktop collapsed sidebar).
   Petřin amendment 2026-05-19: "odsazení tečky u sbalených záložek
   bylo v pořádku, měl jsi to odsadit podle toho i u rozbalených" —
   the generous gap pattern (originally calibrated on the collapsed
   rail) now applies to all three surfaces. */
.hero-tab[data-pending],
.events-panel__tab[data-pending],
.events-panel__rail-label[data-pending] {
  position: relative;
}
/* Expand the pill horizontally when the dot is present so the text
   gets pushed left and the dot sits in its own zone with a modest
   gap (Petřin amendment 2026-05-19: "lehce ten gap sniž"). */
.hero-tab[data-pending] {
  padding-right: 18px;
}
.events-panel__tab[data-pending] {
  padding-right: 1.55rem;
}
.hero-tab[data-pending]::before,
.events-panel__tab[data-pending]::before {
  content: "";
  position: absolute;
  top: 50%;
  right: 7px;
  transform: translateY(-50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24: "chybí glow, místo toho černý
     border — platí pro všechny tečky") — luminous soft-rose glow, NO dark
     ring. The dark `0 0 0 2px var(--cream-base)` ring was the wrong pattern
     that read as a black border around every dot (mockup 23 dot glows). */
  background: var(--danger-bright);
  box-shadow: 0 0 7px 2px rgba(var(--danger-rgb), 0.9);
  pointer-events: none;
}
/* On the active hero pill the background is the turquoise solid — switch
   the dot halo to match so it reads cleanly against the active state. */
.hero-tab.is-active[data-pending]::before {
  box-shadow: 0 0 7px 2px rgba(var(--danger-rgb), 0.95);
}
/* Rail label is rotated (writing-mode: vertical-rl + rotate 180°) in the
   collapsed sidebar — modest gap pattern (Petřin amendment 2026-05-19
   "lehce ten gap sniž, ale ne na předchozí hodnotu"): extra padding
   pushes the centered label text away from the dot zone. */
.events-panel__rail-label[data-pending] {
  padding-top: 1.55rem;
}
.events-panel__rail-label[data-pending]::before {
  content: "";
  position: absolute;
  top: 8px;
  right: 50%;
  transform: translateX(50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger-bright);
  box-shadow: 0 0 7px 2px rgba(var(--danger-rgb), 0.9);
  pointer-events: none;
}

/* M13.X-SONGS-TAB Fáze 3a (ADR-237, Petřin výběr 2026-06-03 „4/12 a
   podobně svítící" + iterace „nechtěla jsem plnou pilulku ale svítící
   zlatý text" + „mělo by to odpovídat ratingu") — glowing gold TEXT
   counter on the Songs tab showing the star budget ("41/50"). Same three
   surfaces as the Yours dot family above. Corner-anchored absolute so it
   never widens the tab row (the 480px desktop header fits five tabs with
   no slack for inline text). Signed-in only, removed once the budget is
   fully given — songsPane.js. */
.hero-tab:has(.fm-tab-count),
.events-panel__tab:has(.fm-tab-count),
.events-panel__rail-label:has(.fm-tab-count) {
  position: relative;
}
.fm-tab-count {
  position: absolute;
  font: 800 var(--fs-3xs) var(--font-body);
  letter-spacing: 0.04em;
  /* NO filled pill (Petra: „hodně škaredé") — bare gold text, glow via
     text-shadow. */
  color: var(--accent-copper-bright);
  text-shadow: 0 0 7px rgba(var(--accent-copper-rgb), 0.8);
  pointer-events: none;
  white-space: nowrap;
}
.hero-tab .fm-tab-count { top: 0; right: 2px; }
.events-panel__tab .fm-tab-count { top: 3px; right: 2px; }
/* Petřin pokyn 2026-06-03 („je tam toho teď nějak moc… svítit jen ta
   čísla") — the counter is an INVITATION, so it lives only on the muted
   inactive tab where the gold numbers are the single glowing thing.
   The active Songs tab already glows gold + carries the active dash,
   and the pane's progress strip shows the full numbers inside. */
.hero-tab.is-active .fm-tab-count,
.events-panel__tab.is-active .fm-tab-count {
  display: none;
}
/* Collapsed rail label is vertical-rl + rotate(180deg) — counter-rotate
   the bubble back to horizontal and pin it to the same end as the
   canonical rail dot (top: 8px in layout coords), centred across the
   48px rail. Push the label text away like [data-pending] does. */
.events-panel__rail-label:has(.fm-tab-count) {
  padding-top: 1.9rem;
}
.events-panel__rail-label .fm-tab-count {
  top: 6px;
  left: 0;
  right: 0;
  margin: 0 auto;
  width: max-content;
  writing-mode: horizontal-tb;
  transform: rotate(180deg);
}

/* Echoes tab counter (2026-06-07, Petřin pokyn „malé, decentní, spíš
   šedé / tlumené" + iterace „o něco málo světlejší token a vůbec to
   neschovávej") — same canonical corner counter, MUTED variant: the
   number is a quiet community stat (how many Echoes exist), not the gold
   rating-budget glow. Secondary text token, no text-shadow; position and
   rail rotation inherited from the rules above. Rendered by
   echoesTabCount.js. */
.fm-tab-count--muted {
  color: var(--text-muted);
  /* Soft halo in the Echoes-world tone (violet, like gold belongs to the
     rating world) — gentler than the Songs counter's 0.8 gold glow, just
     enough to read as intentional, not "forgotten text" (Petra
     2026-06-07). */
  text-shadow: 0 0 7px rgba(var(--accent-violet-rgb), 0.45);
  /* en-GB compact notation emits lowercase "1.3k" — uppercase it here
     visually (like counts elsewhere keep their existing lowercase). */
  text-transform: uppercase;
}
/* Unlike the gold Songs counter, the Echoes count stays visible on the
   ACTIVE tab too (Petřin pokyn 2026-06-07) — re-show what the canonical
   active-tab hide rule above removes. */
.hero-tab.is-active .fm-tab-count--muted,
.events-panel__tab.is-active .fm-tab-count--muted {
  display: inline;
}

/* M12.X-YOURS-INDICATORS — second Yours-tab dot: a mint "live" dot that
   lights when a SAVED event is live now or happening today. Mirrors the
   attention dot but uses the --live token, and sits in the SAME spot.
   Only one dot ever shows: live/today wins, the attention dot is the
   fallback shown only when there's nothing live (Petřin pokyn 2026-05-25).
   That priority is enforced just below by hiding the attention ::before
   whenever the live dot is present. */
/* 2026-06-06 (Petřin nález) — the live dot moved from ::after to ::before,
   the SAME pseudo-element the attention dot lives on. On panel tabs ::after
   belongs to the active gradient underline; the old ::after dot fought it
   per-property (the underline's `left` survived the cascade → the dot
   jumped to the LEFT of an active tab and ate the underline). Sharing
   ::before with the attention dot also guarantees identical size + spot,
   and the cascade enforces the live-wins priority for free: this rule sits
   AFTER the [data-pending] one, so when both attributes are present the
   mint colour simply overrides the red — exactly one dot, live wins. */
.hero-tab[data-live]::before,
.events-panel__tab[data-live]::before {
  content: "";
  position: absolute;
  top: 50%;
  right: 7px;
  transform: translateY(-50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--live);
  box-shadow: 0 0 7px 2px rgba(var(--live-rgb), 0.9);
  pointer-events: none;
}
.hero-tab[data-live],
.events-panel__tab[data-live] {
  /* position:relative was only on [data-pending] — a live-only dot escaped
     to the nearest positioned ancestor (found 2026-06-06 while wiring the
     Concerts/Online dots; latent for the Yours live dot too). */
  position: relative;
}
/* Dot gap = exact parity with the attention dot per surface (hero pill
   18px, panel tab 1.55rem — the old shared 18px sat too close to the
   panel-tab label, Petřin nález 2026-06-06). */
.hero-tab[data-live] {
  padding-right: 18px;
}
.events-panel__tab[data-live] {
  padding-right: 1.55rem;
}
/* Rail label (vertical, collapsed sidebar) — same single-dot spot as its
   attention dot (and same live-wins colour override). */
.events-panel__rail-label[data-live]::before {
  content: "";
  position: absolute;
  top: 8px;
  right: 50%;
  transform: translateX(50%);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--live);
  box-shadow: 0 0 7px 2px rgba(var(--live-rgb), 0.9);
  pointer-events: none;
}
.events-panel__rail-label[data-live] {
  position: relative;
  padding-top: 1.55rem;
}
/* Active hero pill — the [data-pending] halo rule above has higher
   specificity than the plain live rule; mirror it so a live dot on the
   active pill keeps its MINT halo (not the danger one). */
.hero-tab.is-active[data-live]::before {
  box-shadow: 0 0 7px 2px rgba(var(--live-rgb), 0.95);
}
/* ADR-295 — soft entrance for the mint live dots: a 150 ms fade when the
   [data-live] attribute lands. ::before ONLY (::after is the active tab's
   underline); opacity only (the dots position via their own transform). */
@keyframes tab-dot-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.hero-tab[data-live]::before,
.events-panel__tab[data-live]::before,
.events-panel__rail-label[data-live]::before {
  animation: tab-dot-in 150ms ease;
}
@media (prefers-reduced-motion: reduce) {
  .hero-tab[data-live]::before,
  .events-panel__tab[data-live]::before,
  .events-panel__rail-label[data-live]::before {
    animation: none;
  }
}

/* Yours/Your rejected submissions — suppress the noisy badges on rows
   inside the rejected sub-section. The section header already conveys
   the rejected state, so per-row "🚫 zamítnuto" pill + timeline status
   dot just create misalignment. Per Petřin amendment 2026-05-19
   "rozbíjí layout — flagy by měly být v jednom sloupci". */
.feed-list__item--yours-rejected .feed-list__mod-badge--rejected,
.feed-list__item--yours-rejected .feed-list__status-dot {
  display: none !important;
}

/* Yours/Your pending submissions — same logic. Sub-section header
   already says "Pending"; per-row mod badge is duplicate signal
   (Petřin Yours panel sjednocení 2026-05-20). Status dot is
   irrelevant until the event is approved (mirror rejected). */
.feed-list__item--yours-pending .feed-list__mod-badge--pending,
.feed-list__item--yours-pending .feed-list__status-dot {
  display: none !important;
}

/* M11.X-DELETED-PINS (ADR-156) — mirror rejected/pending suppression.
   Deleted pin rows live in their own sub-section header "Deleted Pins"
   so any leftover mod badge / status dot from the shared feed-list
   base would duplicate the signal. Pin entity doesn't render mod-badge
   today anyway, but the rule stays defensive for forward-compat. */
.feed-list__item--yours-deleted .feed-list__mod-badge,
.feed-list__item--yours-deleted .feed-list__status-dot {
  display: none !important;
}

/* STANDALONE tour chip noise — in Yours panel the author knows their
   own submission, so absence-of-tour reads as obvious. Hide across
   all Yours buckets (Saved/Pending/Rejected/Deleted Pins). Public
   Concerts/Online tabs keep the chip so fan-readers can disambiguate
   which shows are tour stops (Petřin Yours panel sjednocení 2026-05-20,
   ADR-134 STANDALONE explicit-state contract preserved in public). */
.feed-list__item--yours-pending .tour-chip--standalone,
.feed-list__item--yours-rejected .tour-chip--standalone,
.feed-list__item--yours-deleted .tour-chip--standalone,
[data-section-list="yours-saved"] .tour-chip--standalone {
  display: none !important;
}

/* Yours rejected row also hides the inline Edit/Suggest/Delete action
   row — the 🗑 in the trailing cluster (below) handles the author's
   hard_self delete path; the inline action-row would just duplicate it
   with extra labels. Same logic for Deleted Pin rows — pin entity
   has no inline action-row by default, but the rule keeps any future
   leak invisible.
   Petřin pokyn 2026-05-19 (audit delete icons): × změněn na 🗑 protože
   semantika je delete (hard_self), ne dismiss. */
.feed-list__item--yours-rejected .feed-list__action-row,
.feed-list__item--yours-deleted .feed-list__action-row {
  display: none !important;
}

/* M11.X-EXPANDABLE-LISTS (2026-05-21) — Yours rejected reason renders
   inline pod meta as a full-row block, NOT a tight column cell. Petřin
   pokyn: typografie jako v popupech (= popup-like size + line-height,
   --font-body Manrope inherited), label „Reason" tučně, full-width
   (= „přes více sloupců"). Lives inside .feed-list__body (yours column
   stack), so width = full body. */
/* Handoff m12-list-items.md §8 .fm-your-item__reason — REJECTED reason is a
   callout BOX (danger gradient + left accent), not inline text. Rejected tone
   = --danger (rulebook); the mockup's pink reads as the danger rose. Was
   inline + black text (invisible on the dark Noir surface). */
.feed-list__rejection-reason {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0.4rem 0 0;
  padding: 0.45rem 0.65rem;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    rgba(var(--danger-rgb), 0.20),
    rgba(var(--danger-rgb), 0.07));
  border: 1px solid rgba(var(--danger-rgb), 0.40);
  border-left: 3px solid var(--danger);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--text-dark);
  overflow-wrap: anywhere;
  word-break: break-word;
}

.feed-list__rejection-reason-label {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--danger-text);
}

/* Reason text = Cormorant italic (handoff §8 reason-text). */
.feed-list__rejection-reason .text-expandable {
  flex: 1 1 auto;
  min-width: 0;
  /* Moderation reason = metadata, not user content → standard UI font
     (Manrope), not the Cormorant italic it had (Petřin pokyn 2026-05-27).
     Rule is scoped to the reason box, so no other surface is affected. */
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  color: var(--text-dark);
}
.feed-list__rejection-reason .text-expandable p:first-child {
  margin-top: 0;
}

/* Trailing actions cluster on a Yours rejected row — [? reason] [🗑 delete]
   side by side, anchored to the row's right edge. */
.events-row__trailing {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-left: auto;
  align-self: center;
  flex: 0 0 auto;
}

/* When the ? reason icon sits in the trailing cluster, drop the left
   margin that was offsetting it from the badge in the original title
   row context. Gap on the parent handles spacing. */
.events-row__trailing .events-row__trailing-info {
  margin-left: 0;
}

/* Yours rejected row 🗑 delete button — flex child at the row's trailing
   edge, vertically centered so it aligns with the title regardless of
   the meta row height. Ghost-style; turns danger-red on hover. */
.events-row__dismiss {
  align-self: center;
  margin-left: auto;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 999px;
  transition: background 160ms ease, color 160ms ease;
}

/* When × sits inside the trailing cluster, drop margin-left:auto — the
   cluster itself is right-anchored, and the gap handles spacing
   relative to the ? icon to its left. */
.events-row__trailing .events-row__dismiss {
  margin-left: 0;
}
.events-row__dismiss:hover,
.events-row__dismiss:focus-visible {
  background: rgba(var(--danger-rgb, 191, 75, 75), 0.10);
  color: var(--danger-text);
  outline: none;
}
.events-row__dismiss:focus-visible {
  outline: 2px solid var(--danger, #bf4b4b);
  outline-offset: -2px;
}

@media (hover: hover) and (pointer: fine) {
  .admin-tab:hover {
    background: var(--accent-turquoise-tint);
  }
}
.admin-tab:focus-visible {
  outline: 2px solid var(--accent-violet-bright);
  outline-offset: -2px;
  border-radius: 4px;
}

.admin-tab--active {
  color: var(--text-dark);
  font-weight: 800;
  border-bottom-color: transparent;
  position: relative;
}
.admin-tab--active::after {
  content: "";
  position: absolute;
  left: 0.5rem; right: 0.5rem; bottom: -1px;
  height: 3px;
  /* M12 Noir 2026-05-28 — solid teal (was teal→copper rainbow gradient). */
  background: var(--accent-turquoise);
  border-radius: 2px;
}

/* ==========================================================================
   M11.X-EVENT-ACTIONS — Frost modal pattern for dynamically-created dialogs
   (deletionRequestModal.js + changeSuggestionModal.js). Before this block the
   dialogs were marked-up with `.frost-modal` classes but no CSS existed —
   they rendered as UA-default white boxes. This codifies the shared chrome
   (cream-surface + atmospheric mesh + halo shadow) and the dialog-specific
   tinted variants (danger for deletion, copper for suggestion).

   Pattern shared with `dialog#pin-form-dialog` + `dialog#report-form-dialog`
   per design-system.md sekce "Light modal" — cream surface + opposite-corner
   radial washes (teal + plum or teal + role-color) + 3-layer halo shadow.
   ========================================================================== */

.frost-modal {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 0;                    /* Inner padding lives on the <form> region */
  max-width: 95vw;
  width: 440px;
  max-height: 90dvh;
  /* App-wide sweep 2026-05-19 (per pin-form-dialog dual-scrollbar fix) —
     `> form` already declares overflow-y:auto for its own scroll
     region; suppress the UA default dialog overflow so only one
     scrollbar renders. Also lets the 20 px border-radius clip the
     form's rounded corners correctly. */
  overflow: hidden;
  /* Default mesh — neutral turquoise + violet, same as pin-form-dialog. */
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
  font-family: var(--font-body);
}

.frost-modal::backdrop {
  /* Night-sky atmospheric backdrop, same as pin-form / event-detail-modal. */
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* Form region inside the frost-modal — flex column with consistent spacing.
   Both deletion-request and change-suggestion dialogs use this layout. */
.frost-modal > form {
  padding: 1.5rem 1.4rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;   /* M12 PR6 Fáze 0 — kanonická dialog section gap (ADR-179) */
  max-height: inherit;
  overflow-y: auto;
}

/* M12 PR6 Fáze 0 — kanonický dialog titul (ADR-179): bílá Cormorant italic
   700, 1.55rem, sjednoceno se všemi dialogy. Žádný gradient. */
.frost-modal__title {
  font-family: var(--font-display);
  /* Quick confirm/reason/ban/delete prompts — small tier (M13.X-TITLE-TIERS).
     The two content viewers (report detail, audit record) override back to the
     big modal tier below. */
  font-size: var(--fs-dialog-title);
  font-weight: 700;
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  font-style: italic;
  color: var(--text-dark);
}

/* Exception — Backstage detail viewers are content windows, not prompts, so
   they keep the big modal title even though they reuse the frost-modal shell. */
.frost-modal--detail .frost-modal__title,
.admin-audit-detail .frost-modal__title {
  font-size: var(--fs-modal-title);
}

/* Safety reservation — if any frost-modal happens to have an injected
   .dialog-close button (= forgot to add `data-no-auto-close-button`),
   keep the 48×48px button from overlapping the gradient title. The
   pad-right is enough to clear the absolute-positioned close button. */
.frost-modal:has(.dialog-close) .frost-modal__title {
  padding-right: 2.8rem;
}

/* Aliases for existing field/legend markup so dialogs converted to
   .frost-modal don't need a full restructure (sigplace JS-injected
   fields, ban radios, test-data confirm body). These match the look
   of the canonical .frost-modal__label without forcing a rewrite. */
.frost-modal .field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  border: none;
  padding: 0;
  margin: 0;
}
.frost-modal .field__label {
  /* M12 PR6 cross-dialog sweep — was white-bold sentence-case (Frost-era);
     align to the canonical .field__label (muted, 500, wide tracking,
     uppercase, HARD RULE 9). .field__label is the text element itself
     (span/legend), siblings are the controls, so uppercase is safe here. */
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--label-color);
  padding: 0;
}
.frost-modal .field--radio {
  flex-direction: row;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-dark);
  cursor: pointer;
}
.frost-modal .field--radio input[type="radio"] {
  accent-color: var(--accent-turquoise);
  width: 1.05rem;
  height: 1.05rem;
}
.frost-modal input[type="text"],
.frost-modal input[type="number"],
.frost-modal input[type="email"],
.frost-modal input[type="url"],
.frost-modal input[type="search"],
.frost-modal select {
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  padding: 0.55rem 0.85rem;
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  width: 100%;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}
.frost-modal input:focus,
.frost-modal select:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}
/* dialog-hint variants kept for warnings inside frost-modal forms
   (M10B cascade warn etc). */
.frost-modal .dialog-hint--danger-light {
  background: rgba(var(--danger-rgb, 178, 38, 38), 0.08);
  border-left: 3px solid var(--danger);
  padding: 0.55rem 0.75rem;
  border-radius: 6px;
  color: var(--danger-text);
}
/* Bigger modal for the ban dialog (radios + textarea) needs more
   breathing room than the default 440px. (Legacy admin-sigplace-dialog
   selector removed with M11G Stage C — POI now reuses poi-create-dialog.) */
dialog#admin-ban-dialog {
  width: 520px;
}
dialog#admin-test-data-confirm-dialog {
  width: 480px;
}

/* Hint — same look as .dialog-hint but scoped to frost-modal so it inherits
   even when the global selector isn't on the element. */
.frost-modal__hint {
  font-size: 0.875rem;
  color: var(--text-muted);
  line-height: 1.45;
  margin: 0;
}

/* Field label — Manrope semibold 0.85rem, small-caps optional via inline.
   Inherits flex column from the parent form, so labels keep the same gap. */
.frost-modal__label {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  /* M12 PR6 cross-dialog sweep — was white-bold sentence-case (Frost-era);
     align to the canonical .field__label look (muted, 500, wide tracking,
     uppercase, HARD RULE 9). Applies to the label text span; the controls
     below reset transform/tracking so typed input isn't uppercased/spaced. */
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.frost-modal__label input,
.frost-modal__label textarea,
.frost-modal__label select {
  text-transform: none;
  letter-spacing: normal;
}

/* Textarea inside frost-modal — same surface treatment as report-form +
   auth-dialog inputs. iOS Safari auto-zoom fix per P-033 (16px = 1rem). */
.frost-modal textarea {
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  width: 100%;
  resize: vertical;
  min-height: 4.5rem;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}

.frost-modal textarea:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

/* Form error region inside frost-modal — re-styled .form-error living
   directly under fields. Tight margin + small font so it doesn't push
   the actions row out of view. */
.frost-modal .form-error {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--danger-text);
  line-height: 1.4;
}

/* Action row — Cancel + Submit. Margin-top auto would stick to the bottom
   only if the form is taller than its content; here we just want a small
   gap from the last field. */
.frost-modal .form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.7rem;
  margin-top: 0.4rem;
}

/* ==========================================================================
   M11.X-EVENT-ACTIONS — Deletion request dialog (`dialog#deletion-request-dialog`)
   Per ADR-125 R6: registered author asks moderator to soft-delete their
   approved event. 6 optional reason chips (pill radios) + optional text 0-500.
   Variant of frost-modal with danger-tinted mesh (event removal = serious
   action, so the chrome reflects it; matches report-form-dialog precedent).
   ========================================================================== */

/* M12 PR6 Dávka D — the deletion-request dialog no longer overrides the shell
   with a danger-tinted frame, and its bespoke red submit (hardcoded red
   literal + duplicated .btn-danger geometry) is gone. It now inherits the
   canonical teal .frost-modal shell; the destructive signal rides on the
   .btn-danger submit class (wired in deletionRequestModal.js). The reason
   chips stay teal (positive selection), the red lives only on the button. */

/* Reason chips — fieldset wraps the radiogroup. Reset UA default fieldset
   border + padding so it reads as a labeled section, not a boxed form group. */
.deletion-request__chips {
  border: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Legend (= reason label) — same look as .frost-modal__label. */
.deletion-request__chips .frost-modal__label {
  /* When .frost-modal__label is a <legend>, its display must be block-like
     and it shouldn't carry the flex column from the form-label rule. */
  display: block;
  padding: 0;
}

/* Chip grid — 2-3 columns on desktop, 2 cols on narrow mobile. */
.deletion-request__chip-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.4rem;
}

@media (max-width: 480px) {
  .deletion-request__chip-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Individual chip = <label> wrapping a hidden <input type="radio"> + <span>.
   Pill-style ghost default; selected = turquoise tinted bg + turquoise border. */
.deletion-request__chip {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-pill);
  background: var(--cream-surface);
  color: var(--text-dark);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  min-height: 36px;
  /* iOS Safari prevents text selection on rapid taps + WCAG-friendly. */
  user-select: none;
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease;
}

/* Hide the actual radio — chip itself acts as the affordance. */
.deletion-request__chip input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  overflow: hidden;
  white-space: nowrap;
}

.deletion-request__chip:hover {
  border-color: var(--accent-turquoise);
  color: var(--accent-turquoise);
  background: var(--accent-turquoise-tint);
}

/* Selected chip — turquoise filled per role (selection = positive choice,
   uniform with Save/Confirm; the danger styling lives on the Submit button,
   not on the picker). */
.deletion-request__chip:has(input[type="radio"]:checked) {
  border-color: var(--accent-turquoise);
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
  box-shadow: inset 0 0 0 1px var(--accent-turquoise);
}

/* Focus-visible halo for keyboard nav — falls on the chip wrapper because
   the radio is visually-hidden but still receives focus. */
.deletion-request__chip:has(input[type="radio"]:focus-visible) {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* ==========================================================================
   M11.X-EVENT-ACTIONS — Change suggestion dialog (`dialog#change-suggestion-dialog`)
   Per ADR-127: any authenticated user suggests a change to an approved event.
   Single mandatory free-text 1-1000 chars + live char counter.
   Uses default frost-modal neutral mesh (suggestion is non-destructive +
   editorial in tone, no danger framing needed). Submit = turquoise primary
   (Send = positive action per ADR-078).
   ========================================================================== */

/* M12 PR6 Dávka D — the bespoke change-suggestion submit (a duplicate of
   .btn-primary that re-implemented the teal gradient with hardcoded rgba
   literals) is gone. The button already carries .btn-primary, so it inherits
   the canonical teal CTA + the shared .btn-primary:disabled state below. */

/* Live char counter — small muted text under the textarea, right-aligned. */
.change-suggestion__counter {
  font-size: 0.75rem;
  color: var(--text-muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  margin-top: 0.2rem;
  /* No font-style italic — keep it utilitarian (per `feedback_clarity_over_decoration`). */
}

/* Cancel button (= .btn-secondary already styled globally) keeps default
   ghost look. No override needed inside frost-modal — already AA-contrast
   on cream-surface. */

.audit-target {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.8em;
  color: var(--text-muted);
}

/* Banned-account banner — STYLE-M10-001 Option A Frost-migrated.
   Was dark hot-pink stack (#3a1418/#ffc1c8/#ef476f). Now danger-tinted
   soft wash on cream context + danger-deep AAA-contrast text. Per
   `feedback_destructive_ctas_uniform_red` color discipline + the
   banned UX is a serious "you're locked" signal, deserves uniform red. */
.banned-banner {
  background: rgba(var(--danger-rgb), 0.10);
  color: var(--danger-text);
  border-bottom: 1px solid var(--danger);
  padding: 0.75rem 1.25rem;
  font-size: 0.9rem;
  text-align: center;
}

.banned-banner strong {
  color: var(--danger-text);
}

/* Reason / Ban dialogs reuse the existing dialog styles — no extra
   rules needed beyond their button danger class. */

/* Password show/hide toggle — eye icon overlaid on the right side of
   any <input type="password"> wrapped by passwordToggle.js. */
.password-field {
  position: relative;
  display: block;
}

.password-field input {
  /* Reserve space on the right so typed text doesn't run under the
     eye icon. Width matches the button below. */
  padding-right: 2.5rem;
}

.password-field__toggle {
  position: absolute;
  top: 50%;
  right: 0.25rem;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  /* M-UX-AUDIT-020 per P-036 (mobile): 44×44 tap target.
     Eye icon stays 16-20px centered inside the expanded hit area. */
  min-width: 44px;
  min-height: 44px;
  padding: 0.6rem 0.7rem;
  cursor: pointer;
  /* STYLE-M10-001 Option A (M10 closure Stage 2 audit 2026-05-16):
     Frost-migrated — was dark-theme #8a90a3 → #e8eaf2 contrast pair. */
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.password-field__toggle:hover,
.password-field__toggle:focus-visible {
  color: var(--text-dark);
  outline: none;
}

.password-field__toggle svg {
  width: 1.15rem;
  height: 1.15rem;
}



/* ════════════════════════════════════════════════════════════════════
   M10B Pin grouping (ADR-055) — kotva marker, expand overlay,
   delete-kotva modal, nearby chooser, quota counter.
   Pre-launch styling only — Tarja-themed marker design moves to M12.
   ════════════════════════════════════════════════════════════════════ */

/* Violet pin marker — shared by standalone pins + kotvy (kotvy add the
   count badge on top). Petřin pokyn 2026-05-14: "vykašlat se na
   modrou ... udělat fialové podle naší designové rodiny". Replaces the
   Leaflet default blue PNG with an inline SVG teardrop rendered via
   CSS mask + currentColor, so the violet palette tokens drive the look
   without any image swap. JS sets className="pin-marker-wrap" + the
   teardrop element; the badge sits on top only for kotvy. */
/* Pin marker — single SVG inline approach (M10D refactor 2026-05-14
   Petřin volba B po realistic retrospective). Předchozí mask+linear-
   gradient+::before composite chain (3 DOM + multiple paint layers
   per marker) nahrazen jediným inline SVG elementem — 1 DOM, vector
   path s gradient + Gaussian-blurred shadow ellipse uvnitř SVG. Soft
   podlouhlý shadow look podobný Leaflet defaultu. Šetří per-marker
   paint cost ~3-5× při 500+ markers. */
.pin-marker-wrap {
  background: transparent;
  border: 0;
}

.pin-marker {
  position: relative;
  /* M12 Noir PR5 — size to the inline SVG (44×55 fan / 44×62 your), not a
     fixed box; the SVG carries explicit width/height attrs. line-height:0
     removes the inline-element descender gap so the box hugs the teardrop. */
  line-height: 0;
}

.pin-marker > svg {
  display: block;
  /* SVG nepřebírá pointer events z parent container (Leaflet handles
     marker hitbox), ale jistota: */
  pointer-events: none;
  /* M12 Noir PR5 — grounding shadow is the in-SVG url(#pinShadow) ellipse
     at the base (Petřin pokyn 2026-05-25 "chybí mi původní stín"), not a
     CSS drop-shadow. */
}

/* M12 Noir PR5 — gathering count badge. Magenta pill top-right of the
   teardrop (mockup 03 "Stacked · magenta count badge"). Number gets a soft
   glow so it reads against the dark pill. */
.pin-marker__badge {
  position: absolute;
  /* Perches on the head's upper-right shoulder, overlapping only the outer
     edge — not reaching inward over the central spark (Petřin pokyn
     2026-05-25, "zasahuje moc dovnitř"; render variant A). */
  top: -5px;
  /* Centre-anchored on the shoulder point (= where the 1-digit pill used to
     sit, box 30px → centre 25.5px) so a multi-digit pill grows SYMMETRICALLY
     instead of only leftward into the head (variant A, Petřin pokyn
     2026-05-31). Pairs with the "99+" cap in map.js so it never exceeds 3
     glyphs. 1-digit render is unchanged. */
  left: calc(100% - 4.5px);
  transform: translateX(-50%);
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  background: var(--accent-mine-deep);
  color: var(--text-light);
  border-radius: 999px;
  border: 1.5px solid var(--navy-deep);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.01em;
  text-shadow: 0 0 4px rgba(255, 215, 235, 0.95);
}


/* M11.X-MINE-HIGHLIGHT (ADR-152 iter #3 — Petřin pokyn 2026-05-22
   "vůbec tam nepotřebuju ten glov, když je to obarvené celé").
   Drop-shadow halo removed — the SVG fill flip via #pinMineRust
   gradient (rust-red, see index.html defs) je sám o sobě dost
   výrazný signál, drop-shadow přidával jen šum bez informační
   hodnoty. The pin-marker-wrap--mine class still gets attached
   when the toggle is ON (so future visual additions hook here
   cleanly), but no current rule paints anything based on it —
   body color carries the entire affordance. */

/* ─────────────────────────────────────────────────────────────────────
   M12.X-OPEN-GATHERINGS (ADR-184) — open gathering "sonar" rings behind the
   teardrop (Petřin návrh 2026-05-26). Concentric dashed circles drawn with a
   CSS mask over a solid colour fill, so the tone is swappable via a token and
   we avoid the SVG-gradient-in-defs transparency trap (memory
   reference_svg_gradient_display_none_breaks_rendering). The tone follows the
   same own-vs-others logic as the body fill: iris by default, magenta when
   the marker carries the permanent --mine identity (ADR-193: identity is
   no longer gated by the sort toggle).
   ───────────────────────────────────────────────────────────────────── */
.pin-marker-wrap--open .pin-marker::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 14px; /* roughly the centre of the teardrop head */
  width: 64px;
  height: 64px;
  transform: translate(-50%, -50%);
  /* Deeper iris (not the light lavender) so the dashes read on the LIGHT
     CartoDB map — same lesson as the marker body's saturated fill. */
  background-color: var(--accent-violet-deep);
  /* Three concentric dashed circles (r=11/18/26 of a 60-box). */
  -webkit-mask: var(--open-ring-mask) center / contain no-repeat;
  mask: var(--open-ring-mask) center / contain no-repeat;
  opacity: 0.9;
  pointer-events: none;
  z-index: -1;
  animation: fm-open-ring 4.5s ease-in-out infinite;
}
:root {
  --open-ring-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 60'%3E%3Cg fill='none' stroke='%23000' stroke-width='2.4' stroke-dasharray='2.6 4.4' stroke-linecap='round'%3E%3Ccircle cx='30' cy='30' r='11'/%3E%3Ccircle cx='30' cy='30' r='18'/%3E%3Ccircle cx='30' cy='30' r='26'/%3E%3C/g%3E%3C/svg%3E");
}
/* Own highlighted open gathering → magenta ring (matches the body flip). */
.pin-marker-wrap--open.pin-marker-wrap--mine .pin-marker::before {
  background-color: var(--accent-magenta);
}
@keyframes fm-open-ring {
  0%, 100% { opacity: 0.72; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1; transform: translate(-50%, -50%) scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
  .pin-marker-wrap--open .pin-marker::before { animation: none; }
}

/* M13L Stage 2 (ADR-211 / ADR-083) — freshness is DATA-ONLY in M13L: map.js
   emits `is_fresh` + the `.pin-marker-wrap--fresh` class on moments < 24h, but
   the VISIBLE treatment (glow ring / breath) is M13P's job, styled together
   with the welcome / pin-drop animation (Petřin pokyn 2026-06-01 — the subtle
   saturate cue was invisible, so the whole visible look moves to M13P). The
   class is intentionally left unstyled here as the data hook for M13P. */

/* M18-LIVE-MAP — a NEW echo the heartbeat just added settles onto the map:
   drops ~6px + fades in, with a brief inner-light swell at mid-flight (the
   "glint"). FINITE, plays once (class removed on animationend in map.js
   animatePinArrival). Lives on the INNER .pin-marker — the outer wrap
   carries Leaflet's positioning transform and must never be transformed.
   Compositor-friendly per P-018 (transform/opacity; the filter swell is a
   one-shot, not a loop). */
.pin-marker--arriving {
  animation: fm-pin-arrive 700ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes fm-pin-arrive {
  0%   { transform: translateY(-6px); opacity: 0; filter: brightness(1); }
  55%  { opacity: 1; filter: brightness(1.28); }
  100% { transform: translateY(0); opacity: 1; filter: brightness(1); }
}

/* (The cluster "gulp" counterpart lives next to the .cluster-frost base
   block further down — same specificity, so it MUST come after it in the
   cascade or the base `animation` shorthand silences it.) */

@media (prefers-reduced-motion: reduce) {
  .pin-marker--arriving {
    animation: none;
  }
}

/* (M18-LIVE-MAP pozn.: mint "news droplet" na markerech/clusterech byl
   IMPLEMENTOVÁN A ZAMÍTNUT Petrou 2026-06-10 po renderu na reálné mapě —
   na world zoomu „nejde vidět a jen dělá bordel". NEVRACET bez nového
   rozhodnutí; KDE novinky jsou, říká klik na pilulku. Detail ve scope
   docs/scope/M18-LIVE-MAP.md.) */

/* ─────────────────────────────────────────────────────────────────────
   M11J — Pin drag & drop visual states (per ADR-150 + ADR-089 visual
   treatment section). GPU-accelerated transforms only (P-018, 60 fps).
   ───────────────────────────────────────────────────────────────────── */

/* Own pin (registered user): cursor hint that the marker is grabbable.
   On touch devices the cursor is irrelevant but Leaflet's leaflet-grab
   class can still bleed onto the marker — we override with grab so the
   affordance reads correctly on the rare touch+mouse hybrid laptops. */
.pin-marker-wrap--draggable {
  cursor: grab;
  /* M11J mobile fix (Petřin smoke 2026-05-22) — Leaflet's marker
     dragging handles touch internally, but `touch-action: none`
     guarantees no browser-level gesture (long-press menu, pull-to-
     refresh) sneaks in before Leaflet's handler claims the touch. */
  touch-action: none;
}
.pin-marker-wrap--draggable:active,
.pin-marker-wrap--draggable.pin-drag-grabbed {
  cursor: grabbing;
}

/* Grab state — fires on dragstart, removed on dragend. Violet halo
   doubles as a motion signal so the user sees the pin lift off the
   map. Uses the existing violet RGB token so we don't drift from
   the brand palette. */
.pin-marker-wrap.pin-drag-grabbed {
  transform: scale(1.15);
  filter: drop-shadow(0 8px 18px rgba(var(--accent-violet-rgb), 0.5));
  transition: transform 120ms cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 1000;
}

/* Merge preview state — applied to a TARGET marker when the dragged
   pin is hovering close enough (same owner + same country). Turquoise
   outline + slow pulse so it reads as "drop here = merge" without
   competing with the dragged pin's violet halo. */
.pin-marker-wrap.pin-drag-merge-target {
  transform: scale(1.1);
  filter: drop-shadow(0 0 6px rgba(var(--accent-turquoise-rgb), 0.85));
  animation: pin-drag-merge-pulse 1.1s ease-in-out infinite;
}

@keyframes pin-drag-merge-pulse {
  0%, 100% {
    filter: drop-shadow(0 0 4px rgba(var(--accent-turquoise-rgb), 0.6));
  }
  50% {
    filter: drop-shadow(0 0 10px rgba(var(--accent-turquoise-rgb), 0.95));
  }
}

/* Merge reject preview — incompatible target (test-data vs real pin
   per ADR-118 R3 isolation). Same proximity trigger as merge-target
   but red glow + tooltip signal "can't merge here"; drop falls
   through to reposition (= no PATCH parent_pin_id). */
.pin-marker-wrap.pin-drag-merge-reject {
  transform: scale(1.1);
  filter: drop-shadow(0 0 6px rgba(218, 52, 64, 0.85));
  animation: pin-drag-merge-reject-pulse 1.1s ease-in-out infinite;
}

@keyframes pin-drag-merge-reject-pulse {
  0%, 100% {
    filter: drop-shadow(0 0 4px rgba(218, 52, 64, 0.6));
  }
  50% {
    filter: drop-shadow(0 0 10px rgba(218, 52, 64, 0.95));
  }
}

/* Floating tooltip near a rejected target — explains WHY the merge
   can't happen. Positioned by JS in viewport coordinates so it sits
   above the map pane without clipping by Leaflet's overflow. */
.pin-drag-tooltip {
  position: fixed;
  z-index: 10000;
  pointer-events: none;
  transform: translate(-50%, 0);
  padding: 6px 12px;
  border-radius: var(--radius-sm, 4px);
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 120ms ease;
}
.pin-drag-tooltip.is-visible {
  opacity: 1;
}
.pin-drag-tooltip--reject {
  background: var(--danger, #da3440);
  color: #fff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

/* Reject animation — drop on an invalid target or PATCH 4xx, marker
   snaps back to original position with a small "reject" wobble.
   Transform-only so the GPU keeps it at 60 fps even on weaker phones. */
.pin-marker-wrap.pin-drag-reject {
  animation: pin-drag-reject 320ms ease-out;
}

@keyframes pin-drag-reject {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-3px, 0); }
  40%  { transform: translate(3px, 0); }
  60%  { transform: translate(-2px, 0); }
  80%  { transform: translate(2px, 0); }
  100% { transform: translate(0, 0); }
}

/* Undo toast variant — adds the inline action button next to the
   message. Uses the existing toast stack layout from uiUtils.js. */
.toast.toast--with-action {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}
.toast__msg {
  flex: 1 1 auto;
  min-width: 0;
}
.toast__action {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--accent-turquoise-bright);
  color: var(--accent-turquoise-bright);
  padding: 4px 12px;
  border-radius: var(--radius-sm, 4px);
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.toast__action:hover:not(:disabled) {
  background: var(--accent-turquoise-bright);
  color: #fff;
}
.toast__action:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Popup additions for kotva — heading layout + expand button. */
.pin-popup__heading {
  display: flex;
  flex-direction: column;
  /* M12.X (Petřin pokyn 2026-05-30) — the 2px gap glued the 3 header tiers
     (name / @nick / Open) together; one space-1 step lets them breathe. */
  gap: var(--space-1);
  /* M11.X-CONTAINER-ENTITY (Petřin pokyn 2026-05-24) — grow to fill the
     header row's free width beside the 28px avatar. Without flex-grow the
     heading is a flex item with default `flex: 0 1 auto`, so it shrink-wraps
     to its widest line (the date string). The popup is min-width:380px but
     that extra space sat EMPTY to the right of the shrink-wrapped heading,
     so `.pin-popup__meta-when { text-align:right }` had no slack to push the
     listened-at time against → it looked left-aligned under the place. Real
     root cause was the shrink-wrap, not the string length (the earlier
     "drop the LISTENED prefix" attempt mistook the symptom). min-width:0
     lets a long unbreakable line wrap instead of forcing the item wider. */
  flex: 1 1 auto;
  min-width: 0;
}
.pin-popup__kotva-name {
  /* M12 Noir PR5 ③ (mockup 19) — L1 primary line: Cormorant ITALIC, moon
     (--text-dark), ~19px. Effective definition (later than the turquoise base
     above); supersedes the Frost turquoise 1.75rem treatment. */
  font-weight: var(--fw-semibold);
  font-style: italic;
  font-size: var(--fs-lg);
  color: var(--text-dark);
  /* M13.X-GATHERING-POLISH (Petřin pokyn 2026-06-01) — vertically CENTER the
     inline TEST/OPEN chips with the name (mockup) instead of letting them sit on
     the italic baseline (read low). Flex for vertical centering ONLY; the chips
     keep their own `margin-left` for horizontal spacing (Petřin pokyn 2026-06-01 —
     zeroing it glued the chips to the name with no padding). */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.pin-popup__by-nick {
  font-size: 0.85rem;
  opacity: 0.75;
}
/* M11.X-ANCHOR-AUDIT Stage E (2026-05-22) — .pin-popup__expand (= old
   „Show all" button) removed. Pagination iter 1 (.pin-popup__page /
   .pin-popup__pagination) replaced by scrollable container per Petřin
   follow-up („by byll lepší mít skrolování uvnitř toho kontejneru?").
   Reason: pagination page-click → innerHTML swap → Leaflet
   ResponsivePopup recompute = perceived „new popup".
   Dead `.pin-expand-overlay*` + `.pin-expand-item*` blocks ponechány
   do M12G dead-CSS sweep. */

/* M11.X-ANCHOR-AUDIT Stage E iter 2 — scrollable container rules
   merged INTO the existing .pin-popup__preview block below (see
   `Container for the per-row preview items`). Kept this comment as
   a breadcrumb back to the iteration reason. */

/* M10B Stage 2 (STYLE-002) — shared atmospheric chrome for the 3
   custom div modals (delete-kotva, expand-overlay, add-pin-nearby).
   Mirrors the dialog[id^="auth-"] family so a div modal "feels like"
   the rest of the app's dialog system: atmospheric radial bg on the
   panel + radial-tinted backdrop with blur. Without this, the
   div-based modals looked flat/grey next to the gradient-rich auth
   dialogs even though they live in the same UI session. */
.pin-expand-overlay,
.delete-kotva-modal,
.add-pin-nearby-modal {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.pin-expand-overlay[hidden],
.delete-kotva-modal[hidden],
.add-pin-nearby-modal[hidden] {
  display: none;
}

/* ============================================================
   M12 Noir PR6-A — Moments-nearby chooser (mockup 09).
   Two tone-coded choice cards: JOIN GATHERING (iris) vs
   STANDALONE (magenta = own). Replaces the Frost-era plain
   list that borrowed .delete-kotva-modal__option. Own panel
   class so it stays decoupled from the moderate-gathering
   modal (that one is redesigned separately in Dávka D).
   ============================================================ */
.moments-nearby-modal__panel {
  position: relative;
  width: 100%;
  max-width: 460px;
  padding: 1.5rem;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  box-shadow:
    var(--shadow-modal),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}
.moments-nearby-modal__title {
  /* Canonical dialog title — white Cormorant italic (ui-standards §2).
     Small tier (M13.X-TITLE-TIERS, Petřin výběr 2026-06-03) — the nearby
     chooser is a mid-flow decision dialog, not a content modal. */
  margin: 0 0 0.25rem 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-dialog-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  color: var(--text-dark);
  /* Reserve room so the absolute .dialog-close doesn't overlap the title. */
  padding-right: 2.5rem;
}
.moments-nearby-modal__subtitle {
  margin: 0 0 1rem 0;
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--text-muted);
}

.moments-nearby {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
}

/* ── M12.X-NEARBY-REORDER (Petřin redesign 2026-05-31, revised after 3-lens
   review) ───────────────────────────────────────────────────────────────
   The four options must be tellable apart at a glance — by STRUCTURE (two
   labelled sections), IDENTITY (avatar + @nick on foreign rows) and a
   distinct left ICON per type, never by colour alone and never by hiding
   options. Every card is a dark NOIR panel with light text (canonical
   `delete-kotva-modal__option` pattern) — no loud solid fills. */

/* Scroll area: at most 3 cards (standalone + nearest of mine + nearest
   foreign open gathering, ADR-206), so this rarely scrolls — the height cap
   stays as a safety net. */
.moments-nearby-modal__scroll {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-height: min(60vh, 460px);
  overflow-y: auto;
  margin-bottom: var(--space-4);
  /* room so the scrollbar doesn't crowd the cards */
  padding-right: var(--space-1);
}

/* Section header — answers "mine vs someone else's" before any card is read. */
.moments-nearby__section-label {
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-loose);
  text-transform: uppercase;
  color: var(--text-muted);
  margin: var(--space-3) var(--space-px) 0;
}

/* Standalone = your own NEW Echo. Dark magenta-tinted card with a magenta
   identity edge + a single solid-magenta glyph as the one colour pop —
   readable light text, native to NOIR (NOT a solid magenta block). */
.moments-nearby__primary {
  width: 100%;
  appearance: none;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--accent-mine-wash);
  border: 1px solid rgba(var(--accent-mine-rgb), 0.36);
  border-left: 3px solid var(--accent-mine);
  color: var(--text-dark);
  font: inherit;
  transition: background 160ms ease, border-color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .moments-nearby__primary:hover {
    background: rgba(var(--accent-mine-rgb), 0.2);
    border-color: var(--accent-mine);
  }
}
.moments-nearby__primary:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--accent-mine-rgb), 0.4);
}
.moments-nearby__primary-glyph {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  background: var(--accent-mine);
  color: var(--text-on-accent);
}
.moments-nearby__primary-body {
  flex: 1 1 auto;
  min-width: 0;
}
.moments-nearby__primary-title {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
  color: var(--text-dark);
  line-height: 1.2;
}
.moments-nearby__primary-sub {
  display: block;
  margin-top: var(--space-px);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* Join card — dark NOIR panel (canonical option-card surface), light text. */
.moments-nearby__card {
  width: 100%;
  appearance: none;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--cream-base);
  border: 1px solid var(--cream-sunken);
  color: var(--text-dark);
  font: inherit;
  transition: background 160ms ease, border-color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .moments-nearby__card:hover {
    background: var(--cream-sunken);
    border-color: var(--accent-turquoise);
  }
}
.moments-nearby__card:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--accent-turquoise-rgb), 0.18);
}

/* Left mark: an icon tile (your places) or the owner's avatar (foreign). */
.moments-nearby__rowicon {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  background: rgba(var(--accent-violet-rgb), 0.14);
  color: var(--accent-violet-soft);
}
.moments-nearby__avatar {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  overflow: hidden;
  display: grid;
  place-items: center;
}
.moments-nearby__avatar svg {
  width: 38px;
  height: 38px;
}
.moments-nearby__card-body {
  flex: 1 1 auto;
  min-width: 0;
}
.moments-nearby__card-eyebrow {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-loose);
  text-transform: uppercase;
  color: var(--nick-color);
  margin-bottom: var(--space-px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.moments-nearby__card-name {
  display: block;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.moments-nearby__card-meta {
  display: block;
  margin-top: var(--space-px);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.moments-nearby__card-hint {
  display: block;
  margin-top: 2px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-2xs);
  font-style: italic;
  color: var(--text-muted);
}
.pin-expand-overlay__panel {
  /* Shared panel chrome with delete-kotva-modal__panel — see that
     selector for the atmospheric radial gradient + multi-layer shadow.
     Listed separately because expand has its own max-width / scroll. */
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  max-width: 520px;
  width: 100%;
  /* M-UX-AUDIT-010 per P-034 (mobile): dvh. */
  max-height: 80dvh;
  overflow-y: auto;
  padding: 1.5rem;
  position: relative;
  box-shadow:
    var(--shadow-modal),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}
/* .pin-expand-overlay__close — unified styling shared with .dialog-close
   (Petřin pokyn 2026-05-16 odp.). */

/* M10B Stage 2 (STYLE-008) — pin-expand-overlay sub-components that
   were referenced in pinExpandOverlay.js but had no CSS definition.
   They rendered with the browser UA default (sharp grey rectangles)
   inside the otherwise atmospheric panel. */
.pin-expand-overlay__loading {
  font-size: 0.9rem;
  color: var(--text-muted);
  font-style: italic;
  text-align: center;
  padding: 1rem 0;
}
.pin-expand-overlay__rename,
.pin-expand-overlay__add-child {
  appearance: none;
  background: transparent;
  border: none;
  color: var(--accent-turquoise);
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
  padding: 0.2rem 0.5rem;
  border-radius: var(--radius-sm);
  transition: color 150ms ease, background 150ms ease;
  /* M11.X-ANCHOR-AUDIT (2026-05-22) — icon + text flex layout. SVG sits
     inline with the label; gap keeps them visually paired. */
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.pin-expand-overlay__rename svg,
.pin-expand-overlay__add-child svg {
  display: block;
}
.pin-expand-overlay__rename:hover,
.pin-expand-overlay__rename:focus-visible,
.pin-expand-overlay__add-child:hover,
.pin-expand-overlay__add-child:focus-visible {
  color: var(--accent-copper);
  background: var(--cream-sunken);
  outline: none;
}

/* M11.X-ANCHOR-AUDIT (2026-05-22) — header actions cluster (rename + add
   child). inline-flex so the two buttons sit side-by-side after the count
   text, with a small gap. */
.pin-expand-overlay__header-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  margin-left: 0.6rem;
}
.pin-expand-overlay__load-more {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 0.6rem 1rem;
  background: transparent;
  border: 1px solid var(--accent-turquoise);
  color: var(--accent-turquoise);
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background 200ms ease, color 200ms ease;
}
.pin-expand-overlay__load-more:hover,
.pin-expand-overlay__load-more:focus-visible {
  background: var(--cream-sunken);
  outline: none;
}
.pin-expand-overlay__load-more:disabled {
  opacity: 0.5;
  cursor: wait;
}
.pin-expand-overlay__header h2 {
  margin: 0 0 4px 0;
  font-size: 1.25rem;
}
.pin-expand-overlay__count {
  margin: 0 0 16px 0;
  opacity: 0.7;
  font-size: 0.9rem;
}
.pin-expand-overlay__list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.pin-expand-item {
  display: flex;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px solid var(--cream-sunken);
}
.pin-expand-item:first-child {
  border-top: 0;
}
.pin-expand-item__avatar {
  flex-shrink: 0;
  border-radius: 50%;
}
.pin-expand-item__body {
  flex: 1;
  min-width: 0;
}
.pin-expand-item__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 0.85rem;
  opacity: 0.8;
  margin-bottom: 4px;
}
.pin-expand-item__category {
  font-weight: 600;
}
.pin-expand-item__custom-label {
  font-style: italic;
}
.pin-expand-item__comment {
  margin: 0;
  font-size: 0.95rem;
}
/* M11D ADR-080: grandfathered legacy pin with null/empty comment.
   Muted + italic = "gently signalize absence" per ADR. */
.pin-expand-item__comment--muted {
  color: var(--text-muted, #7a7a8a);
  font-style: italic;
  opacity: 0.75;
}
.pin-expand-item__actions {
  flex-shrink: 0;
}
.pin-expand-item__delete {
  /* M11.X-ANCHOR-AUDIT (2026-05-22) — text label „Delete" swapped for
     inline SVG trash icon. Square padding + flex centering for clean
     icon-only render; danger-red color via currentColor cascades into
     the SVG stroke. */
  padding: 0.4rem;
  border: 1px solid var(--danger, #b83838);
  color: var(--danger-text);
  background: transparent;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
  transition: background 150ms ease, color 150ms ease;
}
.pin-expand-item__delete svg {
  display: block;
}
.pin-expand-item__delete:hover,
.pin-expand-item__delete:focus-visible {
  background: var(--danger-soft, rgba(180, 35, 35, 0.12));
  outline: none;
}

/* Delete-kotva modal — Q7D'. Overlay chrome shared with pin-expand
   and add-pin-nearby (see "M10B Stage 2 (STYLE-002)" block above);
   panel uses the same atmospheric chrome as auth dialogs. */
.delete-kotva-modal__panel {
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  max-width: 480px;
  width: 100%;
  padding: 1.5rem;
  position: relative;
  /* M12 PR6 Dávka D — set the light text color on the panel itself, like every
     other canonical shell (.frost-modal / .confirm-dialog / .event-detail-modal
     all do). It previously only inherited --text-dark from <body>; making it
     self-contained keeps the option-card title + description legible on the
     dark surface regardless of context. */
  color: var(--text-dark);
  box-shadow:
    var(--shadow-modal),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}
/* .delete-kotva-modal__close — unified styling shared with .dialog-close
   (Petřin pokyn 2026-05-16 odp.). */
/* M13.X-TITLE-TIERS — delete/rename Landmark are quick confirm/edit prompts, so
   they moved out of the big shared `.delete-kotva-modal h2` list into the small
   dialog tier. Now self-contained (font props no longer inherited from that
   shared rule). padding-right reserves room for the 48px × close so a long
   gathering name in the title doesn't slide under it (both dialogs carry the ×). */
.delete-kotva-modal__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-dialog-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  color: var(--text-dark);
  padding-right: 2.8rem;
}
.delete-kotva-modal__subtitle {
  margin: 0 0 16px 0;
  font-size: 0.9rem;
  opacity: 0.8;
}
.delete-kotva-modal__options {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}
.delete-kotva-modal__option {
  /* M10B Stage 2 (STYLE-003 + STYLE-005) — token radii + deep
     turquoise hover border (bright variant is dark-bg only). */
  text-align: left;
  background: var(--cream-base);
  border: 1px solid var(--cream-sunken);
  border-radius: var(--radius-md);
  padding: 0.75rem 0.9rem;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: inherit;
  /* M12 PR6 Dávka D — <button> resets text color to the UA default (black),
     which rendered the option title + description as near-invisible dark-on-
     dark. Set the light surface color explicitly so the cards read on Noir. */
  color: var(--text-dark);
  transition: background 200ms ease, border-color 200ms ease;
}
.delete-kotva-modal__option:hover,
.delete-kotva-modal__option:focus-visible {
  background: var(--cream-sunken);
  border-color: var(--accent-turquoise);
  outline: none;
}
.delete-kotva-modal__option strong {
  font-size: 1rem;
}
.delete-kotva-modal__option span {
  font-size: 0.85rem;
  opacity: 0.75;
}
.delete-kotva-modal__option--dimmed {
  opacity: 0.6;
}
/* M18.X-GATHERING-LEAVE — destructive option (delete my other Echoes / delete
   the whole group). Brick accent on the title + hover border per HARD RULE 12;
   no loud solid fill (keeps parity with the card pattern above).
   --danger-text, NOT --danger (M18 review STYLE-M18-001): --danger is the
   FILL shade — as text on dark it reads duller than every other danger text
   (HARD RULE 13). The hover border below correctly keeps --danger (fill role). */
.delete-kotva-modal__option--danger strong {
  color: var(--danger-text);
}
.delete-kotva-modal__option--danger:hover,
.delete-kotva-modal__option--danger:focus-visible {
  border-color: var(--danger);
  background: var(--cream-sunken);
}
.delete-kotva-modal__footer {
  display: flex;
  gap: 0.7rem; /* match .form-actions — buttons were flush/touching (Petřin pokyn 2026-05-24) */
  justify-content: flex-end;
  margin-top: 0.6rem;
}

/* Add Pin nearby chooser — reuses delete-kotva-modal panel styling
   AND the shared overlay chrome defined above (STYLE-002 block). */

/* M10B Stage 2 (STYLE-012) — dead CSS strip:
   `.quota-banner*` (4 selectors) were prepared as alternate quota UI
   but the final implementation used `dialog-hint--soft` / `--warn`
   classes in form.js. Removed to keep style.css scan signal high.
   .pin-grouping-state / .pin-grouping-chip removed below as well. */

/* Admin placeholder pane — Moderators tab M11 prep (Petřin pokyn 2026-05-16
   Session #7 admin polish). Same atmospheric Frost chrome jako .admin-limits
   pro visual continuity. Cormorant Aurora h2 + body explainer + hint about
   current manual workflow (Cognito console). */
.admin-placeholder {
  margin: 2rem 0 1rem;
  padding: 1.5rem 1.4rem 1.3rem;
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-plum) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.admin-placeholder h2 {
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-dark);
}
.admin-placeholder p {
  margin: 0 0 0.8rem;
  font-size: 0.95rem;
  color: var(--text-dark);
  line-height: 1.5;
}
.admin-placeholder__hint {
  font-size: 0.85rem !important;
  color: var(--text-muted) !important;
  font-style: italic;
  border-top: 1px solid var(--border-on-light);
  padding-top: 0.7rem;
  margin-top: 0.5rem !important;
}

/* Admin Limits section — M10 closure Session #7 admin polish (2026-05-16).
   FIX: Full Frost-polish per Petřin "vypadá z minulého století" + memory
   feedback_design_rollout_all_or_nothing. Pre-fix: basic h2 1.1rem, ugly UA
   inputs (padding 4/8px, border cream-sunken), bare container. Post-fix:
   Cormorant Aurora h2, atmospheric mesh container + halo card shadow, Frost
   inputs (cream-surface bg + border-on-light + radius-sm + focus turquoise
   outline + font 1rem per P-038 iOS auto-zoom). */
.admin-limits {
  margin: 2rem 0 1rem;
  padding: 1.5rem 1.4rem 1.3rem;
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-copper) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.admin-limits h2 {
  margin: 0 0 0.4rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-dark);
}
.admin-limits p {
  margin: 0 0 1.2rem;
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.45;
}
.admin-limits__row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin-bottom: 0.7rem;
}
.admin-limits__row label {
  flex: 1;
  font-size: 0.9rem;
  color: var(--text-dark);
}
.admin-limits__row input {
  width: 88px;
  padding: 0.5rem 0.7rem;
  background: var(--cream-base);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  color: var(--text-dark);
  font-family: var(--font-body);
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.admin-limits__row input:focus {
  outline: none;
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 3px var(--accent-turquoise-tint);
}
.admin-limits__status {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin: 0.8rem 0 0;
  min-height: 1.2em;
}

/* .admin-limits__save-row removed (M15.X-ADMIN-HEADER, ADR-263) — the two
   per-section Save rows merged into the sticky pane band's single Save.
   The status badge in the band keeps the "Saved" label next to the button
   (Petřin pokyn 2026-05-23 stays honoured). */
.admin-pane__band-actions .admin-limits__status {
  margin: 0;
  min-height: 0;
}
/* Inline "Saved." badge next to the instant-save AI toggles (ADR-263). */
.admin-limits__row--checkbox .admin-limits__status {
  margin: 0 0 0 0.75rem;
  min-height: 0;
}
/* Success/error variants render the status as a readable filled badge.
   Sage-green wash for "Saved." (--success is AA on cream), danger wash
   for failures. Fades out after the JS hide timeout. */
.admin-limits__status--success,
.admin-limits__status--error {
  display: inline-block;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
  opacity: 1;
  transition: opacity 400ms ease;
}
.admin-limits__status--success {
  color: var(--success);
  background: color-mix(in srgb, var(--success) 16%, var(--cream-surface));
  border: 1px solid color-mix(in srgb, var(--success) 38%, transparent);
}
.admin-limits__status--error {
  color: var(--danger-text);
  background: color-mix(in srgb, var(--danger) 12%, var(--cream-surface));
  border: 1px solid color-mix(in srgb, var(--danger) 38%, transparent);
}
.admin-limits__status--fade {
  opacity: 0;
}

/* M11.X-ADMIN-LIMITS-CONFIG — per-input helper line ("what this limit
   does") under each AI/propose cap row. Sits tight under its row, set
   in muted text so labels stay the primary read. */
.admin-limits__hint {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin: -0.3rem 0 0.9rem;
  line-height: 1.4;
}

/* M11G Stage A.2 — AI POI kill switch checkbox row + alert banner.
   Banner mirrors the AWS Bedrock budget threshold ($10/mo cap) when
   spend crosses 80% (warning) or 100% (critical). Uses --danger token
   for parity with destructive CTAs (per feedback_destructive_ctas_uniform_red). */
.admin-limits__row--checkbox {
  align-items: flex-start;
  gap: 0.6rem;
}
.admin-limits__row--checkbox label {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-dark);
}
.admin-limits__row--checkbox input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent-turquoise);
  cursor: pointer;
}
.admin-limits__alert {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, var(--cream-surface));
  color: var(--text-dark);
  font-size: 0.9rem;
  line-height: 1.45;
}
.admin-limits__alert[data-level="warning"] {
  /* Slightly softer tone — still red-family, but signals "approaching"
     rather than "exceeded". Same border, lighter wash. */
  background: color-mix(in srgb, var(--danger) 9%, var(--cream-surface));
}
.admin-limits__alert[data-level="unknown"] {
  /* Unable to read AWS Budget — neutral copper tone, not full alarm. */
  border-color: var(--accent-copper);
  background: color-mix(in srgb, var(--accent-copper) 12%, var(--cream-surface));
  color: var(--text-muted);
}

/* M11.X-USAGE-PANEL — sub-headings for each service block inside the
   "Usage & spend" section. A hairline top divider separates the three
   services (AI / web search / email) without nesting extra cards. */
.admin-usage__sub {
  margin: 1.5rem 0 0.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-on-light);
  /* Match the modal field-label token (.field__label) so the service headings
     read as small tracked labels, not Cormorant display — keeps the Limits
     tab legible + consistent with the rest of the app. Petřin pokyn 2026-05-28
     — "stejný token jako názvy polí v modálních oknech". */
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--label-color);
}
.admin-usage__sub:first-of-type {
  margin-top: 0.4rem;
  padding-top: 0;
  border-top: none;
}
/* Headline whole-account AWS total — the "what has this cost me overall"
   line, set larger/bolder than the per-service status lines below it. */
.admin-usage__total {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text-dark);
  margin: 0.1rem 0 0.3rem;
}

/* ──────────────────────────────────────────────────────────────────
   M11.5 Stage 4 — Test Data admin tab (ADR-114)
   Bulk Convert / Delete across 4 entities (pins / events / tours /
   sigplaces). Follows .admin-limits Frost chrome — atmospheric mesh
   container + Aurora h2 + Frost-toned table. Convert = turquoise
   (success), Delete = danger red (per feedback_destructive_ctas_uniform_red).
   ────────────────────────────────────────────────────────────────── */

/* Success CTA — counterpart k .btn-danger, follows same gradient pattern
   but with turquoise (success / "make real" / save) instead of red. */
.btn-success,
dialog button.btn-success {
  background: linear-gradient(135deg, var(--accent-turquoise-bright) 0%, var(--accent-turquoise) 100%);
  color: var(--text-on-accent);
  border: 1px solid var(--accent-turquoise);
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow:
    0 4px 12px rgba(var(--accent-turquoise-rgb), 0.28),
    inset 0 1px 0 rgba(238, 240, 250, 0.18);
}
.btn-success:hover,
.btn-success:focus-visible,
dialog button.btn-success:hover,
dialog button.btn-success:focus-visible {
  border-color: var(--accent-turquoise-bright);
  box-shadow:
    0 8px 22px rgba(var(--accent-turquoise-rgb), 0.40),
    inset 0 1px 0 rgba(238, 240, 250, 0.22);
}
@media (hover: hover) and (pointer: fine) {
  .btn-success:hover,
  dialog button.btn-success:hover {
    transform: translateY(-1px);
  }
}
.btn-success:disabled,
dialog button.btn-success:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.btn-danger:disabled,
dialog button.btn-danger:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
/* M12 PR6 Dávka D — canonical disabled state for the primary CTA (was only
   defined on the now-removed bespoke change-suggestion submit). One rule,
   propagates to every .btn-primary (change-suggestion, future submits). */
.btn-primary:disabled,
dialog button.btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.admin-test-data {
  margin: 1rem 0 1rem;
  padding: 1.6rem 1.4rem 1.4rem;
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-plum) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.admin-test-data__header {
  margin-bottom: 1.1rem;
}
.admin-test-data__header h2 {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-dark);
}
.admin-test-data__subtitle {
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.admin-test-data__subtitle code {
  background: var(--cream-sunken);
  padding: 0.05rem 0.35rem;
  border-radius: 3px;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 0.85em;
  color: var(--text-dark);
}

/* Sticky-ish global toolbar — count + batch action buttons. Wraps on
   narrow widths so the two CTAs stack under the counter rather than
   spill horizontally. */
/* Unified toolbar per Petřin redesign 2026-05-21 — refresh + filters +
   selection summary + bulk action buttons all sit in one row, matching
   the audit log layout pattern. */
.admin-test-data__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1.25rem;
  background: rgba(var(--cream-surface-rgb), 0.8);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  margin-bottom: 1.2rem;
}
/* .admin-test-data__count removed (M15.X-ADMIN-HEADER, ADR-263) — the
   count moved into the sticky pane band (.admin-pane__band-count). */
.admin-test-data__batch-actions {
  display: flex;
  gap: 0.5rem;
  /* Takes over the count's old right-edge anchor in the toolbar. */
  margin-left: auto;
}

/* Filter dropdowns — native <select> with Frost border + body font.
   Same shape as the page-size selector in admin pagination. */
.admin-test-data__filter {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.admin-test-data__filter-select {
  appearance: auto;
  border: 1px solid var(--border-on-light);
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-weight: 500;
  height: 2rem;
  padding: 0 0.5rem;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: border-color 140ms ease;
}
.admin-test-data__filter-select:hover {
  border-color: var(--text-dark);
}
.admin-test-data__filter-select:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
}

/* Reusing .admin-table base — padding, hover, header typography from
   the unified .admin-table tbody rules. Per-column widths tuned for
   the 7-column layout (Type / Title / Author / Status / Details / Date
   / Actions). Bulk-selection column + bulk-action button styles
   removed 2026-05-21 along with the bulk-action UI.
   2026-05-21 round 12 (Petřin screenshot — table overflows): widths
   trimmed + `table-layout: auto` so the table flexes to its container
   instead of overflowing horizontally. */
.admin-test-data__table {
  margin-top: 0.2rem;
  table-layout: auto;
  width: 100%;
}
/* Checkbox column restored 2026-05-22 (Petřin pokyn). Narrow + centered;
   accent-turquoise picks up the Frost CTA color used everywhere else. */
.admin-test-data__col-check {
  width: 2.5rem;
  text-align: center;
}
.admin-test-data__col-check input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent-turquoise);
  cursor: pointer;
}
/* Soft turquoise tint on selected rows — wins over the .admin-table
   zebra striping so the eye can scan the selection at a glance. */
.admin-test-data__row--selected,
.admin-table tbody tr.admin-test-data__row--selected:nth-child(even) {
  background: var(--accent-turquoise-tint);
}
/* Stable button height so the toolbar doesn't jump between enabled /
   disabled state. Baseline visual comes from btn-primary / btn-danger. */
.admin-test-data__bulk-action {
  min-height: 2rem;
}

/* M11.X-ADMIN-BULK (ADR-161) — Reports queue bulk selection. Clones the
   Test Data tab pattern so the checkbox column + batch toolbar feel
   identical across admin surfaces (Petřin pokyn „stejné jako test data"). */
.admin-reports__batch-actions {
  display: flex;
  gap: 0.5rem;
}
.admin-reports__bulk-action {
  min-height: 2rem;
}
.admin-reports__col-check {
  width: 2.5rem;
  text-align: center;
}
.admin-reports__col-check input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent-turquoise);
  cursor: pointer;
}
/* Soft turquoise tint on selected rows — wins over the .admin-table zebra
   striping so the eye can scan the selection at a glance. */
.admin-reports__row--selected,
.admin-table tbody tr.admin-reports__row--selected:nth-child(even) {
  background: var(--accent-turquoise-tint);
}
.admin-test-data__col-type {
  width: 5rem;
}
.admin-test-data__col-title {
  /* Title is the prominent text column — allow it to flex; cap upper to
     prevent eating all width when other columns have short content. */
  width: auto;
  min-width: 10rem;
}
.admin-test-data__col-author {
  width: 7rem;
}
.admin-test-data__col-status {
  width: 6rem;
}
.admin-test-data__col-details {
  /* Auto — takes remaining width; long text gets truncated by overflow. */
  width: auto;
  max-width: 18rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.admin-test-data__col-date {
  width: 6rem;
}
.admin-test-data__table td {
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* Type chips — one per entity. Subtle pastel fill + matching border so
   the chip reads as a category badge, not as a noisy CTA. */
.admin-test-data__type-chip {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  border: 1px solid transparent;
  background: var(--cream-mist);
  color: var(--text-dark);
  white-space: nowrap;
}
.admin-test-data__type-chip--pins {
  background: rgba(var(--accent-violet-rgb), 0.12);
  border-color: rgba(var(--accent-violet-rgb), 0.35);
  color: var(--accent-violet-soft);
}
.admin-test-data__type-chip--concerts {
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  border-color: rgba(var(--accent-turquoise-rgb), 0.35);
  color: var(--accent-turquoise);
}
.admin-test-data__type-chip--online {
  background: rgba(var(--accent-online-rgb), 0.12);
  border-color: rgba(var(--accent-online-rgb), 0.35);
  color: var(--accent-online);
}
.admin-test-data__type-chip--tours {
  background: rgba(var(--accent-copper-rgb), 0.12);
  border-color: rgba(var(--accent-copper-rgb), 0.35);
  color: var(--accent-copper);
}
.admin-test-data__type-chip--sigplaces {
  background: rgba(184, 134, 45, 0.12);
  border-color: rgba(184, 134, 45, 0.35);
  color: var(--warning);
}

/* Status chips — moderation states + tour "active". Match the dot-style
   used in admin moderation queues so admin can scan at a glance. */
.admin-test-data__status-chip {
  display: inline-block;
  font-size: 0.75rem;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  border: 1px solid transparent;
  background: var(--cream-mist);
  color: var(--text-muted);
  white-space: nowrap;
}
.admin-test-data__status-chip--approved {
  background: rgba(45, 138, 92, 0.12);
  border-color: rgba(45, 138, 92, 0.35);
  color: var(--success);
}
.admin-test-data__status-chip--pending {
  background: rgba(184, 134, 45, 0.12);
  border-color: rgba(184, 134, 45, 0.35);
  color: var(--warning);
}
.admin-test-data__status-chip--rejected {
  background: rgba(var(--danger-rgb), 0.12);
  border-color: rgba(var(--danger-rgb), 0.35);
  color: var(--danger-text);
}
.admin-test-data__status-chip--active {
  /* "Active" applies to pins + tours (no moderation workflow) — keep it
     neutral so it doesn't compete visually with the meaningful states. */
  background: var(--cream-mist);
  border-color: var(--border-on-light);
  color: var(--text-muted);
}

.admin-test-data__error {
  color: var(--danger-text);
  font-style: normal;
}

/* Confirm dialog body — per-entity counts list visually compact. */
.admin-test-data__confirm-body p {
  margin: 0 0 0.8rem;
  font-size: 0.92rem;
  color: var(--text-dark);
  line-height: 1.5;
}
.admin-test-data__confirm-counts {
  margin: 0 0 0.5rem;
  padding: 0.6rem 0.9rem;
  background: var(--cream-sunken);
  border-radius: var(--radius-sm);
  list-style: none;
  font-variant-numeric: tabular-nums;
}
.admin-test-data__confirm-counts li {
  padding: 0.15rem 0;
  font-size: 0.92rem;
  color: var(--text-dark);
}

/* Mobile — stack toolbar contents vertically and let action buttons
   span full width so they're tap-reachable on narrow screens. */
@media (max-width: 640px) {
  .admin-test-data__toolbar {
    flex-direction: column;
    align-items: stretch;
  }
  .admin-test-data__batch-actions,
  .admin-reports__batch-actions {
    flex-direction: column;
  }
  .admin-test-data__batch-actions .btn-primary,
  .admin-test-data__batch-actions .btn-danger,
  .admin-reports__batch-actions .btn-primary,
  .admin-reports__batch-actions .btn-danger {
    width: 100%;
  }
  .admin-test-data__filter {
    width: 100%;
    justify-content: space-between;
  }
}

/* Public Test Mode gate (M11.5 Stage 4, ADR-118 R6). Solid cream card
   above the toolbar so admins can flip the gate without scrolling past
   the entity sections. Stays on its own row above the toolbar. */
.admin-test-data__public-gate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1rem;
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  margin-bottom: 0.85rem;
}
.admin-test-data__public-gate-text {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}
.admin-test-data__public-gate-text strong {
  font-size: 0.95rem;
  color: var(--text-dark);
  font-weight: 600;
}
.admin-test-data__public-gate-hint {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.4;
}

/* M17 launch-gate chip per F-UX-007 (Stage 7 review). Sits under the
   "Public test mode" label as a small-caps marker so the admin sees at
   a glance "this is the lever you flip at launch". Same olive-ish
   accent vibe as moderation badges but tuned to read as informational,
   not destructive. */
.admin-test-data__public-gate-launch-chip {
  display: inline-block;
  align-self: flex-start;
  margin-top: 0.1rem;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-pill);
  background: var(--accent-turquoise-tint, rgba(var(--accent-turquoise-rgb), 0.10));
  color: var(--accent-turquoise);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.32);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Toggle switch — generic so it can also serve the Stage 6 Test Mode
   element later. ON state uses --danger so it visually matches the
   test-data red palette across markers + chips per ADR-118 R4. */
.switch {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  user-select: none;
}
.switch input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.switch__slider {
  position: relative;
  width: 42px;
  height: 22px;
  background: var(--border-on-light);
  border-radius: 999px;
  transition: background 160ms ease;
  flex-shrink: 0;
}
.switch__slider::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
  transition: transform 160ms ease;
}
.switch input[type="checkbox"]:checked + .switch__slider {
  background: var(--danger);
}
.switch input[type="checkbox"]:checked + .switch__slider::before {
  transform: translateX(20px);
}
.switch input[type="checkbox"]:focus-visible + .switch__slider {
  outline: 2px solid var(--focus-ring, var(--accent-turquoise));
  outline-offset: 2px;
}
.switch input[type="checkbox"]:disabled + .switch__slider {
  opacity: 0.55;
  cursor: progress;
}
.switch__state {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-dark);
  font-variant-numeric: tabular-nums;
  min-width: 3ch;
}

@media (max-width: 640px) {
  .admin-test-data__public-gate {
    flex-direction: column;
    align-items: stretch;
  }
  .admin-test-data__public-gate .switch {
    align-self: flex-end;
  }
}

/* M11.X-MINE-HIGHLIGHT (ADR-152) — mine-highlight toggle nad events-panel
   tabs. Visible jen pro logged-in users (`hidden` attribute managed v JS).
   Container má jemný spacing pod hero/preview row a nad tablist. */
/* M11.X-MINE-HIGHLIGHT (ADR-152 iter #2) — toggle now lives INSIDE the
   Pins pane (sticky top), not above the events-panel tab strip. Petřin
   pokyn 2026-05-22: "má to být v pinech, nahoře, sticky nebo na boku".
   Sticky top:0 v rámci scrolling pin list pane = toggle zůstává
   viditelný i při scrollování dolů. Backdrop-blur + cream-surface bg
   so the rust-amber list tints below scroll smoothly underneath bez
   "twinkle" efektu (= solid bg would create hard edge as items scroll
   under, blur softens it). */
/* M12 Noir PR4 — toggle relocated to the panel top bar (events-panel__
   topbar). It's now an inline control on the left of that bar, no longer
   a sticky blurred strip inside the Moments pane. */
.mine-highlight-toggle {
  /* M12 Noir PR4 review (m12-list-items.md §0) — wrap the switch + label
     in a compact pill capsule so it reads as one discrete control in the
     header row (was a loose floating switch+text). */
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  padding: 0.4rem 0.7rem 0.4rem 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--hairline-dark-lo);
  border: 1px solid var(--border-on-light);
}
.mine-highlight-toggle[hidden] {
  display: none;
}
.mine-highlight-toggle__text {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-dark);
  letter-spacing: 0.01em;
}
/* Rust-amber checked state — overrides .switch default --danger červené
   na rust-amber accent (= mine-highlight semantic, iter #2). */
.switch--mine input[type="checkbox"]:checked + .switch__slider {
  background: var(--accent-mine);
}
.switch--mine input[type="checkbox"]:focus-visible + .switch__slider {
  outline-color: var(--accent-mine-bright);
}

/* Pin popup uniform preview list (M10B Q5 + Petřin feedback 2026-05-12).
   All pins (parent + children) render as identical rows: icon + label
   + datetime. Grid columns keep icon and time fixed-width, the label
   ellipsis-truncates when too long. */
.pin-popup {
  /* Petřin pokyn 2026-05-12 (round 3) — "proč se bojíme to mít na
     desktopu širší?" Desktop has plenty of room; the previous 320-380
     range left a 280-char comment looking cramped at 10+ wrapped
     lines. Bumping the floor to 380 and the ceiling to 520 gives the
     text column ~380-420px on desktop. Mobile still clamps to the
     viewport via the @media below. */
  min-width: 380px;
  max-width: min(520px, calc(100vw - 32px));
  /* M12 Noir PR5 ③ — anchor the header accent wash (::before below). */
  position: relative;
}

/* ============================================================
 * M12 Noir PR5 ③ — gathering / moment popup (mockup 19 + m12-popups.md §2
 * + m12-component-redesigns.md §1). Restyles the EXISTING .pin-popup__*
 * structure (no class rename — A2 deferred) into the 3-tier Noir hierarchy:
 *   L1 gathering name = Cormorant italic moon (see .pin-popup__kotva-name)
 *   L2 @nick          = magenta if mine / iris if other (below)
 *   L3 location       = caps muted (see .pin-popup__place)
 * + teal note-glyph moment rows + accent wash + tone avatar ring.
 * `--wash-rgb` / author tone switch on the .pin-popup--mine|--other root.
 * ============================================================ */
.pin-popup {
  --wash-rgb: var(--accent-violet-rgb);   /* other (default) = iris */
}
.pin-popup--mine {
  --wash-rgb: var(--accent-mine-rgb);     /* own gathering = magenta */
}
/* Soft accent wash behind the header — bleeds to the rounded wrapper edges
   (negative insets cancel the .leaflet-popup-content 0.85rem/1rem margin) and
   is clipped by the wrapper's overflow:hidden. Concentrated top-left like the
   mockup; gentle so the dark card stays the surface, not the wash. */
.pin-popup::before {
  content: "";
  position: absolute;
  inset: -0.85rem -1rem auto -1rem;
  height: 96px;
  background: radial-gradient(ellipse 80% 100% at 18% 0%,
    rgba(var(--wash-rgb), 0.13) 0%, transparent 70%);
  pointer-events: none;
  z-index: 0;
}
/* Lift the real content above the wash. */
.pin-popup__header,
.pin-popup__preview,
.pin-popup__actions {
  position: relative;
  z-index: 1;
}

/* Avatar — 40px disc. ADR-192 (Petřin pokyn 2026-05-28): ownership
   author-tone ring removed across both popup variants — the popup
   already carries the ownership signal via the magenta/iris @nick
   colour + the magenta/iris wash, and a third hard ring stacked on the
   avatar fought with the per-avatar glow. The shared Avatar unification
   rule provides the glow on both variants now. */
.pin-popup--mine .pin-popup__avatar,
.pin-popup--other .pin-popup__avatar {
  width: 40px;
  height: 40px;
}

/* L2 — @nick author line. Manrope (body) 12px, author tone via the wrapper so
   the ✦ sparkle marker (::before) inherits the same colour. Supersedes the
   Frost "nick = Cormorant violet" treatment for the gathering subtitle. */
.pin-popup__by-nick {
  /* inline-flex so the ✦ ::before sits on the SAME line as the nick (the base
     .pin-popup__nick is display:block, which would otherwise push ✦ to its
     own line). */
  display: inline-flex;
  align-items: center;
  color: var(--nick-color);            /* other (default) */
  opacity: 1;                             /* override the legacy 0.75 dim — L2 is full-tone */
}
.pin-popup--mine .pin-popup__by-nick {
  color: var(--accent-mine);              /* magenta = "your" attribution */
}
.pin-popup__by-nick::before {
  content: "✦";
  margin-right: 0.3rem;
  font-size: 0.65rem;
  vertical-align: 1px;
}
.pin-popup__by-nick .pin-popup__nick {
  font-family: var(--font-body);
  font-weight: 700;
  /* M13.X-GATHERING-POLISH (Petřin pokyn 2026-06-01, re-measured) — the FOUNDER
     nick reads as the prominent identity line through its MAGENTA tone (--mine) +
     the ✦ founder mark + the @, NOT through a big size. The mockup founder nick is
     ~12.5px, same scale as the child @nicks; prominence is colour, not size. */
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  line-height: 1.25;
  color: inherit;
}
/* Standalone moment (nick is the heading, no gathering name) — keep Cormorant
   but flip to magenta when it's mine so ownership reads at a glance. */
.pin-popup--mine .pin-popup__nick {
  color: var(--accent-mine);
}

/* M12.X (Petřin pokyn 2026-05-30) — "ECHOES (N)" count above the scrollable
   moment list. Sits OUTSIDE .pin-popup__preview so it stays pinned while the
   list scrolls. Reuses the participants-label micro-label idiom (uppercase,
   muted, body font) so the popup reads as one system. */
.pin-popup__echoes-label {
  /* M12.X (Petřin pokyn 2026-05-31) — CENTERED labeled divider. The LINE is the
     main element (flanks both sides via ::before + ::after, same strength as the
     footer divider = `--border-on-light`); the "Echo (N)" text is a TINY secondary
     label riding in the MIDDLE. NO opacity on the element (it would also dim the
     lines) — the text is weakened via the muted colour + small size only. */
  /* STICKY (Petřin pokyn 2026-05-31) — the label heads the scrolling echo list
     (first child of .pin-popup__preview), so it pins to the top while the echoes
     scroll under it. Opaque popup-surface bg + small vertical padding so a row
     scrolling under doesn't bleed through the divider. */
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--cream-surface);
  padding-block: 4px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: 0.6rem;
  font-weight: var(--fw-regular);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.pin-popup__echoes-label::before,
.pin-popup__echoes-label::after {
  /* the divider rule — full footer-strength on BOTH sides of the centred label */
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-on-light);
}
/* Gathering echoes section (preview starts with the ECHOES label) — drop the
   preview's own border-top (the centred label already IS the divider) and give
   the root body room to breathe ABOVE the divider (Petřin pokyn 2026-05-31 — the
   root message was too tight to the divider). */
.pin-popup__preview:has(> .pin-popup__echoes-label) {
  margin-top: var(--space-3);
  border-top: 0;
  padding-top: 0;
}
.pin-popup__echoes-label + .pin-popup__preview {
  /* the label already provides the top gap; drop the preview's own border-top
     divider so we don't get a double rule under the count. */
  margin-top: var(--space-1);
  border-top: 0;
  padding-top: 0;
}
/* M12.X (Petřin pokyn 2026-05-30, round 3) — the ECHOES label now lives INSIDE
   the preview slot (first child, heading the OTHER echoes below the root body),
   so its old standalone top margin would double up with the slot's padding-top.
   Zero it here; the slot's border-top + padding already separate it from the
   root body above. */
.pin-popup__preview > .pin-popup__echoes-label:first-child {
  margin-top: 0;
}

/* M12.X (Petřin pokyn 2026-05-30, round 3) — the ROOT echo's comment is the
   gathering BODY: it renders exactly like a normal Echo's comment (moment voice
   via the inner .pin-popup__preview-text), sitting between the header and the
   ECHOES section. Lifted above the accent wash. */
.pin-popup__root-text {
  position: relative;
  z-index: 1;
  margin-top: var(--space-2);
  /* M12.X (Petřin pokyn 2026-05-31) — flex column so the root DATE sits right
     below the comment; the .pin-popup__founding-meta inside right-aligns the date.
     M13.X-GATHERING-POLISH (ADR-213) — the founding echo now lives in its OWN
     contained card (subtle light-ink wash + hairline + md radius) so it reads as
     the start of the gathering's story, separated from the ECHOES list below
     (handoff design_handoff_gathering_echoes, mockup-v1-desktop.png). */
  display: flex;
  flex-direction: column;
  /* 4px (was 6px) — tighten eyebrow→comment→meta rhythm (Petřin pokyn 2026-06-01,
     measured: eyebrow sat too far from the comment). */
  gap: 4px;
  background: rgba(var(--text-light-rgb), 0.03);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  padding: 11px 14px 12px;
}
.pin-popup__root-text:empty {
  display: none;
}
/* Magenta "✦ FOUNDING ECHO" eyebrow — canonical eyebrow type token (700/11px
   Manrope) + the ✦ founder mark via ::before; magenta = the gathering/echo world
   (same accent as the header founder line + the "Leave another Echo" CTA). */
.pin-popup__founding-eyebrow {
  display: flex;
  align-items: center;
  gap: 5px;
  /* Micro caps label — 9px, matching the sibling ECHOES divider micro-label
     (.pin-popup__echoes-label = 0.6rem). Petřin pokyn 2026-06-01: the FOUNDING
     ECHO label is a SMALL eyebrow; the founder nick above is the prominent line. */
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.6rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent-mine);
}
/* M17.X-FOUNDING-OWNERSHIP (Petřin pokyn 2026-06-09) — magenta (--accent-mine)
   reads as "mine"; a foreign founder's eyebrow takes the foreign-nick violet
   (--nick-color) so the colour alone says whose gathering this is. */
.pin-popup__founding-eyebrow--other {
  color: var(--nick-color);
}
.pin-popup__founding-eyebrow::before {
  content: "\2726"; /* ✦ */
  font-size: 1.05em;
  line-height: 1;
}
/* M18.X-ECHO-DELETE-RULES — tombstoned founding echo (approved copy, Petra
   2026-06-10): the author deleted their own founding message but the
   gathering + replies live on. NO eyebrow, NO header author row — the card
   collapses to a quiet italic "Original echo was deleted" + the kept date.
   Muted-but-readable via the dedicated token (never --text-muted here).
   Identity removed, structure kept — the founder slot is never re-crowned. */
.pin-popup__tombstone-text {
  font-style: italic;
  color: var(--text-tombstone);
}
/* Scoped EXCLUSIVELY to the orphaned (tombstone) founder card — it holds only
   the one-line notice + date, so trim its padding/rhythm a touch. Never
   applied to any other founder card or scenario (the class is added only when
   the founding echo is tombstoned). */
.pin-popup__root-text--deleted {
  padding: 7px 14px 8px;
  gap: 3px;
}
/* Meta row under the founding body: the LEFT slot is reserved for the per-echo
   LIKE (added in M13.X-ECHO-LIKES — no placeholder now); the date right-aligns
   via margin-left:auto so the like drops in on the left later. */
.pin-popup__founding-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 2px;
}
.pin-popup__founding-meta .pin-popup__preview-when {
  margin-left: auto;
  /* Founding date stays PROMINENT (full-tone --text-muted), one step brighter
     than the quiet child dates below (Petřin pokyn 2026-06-01 + mockup: founding
     date sampled as --text-muted, child date as --text-light-muted). */
  color: var(--text-muted);
  opacity: 1;
}
/* Child echo dates are deliberately QUIET — softer + SMALLER than the founding
   date. M13.X-GATHERING-POLISH (Petřin pokyn 2026-06-01, measured): at the shared
   0.7rem the child date ran ~8px PAST the row kebab; 0.6rem tucks it back under the
   kebab and reads as quiet metadata. Founding date stays 0.7rem (prominent). */
.pin-popup__echoes-label ~ .pin-popup__preview-item .pin-popup__preview-when {
  color: var(--text-light-muted);
  opacity: 1;
  font-size: 0.6rem;
  /* 2026-06-03 evening (Petřin pokyn) — the date owns its full-width grid row,
     so don't glue it to the popup's right edge: tuck its right edge under the
     kebab DOTS above (⋮ glyph 16px in a 24px button, dots end ~11px from the
     row edge), so the date column reads aligned with the row actions. */
  padding-right: 11px;
}

/* ── Per-echo LIKE control (heart + count) ───────────────────────────────────
   M13.X-ECHO-LIKES — the functional like toggle. Rendered on every surface an
   Echo appears (gathering popup/sheet, single-echo popup, mobile detail, side
   feed). Canonical RSVP heart reused at 15px so likes + RSVP read as one heart
   system. Idle = outline + muted count; liked = filled magenta (--accent-magenta)
   + a short scale "pop" on becoming liked. It's a <button> — reset the UA chrome
   so it sits inline exactly like the old placeholder span did. */
.echo-like {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  /* Button reset — sit flush inline, no UA box. */
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  color: var(--text-light-muted);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-xs);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transition: color 0.15s ease;
}
.echo-like svg {
  width: 15px;
  height: 15px;
  transition: transform 0.15s ease;
}
/* Hover affordance — hint the magenta before the click (idle hearts only). */
.echo-like:not(.is-liked):hover {
  color: var(--accent-magenta);
}
.echo-like.is-liked {
  color: var(--accent-magenta);
}
/* The liked heart is the CANONICAL shaded heart (Petřin pokyn 2026-06-01) — the
   same #heart-shade gradient + magenta stroke as the concert popup heart
   (.rsvp__heart / .popup__save / .fm-rsvp__like), so likes + RSVP read as ONE
   heart. #heart-shade is a single shared gradient defined once (index.html,
   width=0 defs), referenced by every heart app-wide — no per-heart cost even in
   a long feed. */
.echo-like.is-liked svg path {
  fill: url(#heart-shade);
  stroke: var(--accent-magenta);
}
/* Liked COUNT digits lift in the lighter magenta (--magentaLift), while the
   heart keeps the canonical --accent-magenta — parity with the event like
   counter (.rsvp__heart / .fm-rsvp__like), so both like counts read alike
   (Petřin pokyn 2026-06-01). */
.echo-like.is-liked .echo-like__count {
  color: var(--magentaLift);
}
/* 0 likes shows NO number (Petřin pokyn 2026-06-12) — JS emits an empty span
   at zero; collapsing it here keeps the flex gap from reserving a stray space
   next to the bare heart. Covers every like-count span app-wide: echo heart,
   event heart in lists/details, event map popup, mobile sheet. */
.echo-like__count:empty,
.rsvp__heart-count:empty,
.popup__save-count:empty,
.fm-rsvp__like-count:empty {
  display: none;
}
.echo-like:focus-visible {
  outline: 2px solid var(--accent-magenta);
  outline-offset: 2px;
  border-radius: 4px;
}
/* ~220ms scale pop when a like lands (cubic easing overshoots, then settles). */
@keyframes echo-like-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.3); }
  100% { transform: scale(1); }
}
.echo-like--pop svg {
  animation: echo-like-pop 220ms cubic-bezier(0.2, 0.9, 0.3, 1.5);
}
@media (prefers-reduced-motion: reduce) {
  .echo-like--pop svg { animation: none; }
}

/* ── Echo translate glyph (文A) ──────────────────────────────────────────────
   M19.X-TRANSLATE (ADR-306) — the on-demand translate control. Same plastic
   family as .echo-like: a ghost icon button that sits inline in the heart+date
   meta row, idle muted, no UA box. Translate is a neutral utility action (not a
   like) so its accent is the app teal (--accent-turquoise), distinct from the
   magenta heart. The glyph is the X/Twitter "文A" convention, sized to read as a
   ~15px icon box next to the 15px heart. */
.echo-translate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Quiet inline affordance, NOT an app button (Petřin pokyn 2026-06-12) —
     muted + extra-dimmed so it recedes next to the heart; the lighter weight
     and smaller glyph below keep it from reading as a prominent control. */
  color: var(--text-light-muted);
  opacity: 0.8;
  /* A little extra breathing room from the heart so the two don't read as a
     tight pair and readers don't mis-tap the heart (Petřin pokyn 2026-06-12). */
  margin-left: 4px;
  transition: color 0.15s ease, opacity 0.15s ease;
}
.echo-translate__glyph {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-2xs);
  line-height: 1;
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 13px;
}
.echo-translate:hover {
  opacity: 1;
  color: var(--text-light);
}
/* Active = the translation is currently shown below the text. A gentle teal
   marks the on-state without shouting (still smaller + thinner than a button). */
.echo-translate.is-active {
  opacity: 1;
  color: var(--accent-turquoise);
}
.echo-translate.is-loading {
  opacity: 0.55;
  cursor: progress;
}
.echo-translate:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Inline 文A reference inside prose (e.g. the Preferences hint) — bolder + a
   step brighter than the surrounding muted copy so the glyph reads as the icon
   it names instead of blending in (Petřin pokyn 2026-06-12). */
.echo-glyph-ref {
  font-weight: 600;
  font-style: normal;
  color: var(--text-light);
}

/* ── Echo translation block (shown below the original text) ───────────────────
   The original Echo text never disappears; the machine translation appears
   right beneath it, set apart by a quiet teal hairline rule on the left so the
   reader sees it as a derived companion, not the author's own words. */
.echo-translation {
  margin-top: var(--space-2);
  padding-left: 0.625rem;
  /* Muted AI-note rule (Petřin pokyn 2026-06-12) — NOT teal: the song-name
     eyebrow is teal (--song-name), so a teal rule here clashed with it. The
     AI-note whisper tone marks this as the (AI) translation companion while
     staying quiet and visible. */
  border-left: 2px solid var(--text-ai-note);
}
.echo-translation__text {
  color: var(--text-light);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  line-height: 1.45;
  white-space: pre-wrap;
}
.echo-translation__foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.25rem;
}
.echo-translation__badge {
  color: var(--text-light-muted);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-style: italic;
}
.echo-translation__toggle {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: var(--accent-turquoise);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 600;
  -webkit-tap-highlight-color: transparent;
}
.echo-translation__toggle:hover {
  text-decoration: underline;
}
.echo-translation__toggle:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  border-radius: 4px;
}
/* Loading / soft-miss / rate-limit inline note — quieter, no rule. */
.echo-translation--status,
.echo-translation--limit {
  border-left-color: transparent;
  padding-left: 0;
}
.echo-translation__status {
  color: var(--text-light-muted);
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-style: italic;
}
.echo-translation--limit .echo-translation__status {
  /* Rate-limit = a system warning → the canonical gold/copper warn tone
     (ui-standards HARD RULE 12), brightened for legibility on the dark feed. */
  color: var(--accent-copper-bright);
}

/* ── List GOING control (people glyph + count) ───────────────────────────────
   M13.X-EVENT-GOING-LIST (Petřin pokyn 2026-06-03) — the attendance twin of the
   list like heart, sitting right beside it on Concerts/Online/Yours rows. Reads
   as the SAME plastic family as .echo-like: identical 15px glyph box, muted
   idle, and a plastic gradient FILL when active (Petřin pokyn — stejný plastický
   efekt jako srdce). TEAL on both concert AND online rows — going is a CONFIRM
   action (HARD RULE 12, Petřin pokyn 2026-06-03), NEVER magenta (magenta =
   like/ownership). Idle = hollow outline; active fills #going-shade-teal. */
.feed-list__indicators {
  display: inline-flex;
  align-items: center;
  gap: 10px;            /* like heart ↔ going counter breathing room */
  flex-shrink: 0;
}
.event-going {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  /* Button reset — sit flush inline, no UA box (parity with .echo-like). */
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  color: var(--text-light-muted);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-xs);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transition: color 0.15s ease;
}
.event-going svg {
  /* 18px box, NOT the heart's 15px — the people path sits with more padding
     inside its 24-unit viewBox than the heart does (~70% vs ~85% coverage), so
     an equal box renders the people optically smaller (Petřin nález 2026-06-03).
     18px puts the DRAWN people at the heart's drawn size (~13×12.8px); the
     stroke-width (1.7 in rsvp.js) is scaled down in step so line weight stays
     matched to the heart's. */
  width: 18px;
  height: 18px;
  transition: transform 0.15s ease;
}
/* Idle = hollow OUTLINE (stroke set inline on the path = currentColor, so it
   follows the muted colour), exactly like the like heart's idle outline — the
   two read as a matched pair. */
/* Hover affordance — hint the role colour before the click (idle only). */
.event-going:not(.is-going):hover {
  color: var(--accent-turquoise);
}
.event-going.is-going {
  color: var(--accent-turquoise);
}
/* Active = "I'm going": the hollow people outline takes the plastic teal
   gradient FILL + a teal stroke — the exact depth treatment of the liked heart
   (#heart-shade + accent stroke). Idle outline → plastic fill, one family
   across like + going (Petřin pokyn 2026-06-03 — dutý obrys + plastický efekt). */
.event-going.is-going svg path {
  fill: url(#going-shade-teal);
  stroke: var(--accent-turquoise);
}
/* Going COUNT digits lift to the bright teal when active (parity with the
   liked heart's --magentaLift count), while idle stays muted. */
.event-going.is-going .event-going__count {
  color: var(--accent-turquoise-bright);
}
.event-going:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  border-radius: 4px;
}
/* NOTE: no azure twin for online rows — going is a CONFIRM action, teal on
   both worlds (Petřin pokyn 2026-06-03, HARD RULE 12; ui-standards
   "ATTENDANCE CONFIRM = TEAL VŠUDE"). */
/* Reuse the like's scale pop on becoming active. */
.event-going--pop svg {
  animation: echo-like-pop 220ms cubic-bezier(0.2, 0.9, 0.3, 1.5);
}
@media (prefers-reduced-motion: reduce) {
  .event-going--pop svg { animation: none; }
}

/* M13.X-PEBBLES (ADR-247 R7) — discussion-links count chip ("💬 N") on
   Concerts/Online list rows, third in the heart + going indicator group.
   Pure INDICATOR, not a control: stays muted (no hover/active state, no
   cursor), the row click opens the detail where the links live. Typography
   + 18px glyph box mirror .event-going so the trio reads as one family. */
.event-links {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  color: var(--text-light-muted);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fs-xs);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.event-links svg {
  width: 18px;
  height: 18px;
}
/* The chip renders ONLY when the event HAS links (N>0) -> always the plastic
   filled state, mirroring the liked heart / going / rating star family
   (fill = vertical shade gradient + crisp edge stroke; Petrin pokyn
   2026-06-04 „plna ikona s efektem stinu jako srdce a hvezda“).
   MIST, not violet — tři syté ikony vedle sebe četly jako omalovánka
   (Petřin pokyn 2026-06-04 „dej to fakt hodně světlé, třeba mist"). */
.event-links svg path {
  fill: url(#bubble-shade-mist);
  stroke: var(--text-light-muted);
}
.event-links .event-links__count {
  color: var(--text-muted);
}

/* M13.X-GATHERING-POLISH (ADR-213) — the gathering's More/Less toggle uses the
   LABEL token (soft teal --label-color), NOT the
   default violet (handoff fix). Scoped to gathering surfaces ONLY — the founding
   card + the echo rows that follow the ECHOES divider — so standalone pins + the
   side feed keep the default violet moment-tone toggle. */
.pin-popup__root-text .text-expandable__toggle,
.pin-popup__echoes-label ~ .pin-popup__preview-item .text-expandable__toggle {
  color: var(--label-color);
  text-decoration-color: color-mix(in srgb, var(--label-color) 50%, transparent);
  /* Slightly less top gap than the global 0.35rem (Petřin pokyn 2026-06-01 — More
     sat too far from the comment relative to the like row below). */
  margin-top: 0.25rem;
}
.pin-popup__root-text .text-expandable__toggle:hover,
.pin-popup__root-text .text-expandable__toggle:focus-visible,
.pin-popup__echoes-label ~ .pin-popup__preview-item .text-expandable__toggle:hover,
.pin-popup__echoes-label ~ .pin-popup__preview-item .text-expandable__toggle:focus-visible {
  color: var(--label-color);
  text-decoration-color: currentColor;
}

/* M17 [UX] (Petřin pokyn 2026-06-08) — the side-feed Echo More/Less toggle uses
   the LABEL token (soft teal --label-color), matching the gathering popup +
   modal field labels, instead of the default violet. Covers desktop AND mobile:
   the Echoes feed (.feed-list__pin-text) is one shared DOM with no mobile-only
   toggle override, so this single rule reaches both. */
.feed-list__pin-text .text-expandable__toggle {
  color: var(--label-color);
  text-decoration-color: color-mix(in srgb, var(--label-color) 50%, transparent);
}
.feed-list__pin-text .text-expandable__toggle:hover,
.feed-list__pin-text .text-expandable__toggle:focus-visible {
  color: var(--label-color);
  text-decoration-color: currentColor;
}
/* When a gathering has ONLY the founding echo, the OTHER-echoes list is empty —
   hide it so its divider + padding leave no empty hole under the body (Petřin
   pokyn: layout must work with AND without further echoes / the Open chip). */
.pin-popup__preview:empty {
  display: none;
}

.pin-popup__preview {
  /* Container for the per-row preview items. The separator BETWEEN
     rows lives on each item via border-top (see below) — using gap
     alone made the 3 rows in a kotva blur into one block, which read
     as "one long noisy comment" (Petřin pokyn 2026-05-12 round 4). */
  margin-top: 10px;
  border-top: 1px solid var(--cream-sunken);
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  /* M11.X-ANCHOR-AUDIT Stage E iter 2 (2026-05-22) — scrollable when
     parent + children exceed ~5 visible rows (~360px). native overflow
     = žádné innerHTML swap při skrolu = popup stays positionally
     stable (= Leaflet ResponsivePopup ne re-computes). scrollbar-gutter
     prevents per-row trash icon jumping když scrollbar appears. */
  /* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — taller echo list so more
     echoes fit before scrolling, but RESPONSIVE: capped at 50vh so on a short
     screen the popup still fits the viewport (Leaflet auto-pan) instead of growing
     past it (the 240px cap that earlier clipped is replaced by a vh-aware min). */
  max-height: min(440px, 50vh);
  overflow-y: auto;
  /* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — `scrollbar-gutter: stable`
     reserved a ~15px right gutter even when the list does NOT scroll, which inset
     the whole ECHOES section (divider + rows + dates) ~15px from the root/header
     right edge → read as "not lined up". Dropped so a non-scrolling list reaches
     the same right edge as the root comment; the gutter now appears only when the
     list actually overflows (the common few-echoes case stays flush). */
  /* M12.X-GATHERING-POPUP carry-over — overflow-y: auto forces overflow-x
     to compute to `auto` too (CSS spec: visible + non-visible axis →
     visible becomes auto), so the leftmost row's avatar drop-shadow glow
     (--av-glow, ~7px outside the orb box, applied at line ~5030) was
     clipped against the container's left content edge. The earlier fix was
     padding-inline alone, which gave too little room (the glow still kissed
     the edge) AND indented the rows away from the header avatar. M12.X
     (Petřin pokyn 2026-05-30) — pair the inner padding with an equal negative
     margin so the scrollable area widens to a full space-3 gutter on each side
     (glow clears the clip) while the row content stays aligned with the header.
     The list now also spans the full popup width instead of looking inset. */
  padding-inline: var(--space-3);
  margin-inline: calc(-1 * var(--space-3));
  /* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — HIDE the scrollbar lane so a
     long echo list scrolls like the side feed WITHOUT a visible bar AND without the
     bar eating ~15px of width (which shifted the echo rows + their dates left of the
     root date = the alignment kept "breaking" once 5+ echoes overflowed). Content
     still scrolls via wheel / trackpad / touch. */
  scrollbar-width: none;            /* Firefox */
  /* Petřin nález 2026-06-06 — with the scrollbar lane hidden, an overflowing
     list clipped the last echo mid-card right above the magenta CTA, reading
     as "echoes slide under the pill". Bottom edge fade = the canonical
     more-to-scroll hint (.hub-feed idiom; mask = alpha-only). The fade depth
     equals the new bottom padding, so a NON-overflowing list fades only its
     padding, never content. No top fade — the sticky ECHOES label (opaque bg)
     already covers rows scrolling under it, and a mask would dim the label. */
  padding-bottom: 14px;
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 1) 0, rgba(0, 0, 0, 1) calc(100% - 14px), transparent 100%);
          mask-image: linear-gradient(180deg, rgba(0, 0, 0, 1) 0, rgba(0, 0, 0, 1) calc(100% - 14px), transparent 100%);
}
.pin-popup__preview::-webkit-scrollbar { width: 0; height: 0; display: none; }
.pin-popup__preview-loading {
  font-size: 0.8rem;
  opacity: 0.6;
  font-style: italic;
}
.pin-popup__preview-item {
  /* M12.X-NO-MOMENT-KIND (Petřin pokyn 2026-05-29) — moment rows are
     text-first, NO leading note glyph (the teal tile kept getting re-added
     from mockup-19 fidelity passes; it is intentionally gone). Default grid =
     2 columns: text body | per-row actions. Tight line-height so each pin
     stays ~2 visual lines (text + meta). */
  display: grid;
  /* M12.X-GATHERING-POPUP (Petřin pokyn 2026-05-31) — the date (`when`) is its
     OWN full-width second row so its right edge lines up with the root comment's
     date. Row 1 = body | per-row actions; row 2 = the date spanning both columns,
     right-aligned. The kebab stays in its column (always visible), the date is no
     longer cut short by it. */
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "body actions"
    "when when";
  column-gap: 8px;
  row-gap: 3px;
  align-items: start;
  font-size: 0.9rem;
  line-height: 1.25;
  /* Compact rows (Petřin pokyn 2026-05-28 — the popup grew too tall and clipped
     against the viewport edge / stopped auto-panning the map). M12.X (Petřin
     pokyn 2026-05-30) — a touch more vertical room so the rows don't read as
     cramped now that each carries an avatar + @nick + flag + comment + date. */
  padding: 7px 0;
}
/* M12.X-GATHERING-CUE (Petřin pokyn 2026-05-31) — highlight-on-open. When a
   gathering is opened by clicking ONE of its echoes in the feed, that echo
   (a preview row, or the root body if it's the founder's echo) briefly flashes
   so the user can find it among the others instead of hunting. Neutral light
   wash + soft inset ring fading out — NOT magenta (magenta = "mine"; this is a
   "here's the one you clicked" cue, not an identity cue). Shared utility so the
   mobile gathering sheet (.fm-moment-row) can reuse the exact same flash. */
@keyframes fm-echo-flash {
  0% {
    background: rgba(var(--text-light-rgb), 0.16);
    box-shadow: inset 0 0 0 1px rgba(var(--text-light-rgb), 0.34);
  }
  100% {
    background: transparent;
    box-shadow: inset 0 0 0 1px transparent;
  }
}
.fm-echo-flash {
  border-radius: var(--radius-sm);
  animation: fm-echo-flash 1.6s ease-out forwards;
}
@media (prefers-reduced-motion: reduce) {
  .fm-echo-flash {
    animation: none;
  }
}
.pin-popup__preview-body {
  grid-area: body;
}
.pin-popup__preview-item > .pin-popup__row-actions {
  grid-area: actions;
}
.pin-popup__preview-item > .pin-popup__preview-meta {
  /* Meta row under the body+actions — like control (left) + date (right). */
  grid-area: when;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.pin-popup__preview-item > .pin-popup__preview-meta > .pin-popup__preview-when {
  margin-left: auto;
}
/* Open-gathering rows lead with the author avatar (identity cue) → 3 columns:
   avatar | text body | per-row actions. The date row leaves the avatar gutter
   empty and spans body+actions, right-aligned to the same edge as the root date. */
.pin-popup__preview-item--lead {
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "avatar body actions"
    ".      when when";
}
.pin-popup__preview-item--lead > .pin-popup__preview-avatar {
  grid-area: avatar;
}
.pin-popup__preview-item + .pin-popup__preview-item {
  border-top: 1px dashed rgba(var(--accent-turquoise-rgb), 0.18);
}

/* M11J — draggable row: 2-column dot drag-handle grip (Material
   Design pattern) on the RIGHT edge of the row, spanning the full
   row height (Petřin pokyn 2026-05-22 15:52 — grip on the far side
   away from the violet pin marker; full height so it's reachable
   from the time line too). Muted slate so the grip reads as chrome
   / affordance, not a brand accent. Pattern tiles vertically
   (background-repeat: repeat-y) to scale with any row height. */
.pin-popup__preview-item--draggable {
  position: relative;
  cursor: grab;
  /* Grip moved back to the LEFT edge after Petřin mobile smoke
     2026-05-22 16:55 — the right edge sits next to the trash icon
     and an accidental tap during a drag attempt risked deleting the
     pin. Left edge is the safe zone — the grip sits in this 16px gutter,
     clear of the row text (the old category-note tile that used to share
     this edge was removed per ADR-181). */
  padding-left: 16px;
  /* M11J mobile fix — without touch-action: none the browser treats
     touchmove on the row as a scroll attempt and swallows the
     pointermove events before our handler sees them, so the row
     never lifts off. `none` hands all touch gestures over to our JS
     pointer handlers. */
  touch-action: none;
}
.pin-popup__preview-item--draggable::after {
  content: "";
  position: absolute;
  left: 2px;
  /* Full row height with 8 px margin to the dashed separators above
     and below (Petřin pokyn 2026-05-22 16:17 — keep the long dot
     column, just give the top dot the same breathing room as the
     bottom dot). background-position: 50% 50% with repeat-y anchors
     one tile at the centre and steps both ways, so the top and bottom
     remainders are symmetric — no matter what the row height is. */
  top: 8px;
  bottom: 8px;
  width: 10px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='7' viewBox='0 0 10 7'><g fill='%23556680'><circle cx='2.5' cy='3.5' r='1.4'/><circle cx='7.5' cy='3.5' r='1.4'/></g></svg>");
  background-repeat: repeat-y;
  background-position: 50% 50%;
  background-size: 10px 7px;
  opacity: 0.55;
  transition: opacity 120ms ease;
  pointer-events: none;
}
.pin-popup__preview-item--draggable:hover::after {
  opacity: 1;
}
.pin-popup__preview-item--draggable:active {
  cursor: grabbing;
}

/* Floating ghost pin during a row unmerge drag — follows the cursor.
   Pointer-events: none so the underlying map / popup keeps receiving
   events (= we still need to hit-test the drop target on pointerup). */
.pin-drag-row-ghost {
  position: fixed;
  z-index: 10001;
  width: 30px;
  height: 44px;
  pointer-events: none;
  transform: translate(-50%, -100%);
  filter: drop-shadow(0 6px 12px rgba(var(--accent-violet-rgb), 0.45));
  opacity: 0.9;
}
.pin-popup__preview-body {
  /* Stack text + when. min-width: 0 lets the text column shrink
     correctly inside the grid. overflow: hidden clips any inline
     content that would have pushed past the body cell — Petřin pokyn
     round 5, the time element was visually leaking past the right
     edge of the popup on some viewports. */
  display: flex;
  flex-direction: column;
  /* M12.X (Petřin pokyn 2026-05-30) — the 0 gap glued the @nick onto the
     comment and the comment onto the date so the three layers blurred into
     one block. A small gap separates the identity / content / metadata tiers. */
  gap: 3px;
  min-width: 0;
  overflow: hidden;
}
.pin-popup__row-actions {
  display: inline-flex;
  gap: 2px;
  flex-shrink: 0;
  /* M17.X-KEBAB-NAME-LEVEL (Petřin pokyn 2026-06-09) — kebab at the @nick line,
     not centred on the row body. Re-flips the 2026-06-03 centring back to the
     2026-06-01 top-align: Petra wants the dots at the NAME level on every
     surface (feed + popup + mobile). On a tall (song-tagged) row the dots stay
     up by the @nick rather than floating mid-row. */
  align-self: start;
}

/* M11.X-CONTAINER-ENTITY (Petřin pokyn 2026-05-24) — per-child "⋯" overflow
   menu trigger + the Frost popover it opens. Replaces the crowded inline
   icon row inside a container with a single kebab + a design-system menu. */
.pin-popup__row-menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  padding: 2px 4px;
  border: 0;
  background: transparent;
  /* M12 gathering cleanup (Petřin pokyn 2026-05-28) — neutral light kebab to
     match every other ⋯/icon affordance (tabs, lists); the violet tint read as
     an accent that didn't belong on a chrome control. */
  color: var(--text-muted);
  border-radius: 4px;
  line-height: 1;
  cursor: pointer;
  transition: color 150ms ease, background 150ms ease;
}
.pin-popup__row-menu-btn svg {
  display: block;
}
.pin-popup__row-menu-btn:hover,
.pin-popup__row-menu-btn[aria-expanded="true"] {
  background: var(--cream-sunken);
  color: var(--text-dark);
}
.pin-popup__row-menu-btn:focus-visible {
  outline: 2px solid var(--accent-violet-soft);
  outline-offset: 1px;
}
/* M12D.X-MODERATION-UNIFY (U4) — reported cue on a gathering-row kebab. Per
   Petřin pokyn 2026-05-31 JUST the dots go danger-red — NO box/ring/fill around
   the kebab ("nechci čtvereček kolem kebabu"). The flag is the highlighted
   "Moderate" row inside the menu (.pin-row-menu__item--reported). */
.pin-popup__row-menu-btn.is-reported,
.pin-popup__row-menu-btn.is-reported:hover,
.pin-popup__row-menu-btn.is-reported[aria-expanded="true"] {
  color: var(--danger-text);
}

/* The popover itself mounts on <body> (fixed-positioned in JS) so the
   popup's overflow:auto preview can't clip it. */
.pin-row-menu {
  z-index: 1200;
  min-width: 172px;
  padding: 6px;
  background: var(--cream-surface);
  border: 1px solid rgba(238, 240, 250, 0.12);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-dropdown);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.pin-row-menu__item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  border: 0;
  background: transparent;
  /* M12 Noir — light text on the dark menu surface (was --navy-deep, a
     near-black Frost leftover that was invisible on Noir). */
  color: var(--text-dark);
  font: inherit;
  font-size: 0.9rem;
  text-align: left;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 150ms ease;
}
.pin-row-menu__item:hover,
.pin-row-menu__item:focus-visible {
  background: rgba(238, 240, 250, 0.06);
  outline: none;
}
.pin-row-menu__icon {
  display: inline-flex;
  align-items: center;
  /* M15 unify (Petřin pokyn 2026-06-05) — no own hue; inherits the item
     colour exactly like mobile .fm-action-sheet__icon. The hue comes from
     the tone modifiers below (was a flat --accent-violet-soft on every
     icon, which had drifted from the mobile tone-coded canon). */
  flex-shrink: 0;
}
/* Tone-coded icons — 1:1 mirror of mobile.css .fm-action-sheet__item--<tone>
   so the desktop menu and the mobile sheet read as one component. Renderers
   default to teal (same fallback as mobileActionSheet toItems). */
.pin-row-menu__item--teal .pin-row-menu__icon { color: var(--accent-turquoise); }
.pin-row-menu__item--gold .pin-row-menu__icon { color: var(--accent-copper); }
.pin-row-menu__item--magenta .pin-row-menu__icon { color: var(--accent-mine); }
/* Test-data flask (Petřin pokyn 2026-05-24) — gold glyph when the flag is
   ON, grey when OFF. M15 unify: icon-only; the LABEL stays --text-dark like
   every other row (the old whole-row muted paint read as a disabled item). */
.pin-row-menu__item--test-on .pin-row-menu__icon {
  color: var(--test-data);
}
.pin-row-menu__item--test-off .pin-row-menu__icon {
  color: var(--text-muted);
}
.pin-row-menu__item--danger {
  color: var(--danger-text);
}
.pin-row-menu__item--danger .pin-row-menu__icon {
  color: var(--danger-text);
}
.pin-row-menu__item--danger:hover,
.pin-row-menu__item--danger:focus-visible {
  background: rgba(var(--danger-rgb), 0.1);
}
/* M12D.X-MODERATION-UNIFY (Petřin pokyn 2026-05-31) — the "Moderate pending
   reports" row is the HIGHLIGHTED option inside a reported kebab's menu: danger
   flag + label on a soft danger wash, so the flag lives here (not as a sticker
   on the kebab). Desktop mirror of the mobile .fm-action-sheet__item--reported. */
.pin-row-menu__item--reported,
.pin-row-menu__item--reported .pin-row-menu__icon {
  color: var(--danger-text);
}
.pin-row-menu__item--reported {
  background: rgba(var(--danger-rgb), 0.08);
}
.pin-row-menu__item--reported:hover,
.pin-row-menu__item--reported:focus-visible {
  background: rgba(var(--danger-rgb), 0.16);
}
.pin-popup__row-edit,
.pin-popup__row-delete,
.pin-popup__row-report {
  background: transparent;
  border: 0;
  font-size: 1rem;
  cursor: pointer;
  padding: 2px 4px;
  border-radius: 4px;
  line-height: 1;
  color: var(--text-muted);
  /* Force visible box for emoji content — some browsers collapse
     transparent <button> elements to 0 width when content is a single
     emoji glyph, leaving the button rendered but visually invisible.
     min-width + min-height guarantees a 24×24 tap target regardless.
     M11.X-ANCHOR-AUDIT (2026-05-22) — SVG icons replaced the emoji
     glyphs; min-width/height still useful as a tap-target floor. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  transition: color 150ms ease, background 150ms ease;
}
.pin-popup__row-edit svg,
.pin-popup__row-delete svg,
.pin-popup__row-report svg {
  display: block;
}
/* M11.X-ANCHOR-AUDIT (2026-05-22) — per-row delete inherits danger-red
   on idle (= destructive intent visible). Report does NOT — per Petřin
   pokyn 2026-05-22 evening, report ikona NENÍ destructive z user pohledu
   (= klik = "flag this", ne "destroy"). Report stays muted same as edit;
   red wash appears on hover for subtle destructive signal. */
.pin-popup__row-delete {
  color: var(--danger-text);
}
.pin-popup__row-edit:hover,
.pin-popup__row-report:hover {
  background: var(--cream-sunken);
}
.pin-popup__row-report:hover {
  color: var(--danger-text);
}
/* UX-AUDIT-006 (M10 closure audit 2026-05-15): destructive row trash
   shares the --danger red mood per `feedback_destructive_ctas_uniform_red`.
   Emoji glyph itself stays the system trash icon (no filter recolor — it
   would render differently per platform), but the hover background +
   focus halo lean into the same red tone the `.btn-danger` buttons use.
   Conveys "this is destructive" without forcing the icon to be red. */
.pin-popup__row-delete:hover {
  background: var(--danger-soft, rgba(180, 35, 35, 0.12));
}
.pin-popup__row-delete:focus-visible {
  background: var(--danger-soft, rgba(180, 35, 35, 0.12));
  outline-color: var(--danger) !important;
}

/* M10B Stage 2 (STYLE-009) — Atmospheric focus halo for M10B custom
   buttons that don't already get one from the auth dialog button
   family (which has its own focus-visible rules in
   dialog[id^="auth-"]). Keeps keyboard navigation visible without
   the UA default focus ring breaking the look. */
.pin-popup__row-edit:focus-visible,
.pin-popup__row-delete:focus-visible,
.pin-popup__row-report:focus-visible,
.pin-expand-item__delete:focus-visible {
  background: var(--cream-sunken);
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}
.pin-popup__preview-text {
  /* No truncation — full 500-char comments (post-ADR-080) wrap to
     multiple lines. */
  margin: 0;
  white-space: normal;
  word-break: break-word;
  overflow-wrap: anywhere;
  min-width: 0;
  /* Moment voice — DEFAULT (longer moments) = roman serif; short moments get
     handwriting via --hand. All values from the --*-moment-* tokens. */
  font-family: var(--font-moment-long);
  font-size: var(--fs-moment-roman);
  line-height: var(--lh-moment-roman);
}
.pin-popup__preview-text--hand {
  font-family: var(--font-moment);
  font-size: var(--fs-moment-hand);
  font-weight: var(--fw-moment-hand);
  line-height: var(--lh-moment-hand);
}
/* M11D ADR-080: grandfathered legacy pin with null/empty comment and
   no custom_label. Muted + italic placeholder, same DNA as
   .pin-expand-item__comment--muted. */
.pin-popup__preview-text--muted {
  color: var(--text-muted, #7a7a8a);
  font-style: italic;
  opacity: 0.75;
}

/* Mobile — narrow viewport. Popup adapts so the text column stays
   readable + the row-actions don't push everything off-screen. */
@media (max-width: 480px) {
  .pin-popup {
    min-width: min(290px, calc(100vw - 24px));
    max-width: calc(100vw - 24px);
  }
  .pin-popup__preview-item {
    /* Drop to 2 columns on really narrow screens, with text wrapping
       naturally below the icon/when row when needed. */
    gap: 6px;
  }
}
.pin-popup__preview-when {
  /* M12 rollout — font properties INHERITED z shared base nahoře
     (.pin-popup__meta, .pin-popup__preview-when, .feed-list__meta). Tady
     řeším JEN positioning + color override. Petřin pokyn 2026-05-14:
     lighter gray (match .feed-list__meta + .pin-popup__meta). */
  display: block;
  width: fit-content;
  max-width: 100%;
  margin: 0;
  margin-left: auto;
  align-self: flex-end;
  color: var(--text-muted);
  opacity: 0.75;
  white-space: nowrap;
}

/* Highlight pulse for the marker the user just picked in a chooser
   (Add Pin nearby / Delete kotva move submenu). The class is added
   for ~3 seconds, then auto-removed by JS.

   Note (2026-05-12 fix): we deliberately use ONLY a pseudo-element
   ring outside the marker instead of `filter: drop-shadow` and
   `transform: scale()` on the marker itself. Those properties
   promote a compositing layer and re-rasterise nearby map tiles in
   Chromium/Firefox, leaving the surrounding map looking blurry
   while the animation runs. The pseudo-element ring sits *behind*
   the marker (z-index -1), doesn't touch the map layer, and animates
   without compositing tricks. */
/* Pulse the selected marker — gold + violet expanding ring. Per Petřin
   feedback 2026-05-14 the previous 16→24 px turquoise ring at opacity
   0.55 byl nearly invisible.

   IMPORTANT: animation runs on box-shadow OF THE MARKER ELEMENT itself,
   not on a ::before pseudo-element. The default Leaflet pin marker is
   an <img class="leaflet-marker-icon"> — img is a "replaced element"
   and CANNOT host ::before / ::after pseudo-elements in CSS, so the
   previous pseudo-based pulse silently failed for standalone pins
   (which all use the default Leaflet marker). divIcon kotvy markers
   are divs and DID get the pulse — only kotvy pulsed before.

   box-shadow + border-radius on the element itself works on both
   <img> and <div> markers. We do NOT touch `transform` or `filter`
   because either would override Leaflet's translate3d positioning
   (marker jumps to viewport origin) or promote a compositing layer
   that blurs nearby map tiles. */
@keyframes marker-highlight-ring {
  0% {
    box-shadow:
      0 0 0 0 rgba(255, 196, 64, 0.95),
      0 0 0 0 rgba(var(--accent-violet-rgb), 0.65);
  }
  50% {
    box-shadow:
      0 0 0 22px rgba(255, 196, 64, 0.45),
      0 0 0 40px rgba(var(--accent-violet-rgb), 0.30);
  }
  100% {
    box-shadow:
      0 0 0 55px rgba(255, 196, 64, 0),
      0 0 0 75px rgba(var(--accent-violet-rgb), 0);
  }
}

.marker-highlight {
  z-index: 1000 !important;
  border-radius: 50%;
  animation: marker-highlight-ring 1.5s ease-out 2;
}

/* M11.X-ADMIN-LOCATE — translucent "ghost" marker dropped when the admin
   locates a PENDING POI / concert (no live marker on the public map yet).
   Shows where the real pin will land once approved. Colour per entity via
   currentColor; the SVG carries fill-opacity + dashed stroke for the
   phantom look. NOTE: never animate `transform` on the marker root —
   Leaflet uses it for positioning. The pulse ring (.marker-highlight,
   box-shadow) supplies motion; an extra opacity breathe on the SVG adds
   ethereality without touching transform. */
.ghost-marker--poi {
  color: var(--accent-copper);
}
.ghost-marker--event {
  color: var(--accent-turquoise);
}
.ghost-marker svg {
  display: block;
  animation: ghost-marker-breathe 1.7s ease-in-out infinite;
}
.ghost-marker__emoji {
  position: absolute;
  top: 3px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 13px;
  line-height: 1;
  opacity: 0.55;
  pointer-events: none;
}
@keyframes ghost-marker-breathe {
  0%,
  100% {
    opacity: 0.9;
  }
  50% {
    opacity: 0.45;
  }
}
/* System "ghost" info popup — pending entity awaiting moderation (concert
   + landmark, shared). Compact but ROOMY note card, not a cramped bubble:
   the previous tiny window made the standard 32px close × look oversized and
   let the copy run under it. We keep the canonical × and give the window
   breathing room instead (Petřin pokyn 2026-05-31). ui-standards.md §2. */
.ghost-popup .leaflet-popup-content-wrapper {
  min-width: 240px;
}
.ghost-popup .leaflet-popup-content {
  margin: var(--space-4) var(--space-4) var(--space-3);
}
.ghost-popup__body {
  font-size: var(--fs-sm);
  line-height: 1.4;
  color: var(--text-dark);
  max-width: 240px;
  /* Clear the standard 32px close × (top:12 right:12) so the first line
     never runs under it. */
  padding-right: 26px;
}
/* "← Back to admin" = canonical ghost button (NOT the old underlined
   frosty text-link) — own line below the copy. Scoped under .ghost-popup
   (0,2,0) so the teal label OUT-SPECIFIES Leaflet's base
   `.leaflet-container a` link rule (0,1,1) — without this the text rendered
   Leaflet's default link blue. Teal = readable on the dark surface + the
   app's standard link/admin accent (Petřin pokyn 2026-05-31). */
.ghost-popup .ghost-popup__back {
  display: inline-flex;
  align-items: center;
  margin-top: var(--space-3);
  color: var(--accent-turquoise);
  text-decoration: none;
}
.ghost-popup .ghost-popup__back:hover,
.ghost-popup .ghost-popup__back:focus-visible {
  color: var(--accent-turquoise-bright);
  text-decoration: none;
}

/* Fresh-24h glow ODSTRANĚN 2026-05-14 per Petřin pokyn ("měnila se barva
   a vizuál, to nechci"). Defer to M11 with proper UX iteration. */

/* Frost orb cluster icon — M10D Task #14 redesign 2026-05-14 (po
   Petřině visual review předchozího "decent" stickeru: "vůbec
   nezapadá do stylu, působí rušivě"). Cluster JEN na zoomed-out
   levels (disableClusteringAtZoom: 8). Custom icon = radial gradient
   cream→cyan→navy + backdrop-filter blur + multi-layer turquoise
   glow halo + slow breathe pulse. Frozen / cinematic feeling
   z Frisson album N°6 paintings: deep navy sky, cyan splash, magic
   pulse. MarkerCluster.Default.css záměrně neimportujeme; používáme
   jen MarkerCluster.css (core positioning) + tato vlastní pravidla. */
.cluster-frost {
  background: transparent;
  border: none;
}

.cluster-frost__inner {
  width: var(--cluster-size, 38px);
  height: var(--cluster-size, 38px);
  border-radius: 50%;

  /* Violet body — atmospheric radial gradient, alpha 0.95 (solid enough
     že mapa pod ním neprosvítá). Color zachována per Petřin OK
     2026-05-14 "barva se mi docela líbí". */
  /* M12 Noir PR5 — fully opaque sphere (Petřin pokyn 2026-05-25 "zruš
     průhlednost úplně"): bright highlight → base → deep, all opaque tokens,
     no alpha, no black rim. The map never bleeds through. */
  background: radial-gradient(
    circle at 38% 30%,
    var(--accent-violet-bright) 0%,
    var(--accent-violet) 55%,
    var(--accent-violet-deep) 100%
  );

  /* backdrop-filter ODSTRANĚN 2026-05-14 (Petřin retrospective):
     solid alpha 0.95 = blur backdrop nedělá vizuální rozdíl, ale
     vytvářel GPU compositing layer (= expensive paint per frame).
     Cluster orb sám už čte jako "solid violet sphere" bez něj. */

  /* Static box-shadow (no longer animating it per frame). Single
     halo layer + drop = enough atmospheric weight bez per-frame
     paint cost. */
  box-shadow:
    0 0 0 2px rgba(var(--accent-violet-rgb), 0.88),
    0 0 18px rgba(var(--accent-violet-rgb), 0.55),
    0 4px 12px rgba(0, 0, 0, 0.40);

  color: var(--text-light);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  font-size: 0.85rem;
  font-weight: 700;
  font-family: inherit;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;

  display: flex;
  align-items: center;
  justify-content: center;

  /* Breathe via transform: scale — GPU-cheap (compositor-only), žádný
     per-frame box-shadow recalc. Předchozí keyframe pulsing 3-layer
     box-shadow běžel kontinuálně na všech clusterech (= paint cost
     i když user nic nedělá). Per Petřin retrospective 2026-05-14
     simplify performance. */
  animation: cluster-frost-breathe 5s ease-in-out infinite;
  transform-origin: center;
  transition: transform 220ms ease, box-shadow 220ms ease;
}

/* Cheap breathe — transform scale only (compositor layer), žádný
   shadow recompute. Subtle 1.0 → 1.04 → 1.0 pulse. */
@keyframes cluster-frost-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.05); }
}

/* M18-LIVE-MAP — the orb that swallowed a NEW echo (heartbeat arrival)
   "gulps" once: single scale tick, the count updates by itself. Applied to
   .cluster-frost__inner by map.js animatePinArrival (transient class,
   removed on animationend). MUST live after the .cluster-frost__inner base
   rule — same specificity, and the base `animation` shorthand would win the
   cascade otherwise (e2e 2026-06-10 caught the gulp never firing). The
   breathe loop is re-declared FIRST in the list: for a conflicting property
   (transform) the later animation wins, so the gulp overrides the breathe
   for its 450ms and the loop resumes when the class drops. */
.cluster-frost--swallow {
  animation:
    cluster-frost-breathe 5s ease-in-out infinite,
    fm-cluster-swallow 450ms ease-out;
}
@keyframes fm-cluster-swallow {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .cluster-frost--swallow {
    animation: none;
  }
}

/* Density tiers — bumped baseline (smallest cluster ≥ pin width 30 px)
   so even a 2-pin cluster reads bigger than a single pin teardrop. */
.cluster-frost--sm { --cluster-size: 34px; font-size: 0.80rem; }   /* 2–9   */
.cluster-frost--md { --cluster-size: 42px; font-size: 0.88rem; }   /* 10–49 */
.cluster-frost--lg { --cluster-size: 52px; font-size: 0.98rem; }   /* 50–199 */
.cluster-frost--xl { --cluster-size: 64px; font-size: 1.10rem; }   /* 200+  */

/* M11.X-MINE-HIGHLIGHT (ADR-152 iter #3) — cluster s ≥ 1 vlastním pinem
   AND toggle ON. Full body fill flip z violet radial gradient na
   rust-red tones (= Petřin screenshot reference #9C0A00). Outer
   shadow stack zachovává shape volume, ale base barevně přechází
   na --accent-mine rust-red palettu. Per Petřin pokyn "vůbec tam
   nepotřebuju ten glov" odstraněn 18px outer halo blur (= jen
   tenký 2px ring + drop shadow pro shape definition). Toggle-
   dependent — refreshClusters() v map.js re-runs iconCreateFunction
   na flip. */
.cluster-frost--mine .cluster-frost__inner {
  /* M12 Noir PR5 — mine = magenta (matches the crowned magenta moment
     marker), no longer the Frost rust-red. Consistent "mine = --accent-mine"
     across marker + cluster. */
  background: radial-gradient(
    circle at 38% 30%,
    var(--accent-mine-bright) 0%,
    var(--accent-mine) 55%,
    var(--accent-mine-deep) 100%
  );
  box-shadow:
    0 0 0 2px rgba(var(--accent-mine-rgb), 0.88),
    0 4px 12px rgba(0, 0, 0, 0.40);
}

/* M11.X-MARKERS (ADR-116 + ADR-121) — event cluster orb. Mirror of the
   pin cluster recipe above but in the turquoise family + 🎤 glyph for
   instant "concert" recognition. Lives next to pin clusters; never mixed
   per ADR-116 R2 (separate Leaflet cluster groups in map.js). */
.event-cluster-frost {
  background: transparent;
  border: none;
}

.event-cluster-frost__inner {
  width: var(--cluster-size, 38px);
  height: var(--cluster-size, 38px);
  border-radius: 50%;

  /* Turquoise body — atmospheric radial gradient with the same 0.95 alpha
     as the pin orb so the map underneath does not bleed through. RGB stops
     are the --accent-turquoise palette (#0d7575 → darker tail) so this
     orb sits in the brand family next to violet pin orbs. */
  background: radial-gradient(
    circle at 38% 30%,
    var(--accent-turquoise-bright) 0%,
    var(--accent-turquoise) 55%,
    var(--accent-turquoise-deep) 100%
  );

  box-shadow:
    0 0 0 2px rgba(var(--accent-turquoise-rgb), 0.88),
    0 0 18px rgba(var(--accent-turquoise-rgb), 0.55),
    0 4px 12px rgba(0, 0, 0, 0.40);

  color: var(--text-light);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  font-size: 0.85rem;
  font-weight: 700;
  font-family: inherit;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;

  display: flex;
  align-items: center;
  justify-content: center;

  /* Reuse the pin cluster breathe animation — same rhythm = sibling feel. */
  animation: cluster-frost-breathe 5s ease-in-out infinite;
  transform-origin: center;
  transition: transform 220ms ease, box-shadow 220ms ease;
}

.event-cluster-frost--sm { --cluster-size: 34px; font-size: 0.78rem; }   /* 2–9   */
.event-cluster-frost--md { --cluster-size: 42px; font-size: 0.86rem; }   /* 10–49 */
.event-cluster-frost--lg { --cluster-size: 52px; font-size: 0.96rem; }   /* 50–199 */
.event-cluster-frost--xl { --cluster-size: 64px; font-size: 1.08rem; }   /* 200+  */

/* 2026-05-20 — World zoom (≤3) shrinks cluster orbs ~70 % so dense
   continental neighbours (Europe) stop visually piling up while staying
   readable. body.frisson-world-zoom toggled from map.js zoom listener.

   Cluster-tuning 2026-06-10 (Petřin audit „Evropa je hromada"):
   1. Extended from the event family to ALL THREE orb families + the
      individual markers — the 2026-05-20 fix shrunk only turquoise
      orbs, so violet/copper kept piling up at world view.
   2. The shrink must live in the ANIMATION, not a static transform:
      cluster-frost-breathe animates `transform` on the same element,
      and a running animation overrides any static value — the original
      static scale(0.7) never actually applied. The -world keyframes
      breathe around the shrunk size instead. The static transform below
      stays as the reduced-motion fallback (animation: none → static
      value takes over).
   3. Scoped to .leaflet-marker-pane so the legend's miniature orb
      (.map-legend__icon) keeps its size — UI chrome must not change
      with map zoom. */
@keyframes cluster-frost-breathe-world {
  0%, 100% { transform: scale(0.7); }
  50%      { transform: scale(0.735); }
}
body.frisson-world-zoom .leaflet-marker-pane .cluster-frost__inner,
body.frisson-world-zoom .leaflet-marker-pane .event-cluster-frost__inner,
body.frisson-world-zoom .leaflet-marker-pane .poi-cluster-frost__inner {
  animation-name: cluster-frost-breathe-world;
  transform: scale(0.7);
  transform-origin: center;
}

/* M18-LIVE-MAP — world-zoom variant of the new-echo "gulp": the rule above
   overrides animation-name on the shrunk orbs (and out-specifies the base
   .cluster-frost--swallow), so without this the gulp never plays at the
   DEFAULT world view (e2e 2026-06-10). Same trick as the -world breathe:
   the gulp keyframes scale around 0.7. Durations/iterations come from the
   base .cluster-frost--swallow shorthand (5s infinite + 450ms once). */
body.frisson-world-zoom .leaflet-marker-pane .cluster-frost--swallow {
  animation-name: cluster-frost-breathe-world, fm-cluster-swallow-world;
}
@keyframes fm-cluster-swallow-world {
  0%   { transform: scale(0.7); }
  45%  { transform: scale(0.79); }
  100% { transform: scale(0.7); }
}

/* Individual markers scale with zoom (Petra 2026-06-10 — biggest at max
   zoom; markers used to be a constant 30×41 px from zoom 4 up, which
   reads tiny against street-level imagery). The factor lives in
   --fm-marker-zoom-scale, set by map.js on zoomend: 0.75 at world view
   (≤3), 1.0 through the clustered zooms (4–7), then ~+5 %/zoom up to
   1.45 at max zoom. Purely VISUAL: targets the INNER element / svg —
   the outer wrap carries Leaflet's positioning transform and must never
   be transformed; anchors, clustering and spiderfy are untouched.
   transform-origin 50% 90% ≈ the teardrop tip, so the visual stays
   anchored to its lat/lng at every scale. */
.leaflet-marker-pane .pin-marker,
.leaflet-marker-pane .event-marker > svg,
.leaflet-marker-pane .poi-marker > svg {
  transform: scale(var(--fm-marker-zoom-scale, 1));
  transform-origin: 50% 90%;
}

/* M18-LIVE-MAP — world-zoom variant of the new-echo arrival: the shrink
   above is a STATIC transform on the same element the arrival animates,
   and a running animation overrides it — the base fm-pin-arrive would
   play at full scale and snap back to 0.75 at the end. These keyframes
   settle around the shrunk size instead. */
body.frisson-world-zoom .leaflet-marker-pane .pin-marker--arriving {
  animation-name: fm-pin-arrive-world;
}
@keyframes fm-pin-arrive-world {
  0%   { transform: translateY(-6px) scale(0.75); opacity: 0; filter: brightness(1); }
  55%  { opacity: 1; filter: brightness(1.28); }
  100% { transform: translateY(0) scale(0.75); opacity: 1; filter: brightness(1); }
}

@media (prefers-reduced-motion: reduce) {
  body.frisson-world-zoom .leaflet-marker-pane .cluster-frost__inner,
  body.frisson-world-zoom .leaflet-marker-pane .event-cluster-frost__inner,
  body.frisson-world-zoom .leaflet-marker-pane .poi-cluster-frost__inner,
  /* M18-LIVE-MAP — the world-zoom arrival/gulp rules out-specify the base
     reduced-motion guards, so they need their own kill switch here. */
  body.frisson-world-zoom .leaflet-marker-pane .pin-marker--arriving,
  body.frisson-world-zoom .leaflet-marker-pane .cluster-frost--swallow {
    animation: none;
  }
}

@media (hover: hover) and (pointer: fine) {
  .event-cluster-frost:hover .event-cluster-frost__inner {
    transform: scale(1.08);
    box-shadow:
      0 0 0 1.5px rgba(var(--accent-turquoise-rgb), 0.90),
      0 0 28px rgba(var(--accent-turquoise-rgb), 0.70),
      0 4px 14px rgba(0, 0, 0, 0.30);
    animation-play-state: paused;
  }
}

@media (prefers-reduced-motion: reduce) {
  .event-cluster-frost__inner {
    animation: none;
  }
}

/* Hover lift — only on devices with real hover (per design-system rule 8:
   touch zařízení nemají persistent hover, transform by se "zasekl"). */
@media (hover: hover) and (pointer: fine) {
  .cluster-frost:hover .cluster-frost__inner {
    transform: scale(1.08);
    box-shadow:
      0 0 0 1.5px rgba(var(--accent-violet-rgb), 0.90),
      0 0 28px rgba(var(--accent-violet-rgb), 0.70),
      0 4px 14px rgba(0, 0, 0, 0.30);
    /* Pause the breathe on hover so the lift state is stable. */
    animation-play-state: paused;
  }
}

/* Respect users with reduced-motion preference — kill the breathe. */
@media (prefers-reduced-motion: reduce) {
  .cluster-frost__inner {
    animation: none;
  }
}

/* Adaptive zoom-based marker sizing — Petřin density wish 2026-05-14
   (Task #13, BEZ klasického clusteringu): "málo pinů = větší, hodně
   pinů = menší až téměř tečky; při zoomu se markery zase zvětšují".

   IMPLEMENTATION NOTE: nemůžeme použít `transform: scale()` protože
   Leaflet pinuje markery přes INLINE `style="transform: translate3d()"`
   a inline style přebíjí external CSS bez !important. Místo toho měníme
   `width` + `height` přes `!important` (Leaflet inline má width/height
   bez !important → CSS pravidlo s !important je porazí). Default marker
   = 25×41 px; níže nastavované velikosti jsou proporcionální.

   Leaflet zoom: 0 = world view (oddálené, hustota = mnoho pinů v
   malém pixelovém prostoru → markery malé), 19 = street level
   (přiblížené, jednotlivý pin čitelný → markery full size). */
/* Adaptive zoom-based marker sizing ODSTRANĚN 2026-05-14 per Petřin
   volba A po realistic UX assessment Tasku #14.

   Důvod: dva systémy "density signal" (clustering + adaptive shrink)
   bily se navzájem. Při world view byl solo (necluster-uvaný) pin
   zmenšený na 11×18 px = "drobný prach", zatímco cluster bubble vedle
   měla velikost 26-52 px = visual inconsistency. Cluster sám už řeší
   hustotu (bundle blízké → bubble s počtem); adaptive shrink přidával
   jen šum bez znalosti, jestli je v okolí 1 nebo 100 pinů.

   Markery teď vždy default 25×41 px (icon) + 41×41 px (shadow).
   Density vyjadřuje výhradně clustering; isolated piny zachovají
   plnou vizuální přítomnost komunity ve sparse oblastech. */

/* M10B Stage 2 (STYLE-012) — DEAD CSS dropped:
   `.pin-grouping-state` / `.pin-grouping-chip` / `.pin-grouping-chip__label`
   were the inline grouping picker styling, removed in AI-S1-011 (the
   pre-form addPinNearbyChooser is now the only grouping decision
   point). The HTML elements were already gone in Commit 3 of the
   Stage 1 actions; the CSS selectors had no matching DOM. */

/* ──────────────────────────────────────────────────────────────────
 * User dropdown menu (M10B Stage 3 review AI-S1-010, Varianta C)
 *
 * Triggered by the @username button in auth-header. Anchored to the
 * viewport via JS-computed top/right (set inline by authUI.js on each
 * open so the menu stays under the button across viewport changes).
 * Pre-launch minimal styling — Stage 2 Style review will polish it. */
.auth-user-menu {
  position: fixed;
  z-index: 1100;
  min-width: 220px;
  padding: 0.4rem;
  background: rgba(var(--cream-surface-rgb), 0.98);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md, 12px);
  box-shadow:
    0 12px 36px rgba(0, 0, 0, 0.22),
    0 2px 8px rgba(0, 0, 0, 0.12);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.auth-user-menu > button {
  appearance: none;
  background: transparent;
  border: none;
  text-align: left;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--text-dark);
  padding: 0.55rem 0.75rem;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  transition: background 150ms ease;
}

.auth-user-menu > button:hover,
.auth-user-menu > button:focus-visible {
  background: rgba(var(--accent-violet-rgb), 0.12);
  outline: none;
}

/* M12.X-PROFILE-FIDELITY — emoji → sprite glyphs, single teal tone (Petřin
   pokyn 2026-05-26: „stačí jeden tón, ať to není šedé"). Brightens + nudges
   on hover so each row reacts. */
.auth-user-menu__icon {
  color: var(--accent-turquoise);
  flex-shrink: 0;
  transition:
    color 150ms ease,
    transform 150ms ease;
}
.auth-user-menu > button:hover .auth-user-menu__icon,
.auth-user-menu > button:focus-visible .auth-user-menu__icon {
  color: var(--accent-turquoise-bright);
  transform: scale(1.08);
}

.auth-user-menu__sep {
  /* M10B Stage 2 (STYLE-011) — use the design-system border token
     instead of a one-off rgba literal (the 0.12 vs token's 0.14 was
     invisible but duplicated the value). */
  border: none;
  border-top: 1px solid var(--border-on-light);
  margin: 0.25rem 0;
}

/* Rename kotva dialog (M10B Stage 3 review AI-S1-005). Inherits the
   delete-kotva-modal__panel styling for shared dialog chrome; this
   selector exists for future styling overrides specific to rename. */
.rename-kotva-modal .field {
  margin-bottom: 0.5rem;
}

/* Quota line warn variant (M10B Stage 3 review AI-S1-001) — surfaced
   when the user has hit a hard wall (anon exhausted, registered
   exhausted / ceiling reached). Copper hue = warm "pozor" tone,
   distinct from destructive `--danger-light` (admin cascade) so the
   semantics don't blur (STYLE-006 split). */
.dialog-hint--warn {
  color: var(--accent-copper);
  font-weight: 600;
}

/* M10B Stage 2 (STYLE-006) — destructive variant for warnings about
   irreversible cascading actions (admin Delete kotva = wipes N child
   pins). Uses `--danger` (cihla red) instead of copper so the visual
   signal differs from "you're approaching a limit" (copper warn).
   Subtle red-tinted background + border so the message reads as
   "destructive action ahead", not just "watch out". */
.dialog-hint--danger-light {
  background: rgba(var(--danger-rgb), 0.10);
  border: 1px solid rgba(var(--danger-rgb), 0.30);
  color: var(--danger-text);
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
}

/* Admin Reports — pin kind indicator (kotva / child / standalone)
   in the detail panel. M10B Stage 1 review smoke-test follow-up
   (Petřin pokyn 2026-05-12) — admin must see at a glance what they
   are about to act on. */
.admin-detail__kind {
  font-weight: 600;
}
.admin-detail__kind--kotva {
  /* M10B Stage 2 (STYLE-004) — token-only, no wrong-fallback hex. */
  color: var(--accent-copper);
}
.admin-detail__kind--child {
  color: var(--accent-violet-soft);
}
.admin-detail__kind--standalone {
  /* `--accent-turquoise-deep` doesn't exist as a token; the deep
     variant IS `--accent-turquoise`. STYLE-004 fix. */
  color: var(--accent-turquoise);
}

/* === Maintenance overlay (= reactive to API health) ============================
   Shown when /health returns 5xx / timeout for 2+ consecutive checks.
   Auto-hidden when API recovers (= polling cycle in healthWatcher.js).
   Atmospheric chrome matches dialog[id^="auth-"] family — cream panel +
   radial-tinted backdrop with blur, per design-system.md. */
.maintenance-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;  /* above all dialogs (1100), modals, headers */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  animation: maintenance-fade-in 240ms ease-out;
}
.maintenance-overlay[hidden] {
  display: none;
}
@keyframes maintenance-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.maintenance-overlay__panel {
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  max-width: 480px;
  width: 100%;
  padding: 2.5rem 2rem;
  text-align: center;
  box-shadow:
    var(--shadow-modal),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}

.maintenance-overlay__icon {
  font-size: 3rem;
  line-height: 1;
  margin-bottom: 1rem;
}

.maintenance-overlay__title {
  /* M12 PR6 Dávka F — canonical dialog title metrics (ADR-179): was 1.4rem /
     0.01em / no line-height; aligned to the big content-modal tier
     (M13.X-TITLE-TIERS). */
  font-size: var(--fs-modal-title);
  font-weight: 700;
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0 0 0.75rem 0;
  font-family: var(--font-display);
  font-style: italic;
  color: var(--text-dark);
}

.maintenance-overlay__body {
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-dark);
  margin: 0 0 1.25rem 0;
}

.maintenance-overlay__contact {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-muted);
  margin: 0;
}

.maintenance-overlay__contact a {
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.maintenance-overlay__contact a:hover {
  color: var(--accent-copper);
}

/* ============================================================
   M10D Stage 1 Cyklus 5 — Photon search box, Suggest wizard.
   ============================================================ */

/* ---- Photon search box (shared admin component, AI-S1-M10D-011) ---- */
.photon-search-box {
  position: relative;
}
.photon-search-box__input {
  width: 100%;
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  border: 1px solid var(--border-on-light);
  background: var(--cream-base);
  font: inherit;
}
.photon-search-box__input:focus {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: -1px;
  border-color: transparent;
}
.photon-search-box__results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 50;
  margin: 0;
  padding: 0.2rem 0;
  list-style: none;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: 8px;
  box-shadow: var(--shadow-dropdown);
  max-height: 240px;
  overflow-y: auto;
}
.photon-search-box__result {
  padding: 0.45rem 0.7rem;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.photon-search-box__result:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.08);
}
.photon-search-box__result-line {
  font-size: 0.9rem;
  color: var(--text-dark);
}
.photon-search-box__result-coords {
  font-size: 0.72rem;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.photon-search-box__status {
  margin: 0.3rem 0 0;
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* ---- Suggest typ choice dialog (AI-S1-M10D-035 step 1) ---- */
.suggest-typ-choice-dialog {
  border: 1px solid rgba(var(--accent-violet-rgb), 0.30);
  border-radius: var(--radius-lg);
  padding: 1.2rem 1.2rem 1.4rem;
  max-width: 560px;
  width: calc(100vw - 2rem);
  /* Atmospheric mesh — shared light modal chrome (design rollout
     all-or-nothing per memory `feedback_design_rollout_all_or_nothing`). */
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}
.suggest-typ-choice-dialog h2 {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.35rem;
  text-align: center;
}
.suggest-typ-choice__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.9rem;
}
@media (max-width: 559px) {
  .suggest-typ-choice__grid {
    grid-template-columns: 1fr;
  }
}
/* Base — shared layout. Per AI-S2-M10D-020 Petřin volba A:
   --primary (Online) = violet gradient + glow (drama, backend writes);
   --secondary (Frisson Tour) = violet solid + no glow (Reddit redirect). */
.suggest-typ-choice__card {
  appearance: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.45rem;
  padding: 1rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--accent-violet-soft);
  text-align: left;
  font: inherit;
  color: var(--text-on-accent);
  transition: background 180ms ease, transform 180ms ease, box-shadow 180ms ease;
}
.suggest-typ-choice__card--primary {
  background: linear-gradient(135deg, var(--accent-violet) 0%, var(--accent-violet-bright) 100%);
  box-shadow:
    0 6px 18px rgba(var(--accent-violet-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.18);
}
.suggest-typ-choice__card--secondary {
  background: var(--accent-violet);
  box-shadow: 0 3px 10px rgba(var(--accent-violet-rgb), 0.22);
}
.suggest-typ-choice__card:hover,
.suggest-typ-choice__card:focus-visible {
  outline: none;
}
.suggest-typ-choice__card--primary:hover,
.suggest-typ-choice__card--primary:focus-visible {
  box-shadow:
    0 10px 26px rgba(var(--accent-violet-rgb), 0.42),
    inset 0 1px 0 rgba(238, 240, 250, 0.22);
}
.suggest-typ-choice__card--secondary:hover,
.suggest-typ-choice__card--secondary:focus-visible {
  background: var(--accent-violet-bright);
}
@media (hover: hover) and (pointer: fine) {
  .suggest-typ-choice__card:hover {
    transform: translateY(-2px);
  }
}
.suggest-typ-choice__icon {
  font-size: 1.6rem;
  line-height: 1;
}
.suggest-typ-choice__label {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--text-light);
}
.suggest-typ-choice__body {
  font-size: 0.85rem;
  color: rgba(var(--cream-base-rgb), 0.88);
  line-height: 1.4;
}

/* ---- Suggest Frisson Tour edu dialog (RETIRED M11.X-EVENTS-ARCH
   Bundle D — Reddit redirect for Frisson Tour replaced by the unified
   eventCreateModal flow per ADR-094 + ADR-095). Dead-code CSS kept
   below intentionally — pruning would touch too many adjacent rules
   on a short timeline. M12 visual sweep can remove. */
.suggest-frisson-tour-dialog--retired-do-not-style {
  display: none;
}
.suggest-frisson-tour-dialog {
  display: none;
}
.suggest-frisson-tour-dialog h2 {
  margin: 0 0 0.7rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.3rem;
}
.suggest-frisson-tour-dialog__cta {
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 1.1rem;
  border-radius: var(--radius-pill, 999px);
  background: linear-gradient(135deg,
              var(--accent-violet) 0%,
              var(--accent-violet-bright) 100%);
  color: var(--text-light);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-decoration: none;
}

/* Generic dialog close × — shared by the wizard dialogs. */
/* M-UX-AUDIT-017 (M10 closure audit 2026-05-15): wizard .dialog-close
   redundant duplicate of the canonical .dialog-close at line ~2159.
   Removed to let the 44×44 canonical rule apply on wizard dialogs
   too (suggest-typ-choice + suggest-frisson-tour). */

/* ==========================================================================
   Unified modal dialog title (M12 PR6 Fáze 0, ADR-179) — VŠECHNY dialogové
   titulky = bílá Cormorant italic 700, 1.55rem, ls 0.025em, lh 1.15. Žádný
   gradient (zrušen v PR6 title sweep; gradient zůstává jen na hero wordmarku
   + statických content-page h1). Velikost sjednocena napříč pin-form / auth /
   event-create / generic / frost / confirm. Selector listed at end of
   stylesheet to win specificity na všechny dialog h2 bez !important.
   ========================================================================== */
.pin-privacy-chooser-dialog h2,
dialog#add-pin-dialog h2,
.suggest-typ-choice-dialog h2,
.suggest-frisson-tour-dialog h2,
.event-detail-modal h2,
.event-create-dialog h2,
.pin-expand-overlay__panel h2,
#pin-form h2 {
  /* M12 Noir PR6 — dialog/modal titles = white Cormorant italic, no
     gradient (Petřin pokyn 2026-05-25). Aurora gradient retired here +
     in every per-dialog title rule below; gradient stays only on the
     hero wordmark.
     M13.X-TITLE-TIERS — big "content modal" tier. (delete/rename Landmark
     left this list → small confirm tier on .delete-kotva-modal__title.) */
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: var(--fs-modal-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0 0 0.8rem;
  color: var(--text-dark);
}

/* ================================================================
 * Event create single-modal (M11.X-EVENTS-ARCH Bundle D / Petřin
 * redesign 2026-05-17). One modal, no wizard steps. URL paste autofill
 * + Photon smart Where search + venue rich card with mini-mapa.
 * Frost atmospheric mesh, Cormorant display, Manrope body.
 * ================================================================ */

.event-create-dialog {
  /* M12 PR6 Fáze 0 — sjednoceno na kanonický shell (ADR-179): tyrkysová
     hairline border 0.15 (byl violet 0.30) + kanonický mesh teal 0.08 /
     violet 0.06 (byl 0.10/0.08). M12 PR6 Dávka A — šířka na wide-form tier
     720 (ui-standards §2). Online split na 560 níž (.online-event-create-
     dialog), aby jednodušší online formulář nezdědil 720. */
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  /* M15 [UX] sticky footer — bottom padding moved into .event-create__actions
     (same per-region move as #pin-form, AI-S1-M11D-006): the dialog is the
     scroll container and Chromium pins sticky children against its CONTENT
     box, so any padding-bottom here would hold the pinned footer 1.4rem
     above the bottom edge with content showing through the gap. */
  padding: 1.25rem 1.4rem 0;
  /* Keyboard focus auto-scroll must clear the sticky footer (≈4.4rem tall),
     otherwise tabbing into the last fields lands them under the button row. */
  scroll-padding-bottom: 4.5rem;
  max-width: 720px;
  width: calc(100vw - 2rem);
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
  color: var(--text-dark);
}

.event-create__header {
  position: relative;
  margin-bottom: 1rem;
  padding-right: 2.6rem;
}

/* M12 PR6 Fáze 0 — kanonický dialog titul (ADR-179): bílá Cormorant italic
 * 700, 1.55rem, sjednoceno s #pin-form h2 + auth h2 + generic unified rule.
 * !important quartet zrušen — tento selektor (později v souboru + vyšší
 * specificita) vyhrává nad unified `.event-create-dialog h2` bez něj. */
#event-create-title,
.event-create-dialog .event-create__header h2 {
  margin: 0 0 0.4rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-modal-title); /* big content-modal tier (M13.X-TITLE-TIERS) */
  letter-spacing: 0.025em;
  line-height: 1.15;
  font-style: italic;
  color: var(--text-dark);
}

.event-create__subtitle {
  margin: 0;
  font-size: 0.88rem;
  color: var(--text-muted);
  line-height: 1.5;
}

/* M13.X-HEADER-UNIFY (2026-06-04) — the "+" title badge (M12 Dávka-A,
   mockups 10/11) is gone on desktop too (mobile hid it already): the header
   band's world tones (.modal-band--*) carry the form's identity instead.
   The row wrapper stays — mobile centres the title through it. */
.event-create__title-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
}
/* h2 owns a 0.4rem bottom margin via an #id rule; the row supplies the gap to
   the subtitle instead, so zero it here (the #id beats class specificity). */
.event-create__title-row h2 {
  margin: 0 !important;
}
.event-create__form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;   /* M12 PR6 Fáze 0 — kanonická dialog section gap (ADR-179) */
}

/* ---- Paste-URL primary affordance ---- */
.event-create__paste {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  padding: 0.8rem 0.9rem;
  border-radius: var(--radius-md);
  background: linear-gradient(
    135deg,
    rgba(var(--accent-turquoise-rgb), 0.08) 0%,
    rgba(var(--accent-violet-rgb), 0.06) 100%
  );
  border: 1px dashed rgba(var(--accent-turquoise-rgb), 0.30);
}

/* M12 Noir PR6-A — paste-box label is a quiet uppercase field caption
   (matches .field__label), not a second Cormorant heading competing with
   the dialog title. The 🪄 icon stays inline as a small affordance hint. */
.event-create__paste-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 500;
  font-size: var(--fs-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.event-create__paste-icon {
  font-size: var(--fs-base);
}

.event-create__paste-input {
  font: inherit;
  padding: 0.55rem 0.75rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.30);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.75);
  color: var(--text-dark);
}

.event-create__paste-input:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
}

/* M12 Noir PR6-A — removed dead CSS: .event-create__paste-btn,
   .event-create__paste-row + media, and the .event-create__chip* type
   toggle. None are rendered anymore (paste auto-fires on paste; type is
   chosen by the panel CTA before the modal opens — comments in index.html
   confirm). Deleting them also kills a hardcoded teal hex literal. */

.event-create__paste-status {
  margin: 0;
  font-size: 0.82rem;
  color: var(--accent-turquoise);
}

.event-create__paste-status.is-error {
  color: var(--danger-text);
}

/* ---- When (date + optional time) ---- */
.event-create__when {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.event-create__when-row {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.event-create__when-part {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex: 1 1 9rem;
  min-width: 9rem;
}

/* M11.X-CONTAINER-ENTITY (Petřin pokyn 2026-05-24) — Date + Time are short
   fields, keep them SIDE-BY-SIDE. The earlier `flex-direction: column` stack
   had two problems: (1) it wasted vertical space on two short controls, and
   (2) the parts' `flex: 1 1 9rem` basis became a HEIGHT on the column main
   axis, inflating each part to 9rem tall → the "crazy gap" between Date and
   Time. Side-by-side with an asymmetric split: Date grows to fill (long months
   like "December" need the room), Time takes a fixed narrow column ("09:26"
   never needs more). nowrap on both the row and the date label guarantees a
   single clean line.
   M12 Dávka-A (2026-05-25) — un-scoped from #pin-form to ALL event when-rows.
   On the wide (720px) concert / online dialogs the symmetric `1 1 9rem` split
   left the Time picker floating in half the dialog width (empty strip); the
   asymmetric split fixes that and matches mockup 10's compact date + time. */
.event-create__when-row {
  flex-wrap: nowrap;
}
.event-create__when-part {
  min-width: 0;
}
.event-create__when-part:first-child {
  flex: 1 1 auto;
}
.event-create__when-part:last-child {
  flex: 0 0 7rem;
}
/* M12 (Petřin pokyn 2026-05-26) — online "When" row holds THREE controls in
   one line: date (grows) + time + duration (both narrow). Wraps to stacked
   on mobile so nothing gets crushed. */
.event-create__when-row--triple {
  gap: 0.5rem;
}
.event-create__when-row--triple .event-create__when-part:first-child {
  flex: 2 1 9rem;   /* date — largest, but shares width instead of hogging it */
  min-width: 0;
}
.event-create__when-row--triple .event-create__when-part:nth-child(2) {
  flex: 1 1 5rem;   /* time */
}
.event-create__when-row--triple .event-create__when-part:last-child {
  flex: 1.4 1 6.5rem;  /* duration — room for the icon + value + chevron */
}
@media (max-width: 600px) {
  .event-create__when-row--triple {
    flex-wrap: wrap;
  }
  .event-create__when-row--triple .event-create__when-part {
    flex: 1 1 100%;
  }
}
.frost-date-picker__display-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* .event-create__when-sublabel removed (M12 Noir, Petřin pokyn 2026-05-25 —
   "zbytečné nadpisy"): Date/Time sub-headings dropped from all when-blocks
   (pin form + concert/online create + admin). The "When *" label + the time
   hint below carry the required-date / optional-time meaning. */

/* ====== Frost custom date + time pickers ============================
   Replace browser-native calendar / time popups in event modals with
   in-house Frost-themed components. Per Petřin pokyn 2026-05-19 —
   nativní pickery + crowded chip strip = nepřijatelné, chce „náš
   design". Component DOM markup is generated by frostDateTimePickers.js
   factories `dateFieldHtml()` + `timeFieldHtml()`. */

/* Date wrapper is NOT a box — its `.frost-date-picker__display` button carries
   the frame. (It only needs positioning context for the calendar panel.)
   Putting a box here too caused a double frame around the date — Petřin postřeh
   2026-05-26. The box rule below is for the TIME picker only. */
.frost-date-picker {
  position: relative;
  width: 100%;
}

.frost-time-picker {
  /* M12 (Petřin pokyn 2026-05-26) — the wrapper IS the field box (same as
     .frost-duration-picker__field): the input sits borderless inside and the
     chevron toggle is a flex child on the right = chevron INSIDE the box, no
     more outside-the-frame arrow. Matches the date display + duration field. */
  position: relative;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.6rem 0.55rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: var(--cream-sunken);
  transition: border-color 200ms ease, box-shadow 200ms ease,
              background 200ms ease;
}

.frost-time-picker.is-open,
.frost-time-picker:focus-within {
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

/* ---- Date picker — display button ------------------------------- */

.frost-date-picker__display {
  appearance: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: var(--cream-sunken);
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  text-align: left;
  transition: border-color 200ms ease, box-shadow 200ms ease,
              background 200ms ease;
}

.frost-date-picker__display:hover,
.frost-date-picker.is-open .frost-date-picker__display,
.frost-date-picker__display:focus-visible {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

.frost-date-picker__display-icon {
  display: inline-flex;
  color: var(--accent-turquoise);
}

.frost-date-picker__display-text {
  flex: 1 1 auto;
  font-variant-numeric: tabular-nums;
}

/* Pseudo-placeholder (span, not a real input) — keep in sync with the global
   ::placeholder rule via the shared token. */
.frost-date-picker__display-text.is-placeholder {
  color: var(--text-placeholder);
}

.frost-date-picker__display-chevron {
  display: inline-flex;
  color: var(--text-muted);
  transition: transform 180ms ease;
}

.frost-date-picker.is-open .frost-date-picker__display-chevron {
  transform: rotate(180deg);
}

/* ---- Date picker — panel --------------------------------------- */

.frost-date-picker__panel {
  position: absolute;
  top: calc(100% + 0.45rem);
  left: 0;
  right: 0;
  z-index: 30;
  padding: 0.85rem;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: 14px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.frost-date-picker__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.frost-date-picker__month {
  flex: 1 1 auto;
  text-align: center;
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text-dark);
  letter-spacing: 0.01em;
  text-transform: capitalize;
}

.frost-date-picker__nav {
  appearance: none;
  cursor: pointer;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border-on-light);
  border-radius: 8px;
  color: var(--text-dark);
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

.frost-date-picker__nav:hover,
.frost-date-picker__nav:focus-visible {
  outline: none;
  background: var(--accent-turquoise-tint);
  border-color: var(--accent-turquoise);
  color: var(--accent-turquoise);
}

.frost-date-picker__weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-align: center;
  padding: 0 0.05rem;
}

.frost-date-picker__weekdays span {
  padding: 0.25rem 0;
}

.frost-date-picker__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.frost-date-picker__day {
  appearance: none;
  cursor: pointer;
  aspect-ratio: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}

.frost-date-picker__day:hover:not([disabled]),
.frost-date-picker__day:focus-visible:not([disabled]) {
  outline: none;
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
  border-color: var(--accent-turquoise);
}

.frost-date-picker__day.is-today {
  border-color: var(--accent-turquoise);
  color: var(--accent-turquoise);
  font-weight: 700;
}

.frost-date-picker__day.is-selected,
.frost-date-picker__day.is-selected:hover {
  background: var(--accent-turquoise);
  border-color: var(--accent-turquoise);
  color: var(--text-on-accent);
  font-weight: 700;
}

.frost-date-picker__day.is-disabled,
.frost-date-picker__day[disabled] {
  cursor: not-allowed;
  color: var(--text-light-muted);
  background: transparent;
  border-color: transparent;
}

.frost-date-picker__day.is-outside {
  visibility: hidden;
}

.frost-date-picker__footer {
  display: flex;
  justify-content: flex-end;
  padding-top: 0.2rem;
  border-top: 1px solid var(--border-on-light);
}

.frost-date-picker__clear {
  appearance: none;
  cursor: pointer;
  padding: 0.35rem 0.85rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-pill, 999px);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}

.frost-date-picker__clear:hover,
.frost-date-picker__clear:focus-visible {
  outline: none;
  background: rgba(238, 240, 250, 0.05);
  color: var(--text-dark);
  border-color: rgba(238, 240, 250, 0.30);
}

/* ---- Time picker — field --------------------------------------- */

.frost-time-picker__field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.6rem 0.55rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: var(--cream-sunken);
  transition: border-color 200ms ease, box-shadow 200ms ease,
              background 200ms ease;
}

.frost-time-picker.is-open .frost-time-picker__field,
.frost-time-picker__field:focus-within {
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

.frost-time-picker__field-icon {
  display: inline-flex;
  color: var(--accent-turquoise);
}

.frost-time-picker__input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  padding: 0.15rem 0;
}

.frost-time-picker__toggle {
  appearance: none;
  cursor: pointer;
  flex: 0 0 auto;
  width: 1.4rem;
  height: 1.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  transition: background 140ms ease, color 140ms ease, transform 180ms ease;
}

.frost-time-picker__toggle:hover,
.frost-time-picker__toggle:focus-visible {
  outline: none;
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
}

.frost-time-picker.is-open .frost-time-picker__toggle {
  transform: rotate(180deg);
  color: var(--accent-turquoise);
}

/* ---- Time picker — panel --------------------------------------- */

.frost-time-picker__panel {
  position: absolute;
  top: calc(100% + 0.45rem);
  left: 0;
  right: 0;
  z-index: 30;
  max-height: 240px;
  overflow-y: auto;
  padding: 0.45rem;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: 12px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: 1px;
  scrollbar-width: thin;
  scrollbar-color: var(--accent-turquoise) transparent;
}

.frost-time-picker__panel::-webkit-scrollbar {
  width: 8px;
}

.frost-time-picker__panel::-webkit-scrollbar-thumb {
  background-color: rgba(var(--accent-turquoise-rgb), 0.45);
  border-radius: 4px;
}

.frost-time-picker__slot {
  appearance: none;
  cursor: pointer;
  padding: 0.45rem 0.75rem;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-size: 0.92rem;
  font-variant-numeric: tabular-nums;
  text-align: left;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

.frost-time-picker__slot:hover,
.frost-time-picker__slot:focus-visible {
  outline: none;
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
}

.frost-time-picker__slot.is-active {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  font-weight: 600;
}

.frost-time-picker__slot.is-active:hover,
.frost-time-picker__slot.is-active:focus-visible {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
}

/* ---- Duration picker (M12.X-LIVE-DURATION) --------------------- */
/* Editable combo — mirrors the time picker field + panel so it reads as
   the same Frost primitive. Value lives in a hidden input; the visible
   field shows a human label and accepts loose typing. */

.frost-duration-picker {
  position: relative;
  width: 100%;
}

.frost-duration-picker__field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.6rem 0.55rem 0.9rem;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: var(--cream-sunken);
  transition: border-color 200ms ease, box-shadow 200ms ease,
              background 200ms ease;
}

.frost-duration-picker.is-open .frost-duration-picker__field,
.frost-duration-picker__field:focus-within {
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}

.frost-duration-picker__field-icon {
  display: inline-flex;
  color: var(--accent-turquoise);
}

.frost-duration-picker__input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  padding: 0.15rem 0;
}

.frost-duration-picker__toggle {
  appearance: none;
  cursor: pointer;
  flex: 0 0 auto;
  width: 1.4rem;
  height: 1.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  transition: background 140ms ease, color 140ms ease, transform 180ms ease;
}

.frost-duration-picker__toggle:hover,
.frost-duration-picker__toggle:focus-visible {
  outline: none;
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
}

.frost-duration-picker.is-open .frost-duration-picker__toggle {
  transform: rotate(180deg);
  color: var(--accent-turquoise);
}

.frost-duration-picker__panel {
  position: absolute;
  top: calc(100% + 0.45rem);
  left: 0;
  right: 0;
  z-index: 30;
  max-height: 240px;
  overflow-y: auto;
  padding: 0.45rem;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: 12px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: 1px;
  scrollbar-width: thin;
  scrollbar-color: var(--accent-turquoise) transparent;
}

.frost-duration-picker__slot {
  appearance: none;
  cursor: pointer;
  padding: 0.45rem 0.75rem;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: var(--text-dark);
  font: inherit;
  font-size: 0.92rem;
  font-variant-numeric: tabular-nums;
  text-align: left;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

.frost-duration-picker__slot:hover,
.frost-duration-picker__slot:focus-visible {
  outline: none;
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
}

.frost-duration-picker__slot.is-active {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  font-weight: 600;
}

/* ---- Where smart search ---- */
.event-create__where-search {
  position: relative;
}

.event-create__where .photon-search-box__input {
  width: 100%;
  font: inherit;
  padding: 0.55rem 0.75rem;
  border: 1px solid rgba(238, 240, 250, 0.18);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.78);
  color: var(--text-dark);
}

.event-create__where .photon-search-box__input:focus-visible {
  outline: 2px solid var(--accent-violet-soft);
  outline-offset: 1px;
}

.event-create__where .photon-search-box__results,
#poi-create-where-results {
  position: absolute;
  /* Leaflet mini-mapa inside the venue card opens its own stacking
     context with internal panes at z-index 200–700, which pulled the
     map above the dropdown (Petřin postřeh 2026-05-17 — Berlin pick
     unclickable because the map was painting over the list; same bug
     hit the POI modal 2026-05-23 — našeptávač pod mapou).
     Bumping to 1000 keeps the dropdown above the map; click registers
     on the `li` again. */
  z-index: 1000;
  pointer-events: auto;
  list-style: none;
  margin: 0;
  padding: 0.25rem 0;
  width: 100%;
  background: var(--cream-surface);
  border: 1px solid rgba(var(--accent-violet-rgb), 0.25);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18);
  max-height: 280px;
  overflow-y: auto;
}

.event-create__where .photon-search-box__result {
  /* Shared .photon-search-box__result (admin search reuse) defaults
     to flex-direction:column, which stacked our flag + 2-line content
     vertically and pushed the flag to the wrong spot. Force row
     explicitly. Petřin postřeh 2026-05-17 — 'našeptávač vypadá hrozně'. */
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0.75rem;
  cursor: pointer;
  pointer-events: auto;
  transition: background 140ms ease;
}

.event-create__where .photon-search-box__result + .photon-search-box__result {
  border-top: 1px solid rgba(238, 240, 250, 0.06);
}

.event-create__where .photon-search-box__result:hover,
.event-create__where .photon-search-box__result.is-active {
  background: rgba(var(--accent-violet-rgb), 0.12);
}

.event-create__where .photon-search-box__result-content {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1 1 auto;
}

.event-create__where .photon-search-box__result-primary {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  color: var(--accent-violet-soft);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.event-create__where .photon-search-box__result-secondary {
  font-size: 0.8rem;
  color: var(--text-muted);
  line-height: 1.3;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.event-create__where .map-search__result-flag {
  width: 28px;
  height: 20px;
  object-fit: cover;
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
  flex: 0 0 auto;
}

.event-create__where .map-search__result-flag--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 20px;
  font-size: 0.95rem;
  flex: 0 0 auto;
}

.event-create__where .photon-search-box__status {
  margin: 0.3rem 0 0;
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* Conditional "Location is required" asterisk (tour mode). Standalone marker
   that can't ride a label ::after, so it matches the unified required-star look
   (copper + 1.35em) by hand — same tokens as .field__label--required::after. */
.event-create__where-required {
  color: var(--copper-accent, var(--accent-copper));
  font-weight: 700;
  font-size: 1.35em;
  line-height: 1;
  vertical-align: -0.08em;
  margin-left: 0.15rem;
}

/* ---- Venue rich card ---- */
.event-create__venue-card {
  margin-top: 0.6rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.25);
  border-radius: var(--radius-md);
  background: rgba(var(--cream-surface-rgb), 0.55);
  padding: 0.7rem 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.venue-card__top {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 0.65rem;
  align-items: center;
}

.venue-card__flag {
  width: 32px;
  height: 24px;
  object-fit: cover;
  border-radius: 3px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.20);
}

.venue-card__text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}

.venue-card__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  /* M12 Noir PR6-A — concert world = teal (per-world rule, ui-standards
     §3); the resolved-venue accent was violet (online-world colour). */
  color: var(--accent-turquoise);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.venue-card__address {
  font-size: 0.85rem;
  /* Read-only — resolved address of the picked venue (changed via the
     "Change location" button, not typed). Greyer so it doesn't look editable. */
  color: var(--text-readonly);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.venue-card__coords {
  font-size: 0.74rem;
  /* Read-only display value — unified with the address on --text-readonly. */
  color: var(--text-readonly);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

.venue-card__map-toggle,
.event-create__tour-new-collapse,
.event-create__tour-desc-refresh,
.event-create__desc-refresh,
.poi-desc-refresh {
  appearance: none;
  font: inherit;
  /* Glyph = canonical SVG chevron (actionIcons.js), set by JS on all three
     surfaces (composer mapcard + concert venue-card + POI). The old "▾" text
     glyph filled ~40 % of its em box and read tiny at any font-size
     (Petra 2026-06-04).
     M15.X-LANDMARK-DESC: .poi-desc-refresh (landmark description "generate
     again" button) shares the SAME teal circular icon-button recipe — one
     source, no per-window variant. */
  line-height: 1;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.30);
  background: transparent;
  color: var(--accent-turquoise);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 140ms ease, color 140ms ease;
}

.venue-card__map-toggle svg,
.event-create__tour-desc-refresh svg,
.event-create__desc-refresh svg,
.poi-desc-refresh svg {
  display: block;
}

.venue-card__map-toggle:hover,
.venue-card__map-toggle:focus-visible,
.event-create__tour-desc-refresh:hover,
.event-create__tour-desc-refresh:focus-visible,
.event-create__desc-refresh:hover,
.event-create__desc-refresh:focus-visible,
.poi-desc-refresh:hover,
.poi-desc-refresh:focus-visible {
  outline: none;
  background: rgba(var(--accent-turquoise-rgb), 0.15);
}

/* CANONICAL disabled state for the small teal circular icon-button
   family (map toggle / tour collapse / tour & POI refresh) — Petřin
   pokyn 2026-06-06 večer, mirrors the events-panel CTA disabled canon
   (ui-standards §2: grey "recedes, never vanishes", hover/focus tint
   OFF, not-allowed cursor). Incident: disabled tour refresh kept its
   full teal + hover and read as clickable. */
.venue-card__map-toggle:disabled,
.venue-card__map-toggle:disabled:hover,
.venue-card__map-toggle:disabled:focus-visible,
.event-create__tour-new-collapse:disabled,
.event-create__tour-new-collapse:disabled:hover,
.event-create__tour-new-collapse:disabled:focus-visible,
.event-create__tour-desc-refresh:disabled,
.event-create__tour-desc-refresh:disabled:hover,
.event-create__tour-desc-refresh:disabled:focus-visible,
.event-create__desc-refresh:disabled,
.event-create__desc-refresh:disabled:hover,
.event-create__desc-refresh:disabled:focus-visible,
.poi-desc-refresh:disabled,
.poi-desc-refresh:disabled:hover,
.poi-desc-refresh:disabled:focus-visible {
  cursor: not-allowed;
  background: transparent;
  border-color: color-mix(in srgb, var(--text-muted) 28%, transparent);
  color: var(--text-light-muted);
}

/* M15.X-LANDMARK-DESC — Description label row hosts the refresh button on
   the right edge; the label text keeps its normal .field__label styling.
   M15.X-TOUR-FLOW reuses the identical row recipe for the tour section. */
.poi-create__desc-label,
.event-create__tour-desc-label,
.event-create__desc-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* M17.X-REFRESH-UNIFY (2026-06-10) — the tour description field sits right
   under the tour-name field on the tight 0.55rem body gap, so the refresh ↻
   crowded the input above it. The POI/concert description fields breathe
   because their column gap is 0.9rem; give the tour description the same
   headroom so the refresh button isn't glued to the field above (Petřin
   pokyn 2026-06-10). */
.event-create__tour-new-body > .field:has(.event-create__tour-desc-label) {
  margin-top: 0.35rem;
}

/* Pending — spin the ↻ while the description lookup runs (Petřin pokyn
   2026-06-10). M17.X-REFRESH-UNIFY (2026-06-10) — all three refresh
   buttons (concert / POI / tour) share this local spin so a refresh
   click always shows the ↻ turning on the button itself instead of a
   dialog-wide overlay. Reuses the canonical pending-spin keyframe; the
   teal glyph stays (no grey disabled treatment), pointer-events off
   blocks a double-fire while in flight. */
.event-create__desc-refresh.is-loading,
.event-create__tour-desc-refresh.is-loading,
.poi-desc-refresh.is-loading {
  pointer-events: none;
}
.event-create__desc-refresh.is-loading svg,
.event-create__tour-desc-refresh.is-loading svg,
.poi-desc-refresh.is-loading svg {
  animation: pending-spin 0.7s linear infinite;
}

.venue-card__map.is-collapsed {
  display: none;
}

/* "Change location" text-style button — non-destructive in appearance,
   sits under the mini-map so the user reaches it only when they want
   to start over (vs the ▾ toggle that just hides the map for space). */
.venue-card__change {
  appearance: none;
  background: transparent;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--accent-turquoise);
  padding: 0.3rem 0;
  text-align: left;
  text-decoration: underline;
  text-underline-offset: 3px;
  align-self: flex-start;
}

.venue-card__change:hover,
.venue-card__change:focus-visible {
  outline: none;
  color: var(--accent-turquoise-bright);
  text-decoration-thickness: 2px;
}

.venue-card__map {
  width: 100%;
  height: 140px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.18);
  background: rgba(238, 240, 250, 0.04);
}

/* Leaflet sometimes leaves stray default sizes on the tile pane; reset. */
.venue-card__map .leaflet-control-attribution {
  display: none;
}

/* === M13.X-ECHO-FLOW location field (ADR-224) =======================
   The interactive location picker inside the standalone "Leave an Echo"
   composer (echoLocationField.js). Reuses the photon-search-box dropdown +
   the venue mini-map recipe; the chip + precision radios reuse the form's
   own .pin-form__location-chip + .field--radio so the whole field reads as
   one system (ui-standards §0.5). Moment world = teal (§3). */
.echo-location {
  /* Reserve height so empty → filled → precision never collapses the field
     to nothing (ADR-224 HARD rule: no layout skok). */
  min-height: 2.75rem;
}

.echo-location__empty {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.echo-location__search-row {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

.echo-location__search {
  flex: 1 1 auto;
  min-width: 0;
}

/* Search box — match the event-create search look inside a Noir dialog. */
.echo-location .photon-search-box__input {
  background: rgba(var(--cream-surface-rgb), 0.78);
  border: 1px solid var(--border-on-light);
  color: var(--text-dark);
}
.echo-location .photon-search-box__input:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
}
/* M13.X (Petřin pokyn 2026-06-03) — location search inputs read oversized at
   the 1rem form default on desktop; drop them to --fs-md (14px, the small-UI
   body token) there. ONE shared rule so the echo composer, concert/online
   Where search and landmark search shrink in lockstep (pattern parity).
   Desktop-only: mobile keeps 1rem — an input font under 16px makes iOS
   auto-zoom the field on focus (the guard the base .field sizing relies on).
   The event-create selector is repeated because its own rule sets
   `font: inherit` (shorthand resets font-size) at equal-or-higher specificity. */
@media (min-width: 768px) {
  .photon-search-box__input,
  .event-create__where .photon-search-box__input {
    font-size: var(--fs-md);
  }
}

/* Dropdown must clear the mini-map's Leaflet stacking context (panes at
   z 200–700) — same fix as the event-create / POI search. */
.echo-location .photon-search-box__results {
  z-index: 1000;
}
.echo-location .photon-search-box__result {
  flex-direction: row;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0.7rem;
}
.echo-location .photon-search-box__result + .photon-search-box__result {
  border-top: 1px solid rgba(238, 240, 250, 0.06);
}
.echo-location .photon-search-box__result:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.10);
}
.echo-location .photon-search-box__result-content {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1 1 auto;
}
.echo-location .photon-search-box__result-primary {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.98rem;
  color: var(--accent-turquoise);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.echo-location .photon-search-box__result-secondary {
  font-size: 0.8rem;
  color: var(--text-muted);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.echo-location .map-search__result-flag,
.echo-location .map-search__result-flag--placeholder {
  width: 26px;
  height: 19px;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 3px;
}
.echo-location .map-search__result-flag--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 📍 locate-me — square icon button next to the search box. */
.echo-location__locate {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.35);
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  color: var(--accent-turquoise);
  cursor: pointer;
  transition: background 140ms ease;
}
.echo-location__locate:hover,
.echo-location__locate:focus-visible {
  outline: none;
  background: rgba(var(--accent-turquoise-rgb), 0.18);
}
.echo-location__locate:disabled {
  opacity: 0.55;
  cursor: progress;
}

.echo-location__pick-link {
  align-self: flex-start;
  /* Breathing room from the search field above (Petřin postřeh 2026-06-03). */
  margin-top: 0.5rem;
  font-size: 0.82rem;
}

/* Filled state — chip + Change + (GPS) precision. */
.echo-location__chip-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.echo-location__chip {
  flex: 1 1 auto;
  min-width: 0;
}
.echo-location__change {
  flex: 0 0 auto;
  font-size: 0.82rem;
}

/* Precision section — fades / slides in so the expand reads smooth, not a
   jump (ADR-224 HARD rule). The map keeps a fixed height so Leaflet sizes
   correctly the moment it's revealed.
   M13.X-ECHO-MODAL-REDESIGN (ADR-236) — the section is now the designer
   "map card": a sunken bordered card with an icon + title + description
   head and the preview below. */
.echo-location__precision {
  margin-top: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-on-light);
  background: var(--cream-sunken);
  overflow: hidden;
  animation: echoPrecisionIn 0.24s ease;
}
.echo-location__precision[hidden] {
  display: none;
}
@keyframes echoPrecisionIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
/* Card head title — Cormorant italic voice (ADR-236); shared by the GPS
   "Placed at random within" title and the search-path scatter/exact note. */
/* Map-card head label — Manrope micro-caps with the field-label colour
   token (--label-color, same as SONG (OPTIONAL)). Petřin pokyn 2026-06-04:
   „žádný Cormorant, stejný barevný token jako názvy fieldů" — size/weight
   stay as the micro-caps were, only the colour follows the labels.
   Supersedes the ADR-236 Cormorant italic title (nečitelný). */
.echo-location__precision-title,
.echo-location__scatter-note {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-3xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--label-color);
}
.echo-location__levels {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.echo-location__scatter-note[hidden] {
  display: none;
}
/* Level hint sits INLINE next to the radio label, in parentheses — keeps the
   muted italic hint font (not the radio's), saves the second line (Petřin
   pokyn 2026-06-03). Overrides .field__hint--inline's display:block. */
.echo-location__level-hint {
  display: inline;
  margin-top: 0;
  margin-left: 0.3rem;
  color: var(--text-muted);
}
.echo-location__map {
  /* ADR-236 map card: the preview bleeds edge-to-edge under a hairline
     (was an inset rounded box before the card treatment). */
  width: 100%;
  height: 132px;
  border-radius: 0;
  overflow: hidden;
  border: none;
  border-top: 1px solid var(--border-on-light);
  background: color-mix(in srgb, var(--text-dark) 4%, transparent);
}
.echo-location__map .leaflet-control-attribution {
  display: none;
}
/* Precision header — ADR-236 map-card head: teal pin icon + text column
   (title + description) + the circular collapse chevron pinned top-right
   (canonical venue-card map-toggle behaviour). The chevron folds the preview
   body; the chosen location is unaffected (Petřin pokyn 2026-06-03). */
.echo-location__precision-head {
  display: flex;
  align-items: flex-start; /* chevron pins top-right even if the label wraps */
  gap: 10px;
  padding: 11px 13px;
}
.echo-location__precision-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin-top: 2px;
  display: grid;
  place-items: center;
  color: var(--accent-turquoise);
}
.echo-location__precision-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.echo-location__precision-desc {
  margin: 0;
  font-size: var(--fs-2xs);
  font-weight: 500;
  line-height: 1.35;
  /* Hint voice = --text-muted; --text-light-muted reads too dim on Noir
     (Petřin pokyn 2026-06-03 — same fix as .rating-dialog__howto). */
  color: var(--text-muted);
}
.echo-location__precision-toggle {
  flex: 0 0 auto;
  margin-left: auto; /* keep the chevron top-right even when the label is hidden */
}
.echo-location__precision-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
/* GPS privacy radios keep an inset; the map preview below bleeds full-width
   (designer mockup: flush edge-to-edge under a hairline). */
.echo-location__precision-body .echo-location__levels {
  padding: 0 13px;
}
.echo-location__precision.is-collapsed .echo-location__precision-body {
  display: none;
}

.venue-card__name-override {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.field__label-sub {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.venue-card__name-override input {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border: 1px solid rgba(238, 240, 250, 0.18);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.7);
}

/* ---- Add details collapsible ---- */
/* M12 Noir PR6-A — accordion accents on the canonical teal (matches the
   .accordion primitive's teal chevron); was violet (online-world colour). */
.event-create__more {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.22);
  border-radius: var(--radius-md);
  padding: 0.2rem 0.8rem;
  background: rgba(var(--cream-surface-rgb), 0.45);
}

.event-create__more-summary {
  cursor: pointer;
  padding: 0.65rem 0.2rem;
  font-weight: 600;
  color: var(--accent-turquoise);
  font-size: 1rem;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Larger chevron — Petřin postřeh 2026-05-17: original 0.9rem ▸ was
 * nearly invisible on the cream background. Bumped to 1.25rem, hosted
 * in a tinted pill so the affordance reads as a real toggle, not a
 * stray punctuation glyph. */
.event-create__more-summary::before {
  content: "▾";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.55rem;
  height: 1.55rem;
  border-radius: 50%;
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  color: var(--accent-turquoise);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  transition: transform 200ms ease, background 200ms ease;
}

.event-create__more:not([open]) > .event-create__more-summary::before {
  transform: rotate(-90deg);
}

.event-create__more[open] > .event-create__more-summary::before {
  background: rgba(var(--accent-turquoise-rgb), 0.25);
}

.event-create__more-summary:hover::before {
  background: rgba(var(--accent-turquoise-rgb), 0.30);
}

.event-create__more-summary::-webkit-details-marker {
  display: none;
}

.event-create__more-body {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  padding: 0.4rem 0 0.6rem;
}

/* ---- Discussion links — chip field (M13.X-LINK-PILLS) ----
   Mechanika + vizuál = profile Social-links canon (.contacts-input grid +
   .contacts-chip + .btn-link-pill — ui-standards §2). This block only adds
   what the event modal needs on top: spacing, the input skin (matches the
   modal's other fields) and the inline error line. */
.link-chips {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  margin-top: 0.4rem;
}

.link-chips .contacts-input__field {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border: 1px solid rgba(238, 240, 250, 0.18);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.78);
  color: var(--text-dark);
}

/* The Add pill reads "+ Add link" — slightly tighter than the profile's
   "+ Add Social Link" so it sits flush next to the input. */
.link-chips .btn-link-pill {
  padding: 0.42rem 0.85rem;
  font-size: 0.88rem;
}

/* Empty chip row must not reserve blank space under the input
   (same call as the old row list — Petřin pokyn 2026-05-26). */
.link-chips .contacts-rows {
  margin-bottom: 0;
}
.link-chips .contacts-rows:empty {
  display: none;
}

.link-chips__error {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--danger-text);
}
.link-chips__error[hidden] {
  display: none;
}

.event-create__cover-picker {
  margin-top: 0.5rem;
  min-height: 1rem;
}

/* ---- Openverse photo picker (Bundle D.3 / ADR-099) ----
   Mounts inside the cover-image fieldset of the event-create modal.
   Subtle Frost 2-corner atmospheric wash mirrors the rest of the modal
   so the picker reads as part of the same surface, not a foreign panel. */
.event-create__photo-picker {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.7rem 0.75rem;
  border-radius: var(--radius-md);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.20);
  /* M12 Noir PR6-A — base fill = dark dialog surface (was a 55% light fill,
     a Frost leftover that glowed as a pale panel on the midnight dialog). */
  background:
    radial-gradient(circle at 0% 0%, var(--frost-wash-teal), transparent 65%),
    radial-gradient(circle at 100% 100%, var(--frost-wash-plum), transparent 65%),
    rgba(var(--cream-surface-rgb), 0.55);
}

.photo-picker__search {
  display: flex;
}

.photo-picker__input {
  flex: 1 1 auto;
  font: inherit;
  padding: 0.5rem 0.7rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.30);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.85);
  color: var(--text-dark);
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.photo-picker__input:focus-visible {
  outline: none;
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 2px rgba(var(--accent-turquoise-rgb), 0.20);
}

.photo-picker__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.45rem;
}

@media (max-width: 540px) {
  .photo-picker__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.photo-picker__tile {
  position: relative;
  appearance: none;
  cursor: pointer;
  padding: 0;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  background: rgba(238, 240, 250, 0.04);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  transition: border-color 160ms ease, transform 160ms ease,
              opacity 220ms ease, box-shadow 200ms ease;
}

.photo-picker__tile:hover,
.photo-picker__tile:focus-visible {
  outline: none;
  border-color: var(--accent-turquoise);
  transform: translateY(-1px);
  box-shadow: var(--shadow-card);
}

.photo-picker__thumb {
  display: block;
  width: 100%;
  height: 100%;
  /* contain (not cover) so the whole photo is visible — no cropped
     sides (Petřin pokyn 2026-05-23). Letterbox fills with the tile's
     own muted background. */
  object-fit: contain;
}

/* Shown in place of a thumbnail whose upstream host 404'd or the browser
   blocked (Firefox OpaqueResponseBlocking). Keeps the tile a stable,
   selectable part of the grid instead of letting it vanish. */
.photo-picker__thumb-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-size: 1.5rem;
  color: var(--text-muted);
  background: rgba(238, 240, 250, 0.04);
}

.photo-picker__check {
  position: absolute;
  top: 0.3rem;
  right: 0.3rem;
  width: 1.4rem;
  height: 1.4rem;
  display: none;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  font-weight: 700;
  font-size: 0.9rem;
  box-shadow: 0 2px 6px rgba(var(--accent-turquoise-rgb), 0.45);
}

.photo-picker__tile.is-selected {
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 1px var(--accent-turquoise);
}

.photo-picker__tile.is-selected .photo-picker__check {
  display: flex;
}

.photo-picker__tile.is-dimmed {
  opacity: 0.32;
}

.photo-picker__tile.is-dimmed:hover,
.photo-picker__tile.is-dimmed:focus-visible {
  opacity: 0.85;
}

.photo-picker__status {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.4;
}

.photo-picker__status.is-error {
  color: var(--danger-text);
}

.photo-picker__byline {
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.5;
  padding-top: 0.35rem;
  border-top: 1px dashed rgba(var(--accent-turquoise-rgb), 0.22);
}

.photo-picker__byline-text {
  color: var(--text-dark);
}

.photo-picker__byline-link {
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.photo-picker__byline-link:hover,
.photo-picker__byline-link:focus-visible {
  color: var(--accent-turquoise-bright);
}

.photo-picker__clear {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 0.82rem;
  color: var(--danger-text);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.photo-picker__clear:hover,
.photo-picker__clear:focus-visible {
  outline: none;
  color: var(--danger-text);
}

/* ---- Submit row ---- */
.event-create__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 0.4rem;
}

/* M15 [UX] (Petřin podnět 2026-06-06) — desktop sticky footer for the long
   create/edit forms (concert / online / landmark / tour). These dialogs
   scroll as a whole, so Cancel + Save used to sit below the fold until the
   user scrolled all the way down. Mirror the echo composer's canonical
   recipe (#pin-form > .form-actions, AI-S1-M11D-006): pin the row to the
   scrollport bottom. Negative side margins bleed the row over the dialog's
   1.4rem side padding so the surface spans edge-to-edge; the dialog's
   bottom padding moved into this row's padding (see .event-create-dialog)
   so the pinned and the scrolled-to-end positions coincide (no jump).
   z-index sits ABOVE the venue mini-map's Leaflet panes (200–700,
   uncontained — see the dropdown fix at .photon-search-box__results) but
   BELOW that dropdown (1000) so picking a venue still paints over the
   footer. Surface = near-opaque admin-band recipe (.admin-pane__band,
   ADR-263), NOT a fade: content genuinely slides UNDER the row here
   (pin-form clips its scroller above the footer), and both the cream-fade
   and a mobile-style glass gradient let the light venue mini-map shine
   through (Petřiny screenshoty 2026-06-06 — „tlačítka musí být vidět").
   92% surface + blur hides what passes beneath; hairline marks the edge.
   Mobile keeps its own bar (ID-scoped + later in cascade). */
.event-create-dialog .event-create__actions {
  position: sticky;
  bottom: 0;
  z-index: 900;
  margin: 0.4rem -1.4rem 0;
  padding: 0.7rem 1.4rem 1.3rem;
  background: rgba(var(--cream-surface-rgb), 0.92);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--border-on-light);
}

@media (max-width: 540px) {
  .event-create__actions {
    flex-direction: column-reverse;
  }
  .event-create__actions button {
    width: 100%;
  }
}


/* ---- Tour chips list + inline new-tour form (Bundle D Phase 1) ---- */
.event-create__tour-field {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}

.event-create__tour-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Tour chips — Petřin pokyn 2026-05-17 post-test-2:
   • All chips (existing tours + Standalone) share the same violet
     hero gradient by default — they read uniformly as primary chips.
   • Only the SELECTED chip changes style — to a DARKER violet so it
     stands out clearly from the unselected siblings. Unselected chips
     keep their default style regardless of which one is active.
   • .is-add-new = dashed turquoise admin affordance (different gesture).
   • .is-standalone NO LONGER has special muted styling — Petra wants
     Standalone to look like a normal tour chip. */
/* M12 (Petřin pokyn 2026-05-26) — tour chips were solid bright-violet gradient
   pill-BUTTONS with white text (ui-standards §1.1/§2 "no violet buttons" + §4
   HARD RULE 11). Now mirror the online "What kind?" subcategory chips EXACTLY
   (Petřin reference — "v online eventech už máme čipy") so tour selection +
   subcategory selection read as ONE chip system: idle = quiet tinted lavender
   on muted text, selected = solid violet + dark ink. Values intentionally
   identical to `.online-event-create__subcategory-chip` below. */
.event-create__tour-chip {
  appearance: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill, 999px);
  background: rgba(var(--accent-violet-rgb), 0.06);
  border: 1px solid rgba(var(--accent-violet-rgb), 0.20);
  color: var(--text-muted);
  font: inherit;
  font-weight: 500;
  font-size: 0.85rem;
  letter-spacing: 0.01em;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}

.event-create__tour-chip:hover,
.event-create__tour-chip:focus-visible {
  outline: none;
  background: rgba(var(--accent-violet-rgb), 0.12);
  color: var(--accent-violet-soft);
  border-color: rgba(var(--accent-violet-rgb), 0.35);
}

.event-create__tour-chip.is-active {
  /* Selected = solid violet + dark ink — identical to the subcategory chip's
     active state (HARD RULE 11: dark text on the bright violet fill). */
  background: var(--accent-violet);
  color: var(--text-on-accent);
  border-color: var(--accent-violet-soft);
  box-shadow: 0 4px 12px rgba(var(--accent-violet-rgb), 0.28);
}

.event-create__tour-chip.is-active:hover,
.event-create__tour-chip.is-active:focus-visible {
  background: var(--accent-violet);
  border-color: var(--accent-violet-soft);
}

.event-create__tour-chip.is-add-new {
  /* Admin affordance — dashed turquoise outline = clearly different
     gesture (creating, not picking). */
  background: transparent;
  color: var(--accent-turquoise);
  border: 1px dashed rgba(var(--accent-turquoise-rgb), 0.50);
  box-shadow: none;
  font-weight: 500;
}

.event-create__tour-chip.is-add-new:hover,
.event-create__tour-chip.is-add-new:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.10);
  color: var(--accent-turquoise);
  box-shadow: none;
}

.event-create__tour-new {
  margin-top: 0.5rem;
  padding: 0.85rem 0.95rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.30);
  border-radius: var(--radius-md);
  background: rgba(var(--cream-surface-rgb), 0.60);
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.event-create__tour-new-title {
  margin: 0 0 0.2rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.2rem;
  color: var(--accent-turquoise);
  letter-spacing: 0.01em;
}

.event-create__tour-new input {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border: 1px solid rgba(238, 240, 250, 0.18);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.75);
}

.event-create__tour-new-error {
  margin: 0;
  font-size: 0.82rem;
  color: var(--danger-text);
}

/* Head row hosts the collapse chevron on the right edge (Petřin pokyn
   2026-06-06 — no own Save/Cancel, the section publishes with the
   concert; chevron mirrors the venue map preview toggle recipe). */
.event-create__tour-new-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* "(not confirmed!)" — warning suffix on a collapsed unconfirmed
   draft (Petřin pokyn 2026-06-06 večer: varovná, jiný font, viditelná
   na tmavém pozadí). Body font (vs the Cormorant display title) +
   --danger-text = the SAME tone the error messages in this very
   section already use (proven readable on the dark form surface). */
.event-create__tour-new-title-warn {
  /* EXACT match of the canonical warning text (.field__error /
     .form-error): small absolute size, 600 weight, danger tone, body
     font — Petřin pokyn 2026-06-06 večer ("stejný font jako varovná
     hláška, malé písmo"). */
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: normal;
  color: var(--danger-text);
}

.event-create__tour-new-body {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

/* ✓ confirm row (Petřin pokyn 2026-06-06) — canonical give-stars
   .rating-step--confirm button, alone on the right edge. */
.event-create__tour-new-foot {
  display: flex;
  justify-content: flex-end;
}

/* Confirmed proposal pill "New: X" — selectable like any tour chip;
   dashed = not in DB yet (created with the concert on Publish). */
.event-create__tour-chip.is-draft {
  border-style: dashed;
}

/* ---- Tour auto-match suggestion (ADR-267) ---- */

.event-create__tour-suggest {
  margin: 0.4rem 0 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--text-dark);
}

.event-create__tour-suggest-assign {
  padding: 0.25rem 0.8rem;
  font-size: var(--fs-sm);
}

.event-create__tour-suggest-dismiss {
  padding: 0.15rem 0.45rem;
  font-size: 1rem;
  line-height: 1;
}

/* Suggestion ring on the auto-matched chip (ADR-267 R8) — eye-landing
   aid; removed on assign / dismiss / manual pick. */
.event-create__tour-chip.is-suggested {
  border-color: rgba(var(--accent-violet-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--accent-violet-rgb), 0.18);
}

/* ---- Venue description Wikipedia lookup ---- */
.event-create__description-field {
  position: relative;
}

.event-create__desc-actions {
  margin-top: 0.4rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.event-create__desc-lookup {
  appearance: none;
  cursor: pointer;
  padding: 0.4rem 0.85rem;
  font: inherit;
  font-weight: 600;
  font-size: 0.85rem;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.40);
  border-radius: var(--radius-pill, 999px);
  background: transparent;
  color: var(--accent-turquoise);
  transition: background 160ms ease, color 160ms ease;
}

.event-create__desc-lookup:hover:not(:disabled),
.event-create__desc-lookup:focus-visible:not(:disabled) {
  outline: none;
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
}

.event-create__desc-lookup:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.event-create__desc-lookup-hint {
  /* Petřin postřeh 2026-05-17 — rgba(…0.55) on the cream wash failed
     WCAG AA. Switch to the solid muted token (≈5.5:1) and bump the
     font from 0.75 → 0.85 rem so the playful "Try your luck" line is
     actually readable next to the button. */
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.4;
}

.event-create__desc-lookup-status {
  margin: 0.4rem 0 0;
  font-size: 0.82rem;
  color: var(--accent-turquoise);
}

.event-create__desc-lookup-status.is-error {
  color: var(--danger-text);
}

/* ---- Events tab: split CTA row (Concert + Online side-by-side) ---- */
/* Petřin pokyn 2026-05-17 — user picks category here, no modal chip.
   Compact sizing so the two buttons fit on a narrow desktop events
   panel column AND on mobile, without wrapping. */
.events-panel__cta-row--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.4rem;
}

/* Split-row CTA layout — panel widened to 400 px (Petřin pokyn 2026-05-17),
   so the buttons get full comfortable sizing. We only need nowrap +
   ellipsis fallback for very narrow viewports. */
.events-panel__cta-row--split .events-panel__cta-btn {
  width: 100%;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 360px) {
  .events-panel__cta-row--split .events-panel__cta-btn {
    font-size: 0.78rem;
    padding: 0.45rem 0.5rem;
  }
}

.events-panel__cta-btn--secondary {
  appearance: none;
  cursor: pointer;
  border-radius: var(--radius-pill, 999px);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.40);
  background: transparent;
  color: var(--accent-turquoise);
  font: inherit;
  transition: background 160ms ease, color 160ms ease, transform 140ms ease;
}

.events-panel__cta-btn--secondary:hover,
.events-panel__cta-btn--secondary:focus-visible {
  outline: none;
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  transform: translateY(-1px);
}

/* ---- New tour inline form: name row + description ----
   Icon column removed 2026-06-06 (ADR-267) — the field was dead (never
   persisted; tours have no icon column). */
.event-create__tour-new-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.55rem;
  align-items: end;
}

.event-create__tour-new textarea {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border: 1px solid rgba(238, 240, 250, 0.18);
  border-radius: var(--radius-sm);
  background: rgba(var(--cream-surface-rgb), 0.75);
  resize: vertical;
}

/* ---- Tour chip context menu (D.2h admin tour cancel Petřin pokyn 2026-05-17) ----
   Note: .is-standalone modifier has NO special styling — Petřin pokyn
   2026-05-17 post-test-2: Standalone should look like a normal tour
   chip (same violet hero gradient by default, same darker violet when
   selected). Removed previous muted/italic overrides. */

/* Quick test admin context menu over a tour chip — right-click only.
   Destructive action styled in --danger (per memory feedback_destructive_ctas_uniform_red). */
.event-create__tour-ctx-menu {
  position: fixed;
  z-index: 2000;
  background: var(--cream-surface);
  border: 1px solid rgba(238, 240, 250, 0.20);
  border-radius: var(--radius-md);
  box-shadow:
    0 10px 30px rgba(0, 0, 0, 0.24),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.06);
  padding: 0.3rem;
  min-width: 220px;
  display: flex;
  flex-direction: column;
}

.event-create__tour-ctx-menu-item {
  appearance: none;
  cursor: pointer;
  text-align: left;
  border: none;
  background: transparent;
  padding: 0.55rem 0.7rem;
  font: inherit;
  font-size: 0.9rem;
  border-radius: var(--radius-sm);
  color: var(--text-dark);
}

.event-create__tour-ctx-menu-item:hover,
.event-create__tour-ctx-menu-item:focus-visible {
  outline: none;
  background: rgba(var(--accent-violet-rgb), 0.10);
}

.event-create__tour-ctx-menu-item.is-destructive {
  color: var(--danger-text);
  font-weight: 600;
}

.event-create__tour-ctx-menu-item.is-destructive:hover,
.event-create__tour-ctx-menu-item.is-destructive:focus-visible {
  background: rgba(179, 38, 30, 0.10);
}

/* ---- Cover preview when a selection is locked in (Petřin pokyn
   2026-05-17 — show the actual photo, not the search box). Aspect
   ratio preserved via object-fit:contain; bounded max-height so a
   tall portrait doesn't push the whole modal off-screen. ---- */
/* Placeholder card shown when no cover is picked + user hasn't yet
   expanded the search (Petřin pokyn 2026-05-17 — passive text input
   was unclear, needs visible icon + CTA so users know it's clickable). */
/* Auto-fill provenance disclaimer — shown below description textarea
   when description was auto-filled by AI/Wikipedia. M14 unify (Petřin
   pokyn 2026-06-05): EVERY persistent AI info line in the app speaks
   ONE visual language — sparkle ✦ + whisper token --text-ai-note +
   0.8rem body font. No tinted strip, no teal border, no italic (the
   old M11 treatment read as a different component). */
.event-create__desc-autofill-note {
  margin: 0.35rem 0 0;
  padding: 0;
  font-family: var(--font-body);
  font-size: 0.8rem;
  line-height: 1.35;
  font-style: normal;
  color: var(--text-ai-note);
  background: none;
  border-left: 0;
  border-radius: 0;
}
.event-create__desc-autofill-note::before {
  content: "✦ ";
}

.photo-picker__placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 1.2rem 1rem;
  margin-bottom: 0.6rem;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg,
              rgba(var(--accent-violet-rgb), 0.10) 0%,
              rgba(var(--accent-turquoise-rgb), 0.08) 100%);
  border: 1px dashed rgba(var(--accent-violet-rgb), 0.35);
  text-align: center;
}

.photo-picker__placeholder-icon {
  font-size: 2.4rem;
  line-height: 1;
  filter: drop-shadow(0 4px 10px rgba(var(--accent-violet-rgb), 0.25));
}

.photo-picker__placeholder-title {
  margin: 0;
  font-size: 0.92rem;
  color: var(--text-muted);
}

/* M12 (Petřin pokyn 2026-05-26) — the "Find a photo" CTA now reuses the
   canonical teal `.btn-primary` (added in photoPicker.js). The old bespoke
   violet-gradient + white-text skin is removed: it was a violet button with
   white text on a bright fill (ui-standards §1.1/§2 + §4 HARD RULE 11). No
   `.photo-picker__placeholder-cta` colour rule remains so it can't override
   `.btn-primary` from later in the cascade. */

/* "✏️ Search with other keywords" link — sits below the grid for
   power-users who want a free-form search instead of the curated 5. */
.photo-picker__custom-search-toggle {
  appearance: none;
  background: none;
  border: none;
  padding: 0.4rem 0;
  margin-top: 0.4rem;
  color: var(--accent-violet-soft);
  font: inherit;
  font-size: 0.84rem;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  text-align: left;
  display: block;
}

.photo-picker__custom-search-toggle:hover,
.photo-picker__custom-search-toggle:focus-visible {
  color: var(--accent-violet-bright);
  outline: none;
}

.photo-picker__selection {
  position: relative; /* anchor for absolute-positioned remove (×) btn */
  display: flex;
  justify-content: center;
  background: rgba(238, 240, 250, 0.05);
  border: 1px solid rgba(238, 240, 250, 0.10);
  border-radius: var(--radius-md);
  padding: 0.5rem;
  margin-bottom: 0.6rem;
  min-height: 80px;
}

/* Remove (×) button — top-right of the preview. Wipes the cover so
   the picker returns to empty state. Different gesture from
   "Change" (which keeps selection while opening the picker grid). */
.photo-picker__remove {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  border: 1px solid rgba(238, 240, 250, 0.20);
  background: rgba(var(--cream-surface-rgb), 0.92);
  color: var(--text-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  transition: background 140ms ease, color 140ms ease,
              transform 140ms ease, box-shadow 160ms ease;
  z-index: 2;
}

.photo-picker__remove:hover,
.photo-picker__remove:focus-visible {
  background: var(--danger, #b54141);
  color: #fff;
  border-color: var(--danger, #b54141);
  outline: none;
  transform: scale(1.06);
  box-shadow: 0 4px 10px rgba(181, 65, 65, 0.32);
}

/* "Change" link in the byline — pure text link styling, distinct
   from the destructive × button on the preview. Underlined by
   default per memory feedback_anon_nudge_link_look. */
.photo-picker__change {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  color: var(--accent-violet-soft);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.photo-picker__change:hover,
.photo-picker__change:focus-visible {
  color: var(--accent-violet-bright);
  outline: none;
}

/* ============================================================
   M12.X-IMAGE-FRAMING (ADR-186) — focal point picker. MISSING MOCKUP:
   built from the Noir token system (no handoff mockup, scope §UI). Lives
   inside the photo-picker selection panel: the locked-in cover becomes a
   draggable editable preview + a live "how it looks on the map" crop.
   ============================================================ */
.photo-picker__focal {
  width: 100%;
}

.focal-picker__title {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 600;
  color: var(--text-dark);
  margin: 0 0 var(--space-0-5);
}

.focal-picker__hint {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: var(--lh-snug);
  margin: 0 0 var(--space-3);
}

/* Editor stage — shrink-wraps a size-capped image (NOT full-bleed) so it
   reads as a compact preview, and so the reticle's percentage maps 1:1 to
   object-position (stage box === rendered image box, no letterbox). */
.focal-picker__stage {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;         /* center the shrink-wrapped preview */
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: grab;
  touch-action: none;          /* pointermove drives the drag on touch */
  user-select: none;
  border: 1px solid var(--border-on-light);
  line-height: 0;              /* kill inline-img descender gap */
}
.focal-picker__stage:active { cursor: grabbing; }
.focal-picker__stage:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--accent-turquoise-rgb), 0.45);
}
.focal-picker__img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 180px;           /* compact preview, not a full-size photo */
  pointer-events: none;
}

/* Draggable focal reticle — turquoise ring + crosshair, dark halo so it
   reads on any photo. Centered on its point via the negative margin. */
.focal-picker__reticle {
  position: absolute;
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  border-radius: 50%;
  border: 2px solid var(--accent-turquoise);
  box-shadow:
    0 0 0 2px rgba(10, 13, 30, 0.55),
    0 2px 10px rgba(0, 0, 0, 0.45);
  background: radial-gradient(circle, transparent 38%, rgba(var(--accent-turquoise-rgb), 0.12) 40%);
  pointer-events: none;
}
.focal-picker__reticle::before,
.focal-picker__reticle::after {
  content: "";
  position: absolute;
  background: var(--accent-turquoise);
}
.focal-picker__reticle::before { left: 50%; top: 8px; bottom: 8px; width: 1.5px; margin-left: -0.75px; }
.focal-picker__reticle::after  { top: 50%; left: 8px; right: 8px; height: 1.5px; margin-top: -0.75px; }

/* Live crop preview — popup hero aspect (set inline via aspect-ratio). */
.focal-picker__preview-wrap { margin-top: var(--space-4); }
.focal-picker__preview-label {
  display: block;
  font: var(--text-eyebrow);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-light-muted);
  margin-bottom: var(--space-2);
}
.focal-picker__preview {
  width: 100%;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--border-on-light);
}
.focal-picker__preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;           /* same crop the map popup uses */
  display: block;
}

.focal-picker__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-3);
}
.focal-picker__readout {
  font-size: var(--fs-xs);
  color: var(--text-light-muted);
  font-variant-numeric: tabular-nums;
}
.focal-picker__reset {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  color: var(--accent-turquoise);
  font: inherit;
  font-size: var(--fs-sm);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.focal-picker__reset:hover,
.focal-picker__reset:focus-visible {
  color: var(--accent-turquoise-bright);
  outline: none;
}

/* Dead URL / CORS — collapse the editor, keep a quiet note (the × remove
   in the photo picker above still works). */
.focal-picker--broken .focal-picker__stage,
.focal-picker--broken .focal-picker__preview-wrap,
.focal-picker--broken .focal-picker__footer { display: none; }
.focal-picker--broken::after {
  content: "🖼️";
  display: block;
  text-align: center;
  font-size: 1.6rem;
  opacity: 0.5;
  padding: var(--space-4) 0;
}

/* ============================================================
   Online event create modal — ADR-108 Bundle E.
   Reuses the concert modal's shell styles (.event-create-dialog,
   .event-create__header, .event-create__form, .event-create__paste,
   .event-create__more, .event-create__discussion-*, etc.) so the
   Frost design language stays unified — design = same, just adapted
   to online needs (Petřin pokyn 2026-05-17 evening).

   Online-specific bits:
     • title row = input + platform badge inline (badge replaces the
       missing cover image as the visual anchor)
     • subcategory chip selector (radio group, 5 options)
   ============================================================ */

/* Title field — flex row so badge sits to the right of the input. */
.online-event-create__title-row {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
}

.online-event-create__title-input {
  flex: 1 1 auto;
  min-width: 0; /* allow input to shrink instead of overflowing */
}

/* Platform badge — sits next to the title input. Online world = azure
   (per-world rule, ui-standards §3; Petřin pokyn 2026-05-31, was iris), so
   the badge wash + hover accent are azure, not the concert-teal it inherited
   from Frost. currentColor on the SVG picks up the badge text colour. */
.online-event-create__platform-badge {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  border-radius: var(--radius-sm, 6px);
  /* ADR-255 — detected platform tints the badge (hub tones); idle/unknown
     ("web") falls to the muted grey, azure only as the no-attr fallback. */
  color: var(--platform, var(--text-muted));
  background: color-mix(in srgb, var(--platform, var(--accent-online)) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--accent-online)) 18%, transparent);
  transition: color 160ms ease, background 160ms ease,
              border-color 160ms ease;
}

.online-event-create__platform-badge:hover {
  color: var(--platform, var(--accent-online-bright));
  background: color-mix(in srgb, var(--platform, var(--accent-online)) 14%, transparent);
  border-color: color-mix(in srgb, var(--platform, var(--accent-online)) 32%, transparent);
}

.online-event-create__platform-badge-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
}

.online-event-create__platform-badge-icon svg.platform-icon {
  width: 24px;
  height: 24px;
  display: block;
  /* Mono inheritance — color picked up from parent badge text color
     (see currentColor in platformIcons.js). Override the global
     `.platform-icon { color: violet }` to honor the parent context. */
  color: currentColor;
}

/* Subcategory chip selector — pill radio group. Smaller + flatter than
   tour chips (= subcategory is a soft classification, not a primary
   entry). Hidden native radio, label is the clickable surface. */
.online-event-create__subcategory-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.4rem;
}

.online-event-create__subcategory-chip {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill, 999px);
  /* Subcategory selector = VIOLET (secondary classification, NOT the azure
     world colour) — matches the list subcategory chip. Not all online chips
     are azure (Petřin pokyn 2026-05-31). */
  background: rgba(var(--accent-violet-rgb), 0.06);
  border: 1px solid rgba(var(--accent-violet-rgb), 0.20);
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  transition: background 140ms ease, color 140ms ease,
              border-color 140ms ease, transform 140ms ease;
}

.online-event-create__subcategory-chip:hover {
  background: rgba(var(--accent-violet-rgb), 0.12);
  color: var(--accent-violet-soft);
  border-color: rgba(var(--accent-violet-rgb), 0.35);
}

.online-event-create__subcategory-chip.is-active {
  background: var(--accent-violet);
  color: var(--text-on-accent);
  border-color: var(--accent-violet-soft);
  box-shadow: 0 4px 12px rgba(var(--accent-violet-rgb), 0.28);
}

.online-event-create__subcategory-chip input[type="radio"] {
  /* Hide native radio — label is the visual + click target. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.online-event-create__subcategory-chip:focus-within {
  outline: 2px solid var(--accent-violet-soft);
  outline-offset: 2px;
}

/* ---- Online dialog: width tier + azure world-tone overrides (PR6-A) ----
   Online is now a two-column form (matching concert + landmark, mockup 11),
   so it sits on the same 720 wide tier — at 560 the two columns were cramped
   and the "What kind?" chips wrapped awkwardly (Petra's call 2026-05-26). Its
   paste box wears the online azure accent instead of the concert teal
   (Petřin pokyn 2026-05-31, was iris). */
.online-event-create-dialog {
  max-width: 720px;
}
.online-event-create-dialog .event-create__paste {
  background: linear-gradient(
    135deg,
    rgba(var(--accent-online-rgb), 0.08) 0%,
    rgba(var(--accent-online-rgb), 0.05) 100%
  );
  border-color: rgba(var(--accent-online-rgb), 0.30);
}
.online-event-create-dialog .event-create__paste-input {
  border-color: rgba(var(--accent-online-rgb), 0.30);
}
.online-event-create-dialog .event-create__paste-input:focus-visible {
  outline-color: var(--accent-online-bright);
}
.online-event-create-dialog .event-create__paste-status {
  color: var(--accent-online-bright);
}

/* ============================================================
   Online event DETAIL modal — ADR-108 Bundle E iterace 4.
   Fully separate dialog from the concert detail (`.event-detail-modal`
   stays untouched). Compact + platform-first: no cover, platform icon
   next to title, large primary CTA, popup-scoped 12 px status dot.
   ============================================================ */

.online-event-detail-dialog {
  /* M12 PR6 Dávka B — adopt the canonical dialog shell (ADR-179), identical to
     .event-detail-modal so the two detail dialogs read as siblings. WAS broken:
     it referenced an UNDEFINED token whose literal fallback was a near-white
     card, on which the Noir near-white --text-dark body text was invisible.
     Now: azure hairline + 2-corner atmospheric mesh + atmospheric shadow
     trio. Online world = azure primary (Petřin pokyn 2026-05-31, was teal
     copied from concert); violet bottom-left stays as shared night atmosphere,
     same as .event-detail-modal. Keeps its own width/padding rhythm. */
  border: 1px solid rgba(var(--accent-online-rgb), 0.15);
  padding: 1.6rem 1.5rem 1.4rem;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-online-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-online-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-online-rgb), 0.04);
  max-width: 520px;
  width: calc(100vw - 2rem);
  max-height: 90vh;
  overflow-y: auto;
  position: relative;
}

.online-event-detail-dialog::backdrop {
  /* Canonical backdrop — night-sky radial + 6px blur (was rgba violet + 2px). */
  background:
    radial-gradient(circle at 50% 0%, rgba(var(--accent-violet-rgb), 0.12), transparent 60%),
    rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* Position override pro online-event-detail-dialog (unified styling
   per .dialog-close group — viz Petřin pokyn 2026-05-19). Posun mírně
   doprava aby seděl na padded okraj 1.5rem. */
.online-event-detail-dialog__close {
  top: 0.6rem;
  right: 0.7rem;
}

.online-event-detail-dialog__body {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.online-event-detail__loading,
.online-event-detail__error {
  color: var(--text-muted);
  text-align: center;
  padding: 1.2rem 0;
}

/* ── Header row: platform icon + title + status dot ────────────── */
.online-event-detail__header {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  margin-top: 0.2rem;
}

.online-event-detail__platform-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sm);
  /* ADR-255 — tile wears the PLATFORM tone (hub recipe), not blanket azure;
     azure stays the fallback for "web"/unknown so the modal chrome (edge,
     CTA, chips) keeps owning the online world colour. */
  color: var(--platform, var(--accent-online-bright));
  background: color-mix(in srgb, var(--platform, var(--accent-online)) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--accent-online)) 26%, transparent);
}
.online-event-detail__platform-icon svg.platform-icon {
  width: 26px;
  height: 26px;
  display: block;
  /* Override the global `.platform-icon { color: violet }` so the icon
     picks up the parent's teal color (= Frost identity in the popup
     context, NOT the public-profile violet default). */
  color: currentColor;
}

.online-event-detail__title-wrap {
  flex: 1 1 auto;
  min-width: 0;
}

.online-event-detail__title {
  /* M12 PR6 Dávka B — canonical dialog title (ADR-179 shell): var(--font-display)
     token + 0.025em tracking / 1.15 line-height, matching .event-detail__title +
     #pin-form h2. Big content-modal tier (M13.X-TITLE-TIERS). */
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-modal-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  margin: 0;
  font-style: italic;
  color: var(--text-dark);
}

/* Popup status dot — Petřin pokyn 2026-05-18: reuse THE EXACT SAME
   `.feed-list__status-dot--{state}` classes as the list row so colors,
   halo + live-pulse stay 1:1 v sync. Tento `--in-meta` modifier jen
   přidá spacing pro meta-line context, žádné color rules tu nejsou. */
.online-event-detail__status-dot--in-meta {
  margin-right: 0.45rem;
  flex-shrink: 0;
  position: relative;
  top: -1px;
}

/* ── Meta row: dot + muted single line ─────────────────────────── */
.online-event-detail__meta {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-muted);
  letter-spacing: 0.01em;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.online-event-detail__meta-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* ── Description ───────────────────────────────────────────────── */
.online-event-detail__description {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--text-dark);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Primary CTA — big, full-width, per-platform copy ─────────── */
.online-event-detail__primary-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.85rem 1.2rem;
  border-radius: var(--radius-md, 10px);
  /* M12 PR6 Dávka B — canonical vertical-gradient primary (mockup 08), painted
     in the online world colour AZURE (Petřin pokyn 2026-05-31, was teal copied
     from concert) so it matches the azure popup RSVP + azure card "Open on …"
     CTA. Supersedes the pre-Noir diagonal gradient AND m12-event-detail-modal.md
     §3B "flat solid + white text". */
  background: linear-gradient(180deg, var(--accent-online-bright) 0%, var(--accent-online-deep) 100%);
  color: var(--text-on-accent);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: 0.01em;
  text-decoration: none;
  box-shadow: 0 6px 18px rgba(var(--accent-online-rgb), 0.30);
  transition: transform 140ms ease, box-shadow 160ms ease;
  margin: 0.2rem 0 0.2rem;
}
.online-event-detail__primary-cta:hover,
.online-event-detail__primary-cta:focus-visible {
  outline: none;
  box-shadow: 0 10px 26px rgba(var(--accent-online-rgb), 0.42);
}
@media (hover: hover) and (pointer: fine) {
  .online-event-detail__primary-cta:hover {
    transform: translateY(-1px);
  }
}

.online-event-detail__primary-cta-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
}
/* Override the global `.platform-icon { color: var(--accent-violet-soft) }` rule
   so the CTA glyph matches the CTA's DARK text. M12 PR6 Dávka B — was a light
   rgba (paired with the old white CTA text); now var(--text-on-accent) to sit
   on the bright azure gradient like the canonical .btn-primary label. */
.online-event-detail__primary-cta-icon svg.platform-icon {
  width: 22px;
  height: 22px;
  display: block;
  color: var(--text-on-accent);
}

.online-event-detail__primary-cta-label {
  font-weight: 700;
}

.online-event-detail__primary-cta-arrow {
  font-size: 1.1em;
  margin-left: 0.2rem;
  opacity: 0.85;
}

/* ── RSVP block (renderRsvp injects its own structure) ─────────── */
.online-event-detail__rsvp {
  display: block;
  margin-top: 0.4rem;
}

/* ── Discussion links — multi-platform inline list ─────────────── */
.online-event-detail__discussion {
  /* M12 PR6 Dávka B (m12-event-detail-modal.md §3C) — tokenize the divider;
     raw rgba(0,0,0,.06) is invisible on the dark Noir surface. */
  border-top: 1px solid var(--border-on-light);
  padding-top: 0.85rem;
  margin-top: 0.3rem;
}

.online-event-detail__discussion[hidden] {
  display: none;
}

.online-event-detail__discussion-heading {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-online-bright);
}

.online-event-detail__discussion-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.online-event-detail__discussion-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.8rem;
  border-radius: var(--radius-pill, 999px);
  /* ADR-255 — pill wash/border/hover = platform tone (was blanket azure);
     azure remains the fallback outside [data-platform]. */
  background: color-mix(in srgb, var(--platform, var(--accent-online)) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--accent-online)) 18%, transparent);
  color: var(--text-muted);
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
  transition: color 140ms ease, background 140ms ease,
              border-color 140ms ease;
}
.online-event-detail__discussion-link:hover,
.online-event-detail__discussion-link:focus-visible {
  outline: none;
  color: var(--platform, var(--accent-online-bright));
  background: color-mix(in srgb, var(--platform, var(--accent-online)) 12%, transparent);
  border-color: color-mix(in srgb, var(--platform, var(--accent-online)) 35%, transparent);
}

.online-event-detail__discussion-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  /* ADR-255 — glyph always wears the platform tone (label stays muted). */
  color: var(--platform, currentColor);
}
.online-event-detail__discussion-icon svg.platform-icon {
  width: 18px;
  height: 18px;
  display: block;
  color: currentColor;
}

.online-event-detail__discussion-share {
  /* "Share on r/tarjaturunen" CTA — same shape as discussion links
     but slightly accented to read as a call-to-action rather than a
     simple listing. */
  margin-top: 0.3rem;
  font-weight: 600;
}

/* (Removed ADR-255: dead `.feed-list__icon--platform` block — no JS has
   built that class since the M12 Noir icon-tiles; the only reference was a
   "deliberately NOT adding" comment in eventsPanel.js.) */

/* =====================================================================
   M11.5 (ADR-114) — Test Data Lifecycle
   Gold test-data hue (--warning) — Petřin pokyn 2026-05-24: test-data
   surfaces moved off danger red onto the gold flag colour so the flask
   toggle, this create-modal row, and the popup TEST chip all read as one
   "test data" family (and stay distinct from real destructive red). Used:
     - .test-data-field      Create modals' "Mark as test data" row
                             (4 modals: pin, concert, online, sigplace)
     - .test-data-pill       Inline TEST chip rendered by Stage 5 across
                             pin/event popups + panel rows (helper:
                             frontend/js/testDataBadge.js, not in Stage 2).
   ===================================================================== */

.test-data-field {
  /* Builds on .field--checkbox spirit (44px tap target, row layout).
     The surrounding tinted block makes the toggle scannable inside a
     busy Add Details accordion without dominating it. */
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 0.6rem;
  margin-top: 0.75rem;
  padding: 0.6rem 0.75rem;
  background: rgba(var(--warning-rgb), 0.10);
  border: 1px solid rgba(var(--warning-rgb), 0.30);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.875rem;
  line-height: 1.4;
  color: var(--text-dark);
}
.test-data-field input[type="checkbox"] {
  margin-top: 0.15rem;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  accent-color: var(--warning);
}
/* M13.X (Petřin pokyn 2026-06-03) — locked state when joining a gathering:
   the row is informative-only (the Echo inherits the gathering's test/real
   side), so it reads as non-interactive with a tooltip explaining the lock. */
.test-data-field--locked {
  cursor: not-allowed;
  opacity: 0.8;
}
.test-data-field--locked input[type="checkbox"] {
  cursor: not-allowed;
}
.test-data-field__text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.test-data-field__label {
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--warning);
}
.test-data-field__warning {
  font-size: 0.78rem;
  /* Test-data sub-copy = gold (--warning) per designer directive (Petřin
     pokyn 2026-05-25) — was --text-muted, which reads near-white in Noir.
     Same gold family as the label + flask; smaller italic gives hierarchy.
     Shared by all 4 create-modal test-data rows (pin/concert/online/sigplace). */
  color: var(--warning);
  font-style: italic;
}

.test-data-pill {
  /* Per Petřin pokyn 2026-05-18 — text-only fix napříč 8 placement
     (chip outer dimensions zachovány):
       - font-size 0.68rem → 0.78rem (≈ 12.5px text)
       - removed font-variant-caps: all-small-caps (already-uppercase
         "TEST" rendered as small-caps glyphs = optically smaller)
       - removed opacity 0.92 (full opacity → AAA contrast)
       - font-weight 700 → 800
     Padding + bg + border zachovány na původních hodnotách (= chip
     outer size stays). */
  display: inline-block;
  /* Petřin pokyn 2026-05-25 — v popupech/hlavičkách byl TEST moc natěsno
     k názvu lokality; větší odstup (0.4 → 0.85rem). Feed-list má vlastní
     margin override, takže záložky to neovlivní. */
  margin-left: 0.85rem;
  padding: 0.1rem 0.5rem;
  border-radius: var(--radius-pill);
  background: rgba(var(--warning-rgb), 0.10);
  color: var(--warning);
  border: 1px solid rgba(var(--warning-rgb), 0.32);
  /* Petřin pokyn 2026-05-25 — TEST musí být STEJNĚ velký + STEJNÝ font všude.
     V popupu titulek (.pin-popup__kotva-name) je Cormorant italic, takže čip
     bez explicitního fontu dědil kurzívu + větší serif. Pevně body sans,
     nekurzíva, 0.56rem / 0.10em / 700 = identické se záložkovým .chip baseline. */
  font-family: var(--font-body);
  font-style: normal;
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: middle;
  cursor: help;
}

/* M14 AI-BADGE (EU AI Act disclosure) — canonical AI provenance mark,
   rendered via uiUtils.aiBadgeHtml() next to AI-generated descriptions
   (event / POI / tour). Per Petřin pokyn 2026-06-05: a quiet soft-white
   sparkle (✦), NOT a chip/pill — muted token, no border, no fill, so it
   never competes with the description it annotates. The two-variant
   tooltip (errors / edited) carries the meaning; aria-label mirrors it
   for screen readers. */
.ai-badge {
  display: inline-block;
  margin-left: 0.35rem;
  /* (see popup-scoped block rule below for the standalone-line variant) */
  /* Dedicated whisper token — HARD RULE 14 výjimka (Petřin pokyn
     2026-06-05): present but never competing with the content. */
  color: var(--text-ai-note);
  font-family: var(--font-body);
  font-style: normal;
  /* Fixed small size = validation/warning message scale (.field__error
     0.8rem) per Petřin pokyn 2026-06-05 — NEVER scales with the text. */
  font-size: 0.8rem;
  line-height: 1.35;
  vertical-align: baseline;
}

/* =====================================================================
   M11.5 Stage 6 (ADR-118 R5) — Test Mode user toggle
   Sibling of #hero-strip + .top-right-cluster (NOT inside either —
   zero-touch on existing containers). Positioned absolutely.

   Color palette mirrors `.test-data-field` (create-modal checkbox) +
   `.test-data-pill` (popup TEST chip) — per Petřin pokyn 2026-05-24 the
   whole test-data family moved off danger red onto the gold flag colour
   (--warning), so toggle + create-modal row + popup chip read as one
   "test data" family. OFF = subtle gold tint + gold text; ON = solid gold
   fill + dark text (white-on-gold would fail WCAG AA) so it still reads
   as a saturated "can't miss it" CTA (per Petřin "ať to nejde přehlédnout").

   Position desktop ≥1024px: between hero-strip (centered) and
   .top-right-cluster (right-anchored). Mobile/narrow desktop: below
   hero, still centered.
   ===================================================================== */

.test-mode-toggle {
  position: absolute;
  /* Desktop default — vertically aligned with hero-strip top; anchored
     well clear of the cluster. Petřin 3rd screenshot ještě collision na
     26rem → bumped na 30rem; spárováno s drop-below threshold 1440px
     (cluster + hero potřebují ≈ 30rem aby toggle se vešel). */
  top: max(0.9rem, env(safe-area-inset-top));
  right: max(32rem, calc(env(safe-area-inset-right, 0) + 32rem));
  z-index: 1000;

  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  /* Per Petřin "udělej to celé trochu menší" — padding + font shrunken
     so toggle ends ≈ 7rem wide (down from ≈ 10rem). */
  padding: 0.35rem 0.75rem 0.35rem 0.6rem;
  border-radius: var(--radius-sm);
  /* Per Petřin pokyn 2026-05-18 #6 — toggle byl "moc výrazný",
     "předtím byl víc průsvitný". Sníženo:
       - cream layer 0.95 → 0.72 (víc průsvitné; backdrop blur 8 → 12px
         tak aby map tiles za toggle nerušily)
       - red tint 0.13 → 0.08
       - border 1.5px / 0.55 → 1px / 0.32 (parita s .test-data-pill chip)
       - box-shadow měkčí. Pozice + velikost + UPPERCASE zachovány. */
  background:
    linear-gradient(rgba(var(--warning-rgb), 0.08), rgba(var(--warning-rgb), 0.08)),
    rgba(var(--cream-surface-rgb), 0.72);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(var(--warning-rgb), 0.32);
  color: var(--warning);
  box-shadow:
    0 2px 6px rgba(var(--warning-rgb), 0.12),
    inset 0 1px 0 rgba(238, 240, 250, 0.40);

  font-family: var(--font-body, inherit);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  line-height: 1;

  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* No transition on background/border/box-shadow per Petřin pokyn
     2026-05-18 evening — "blikání ti těžko vyfofím". Post-reload
     renderState() flips data-state from the HTML default "off" to the
     localStorage state, and any 0.18s animation on color/bg/shadow
     was visible "blikání" through the reload. Kept only transform
     transition for :hover/:active press feedback (= user-initiated,
     not state-driven). */
  transition: transform 0.18s ease;
}

/* Press feedback for touch + click (F-MOB-011 Stage 7 review). Desktop
   variant uses pure scale; the centered mobile/cluster-crowded variants
   below redeclare :active with their compose-required transform so the
   translateX centering isn't lost mid-press. */
.test-mode-toggle:active {
  transform: scale(0.97);
}

.test-mode-toggle[hidden] {
  display: none;
}

.test-mode-toggle__check {
  /* Petřin pokyn 2026-05-18 evening: the prior hollow circle never
     visibly tracked state — a dot that doesn't reflect ON vs OFF is
     pointless. Replaced with a square checkbox: empty outline when OFF,
     filled white box with red ✓ when ON. Obvious state change at a
     glance. Border-radius 3px reads as a checkbox, not an indicator dot.
     The ✓ glyph is always present in the DOM but hidden via
     `color: transparent` in the OFF state so the box metrics stay
     stable across the flip (no glyph jiggle). */
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 3px;
  background: transparent;
  border: 1.5px solid var(--warning);
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 800;
  line-height: 1;
  color: transparent;
  /* No transitions on the check box — see parent .test-mode-toggle
     comment. Post-reload state flip would otherwise animate the ✓
     fade-in / box color change. */
}
.test-mode-toggle__check::before {
  content: "\2713"; /* ✓ */
}

.test-mode-toggle__label {
  line-height: 1;
}

@media (hover: hover) and (pointer: fine) {
  .test-mode-toggle:hover {
    background:
      linear-gradient(rgba(var(--warning-rgb), 0.16), rgba(var(--warning-rgb), 0.16)),
      rgba(var(--cream-surface-rgb), 0.85);
    border-color: rgba(var(--warning-rgb), 0.50);
    box-shadow:
      0 3px 9px rgba(var(--warning-rgb), 0.20),
      inset 0 1px 0 rgba(238, 240, 250, 0.45);
    transform: translateY(-1px);
  }
}

.test-mode-toggle:focus-visible {
  outline: 2px solid rgba(var(--warning-rgb), 0.75);
  outline-offset: 2px;
}

/* ON state — solid gold fill + dark text. The toggle leaves the "subtle
   gold surface" of OFF and becomes a saturated CTA. Text flips to the dark
   navy surface token because white-on-gold fails WCAG AA contrast; dark
   text on gold reads cleanly while still feeling like a bold filled chip. */
.test-mode-toggle[data-state="on"] {
  background: var(--warning);
  border-color: var(--warning);
  color: var(--cream-surface);
  box-shadow:
    0 6px 18px rgba(var(--warning-rgb), 0.45),
    inset 0 1px 0 rgba(238, 240, 250, 0.25);
}
.test-mode-toggle[data-state="on"] .test-mode-toggle__check {
  /* Dark-filled box + gold ✓ glyph. Reveals the check character that
     was rendered (transparent) in the OFF state — no DOM/content swap,
     just color flip → no layout shift. */
  background: var(--cream-surface);
  border-color: var(--cream-surface);
  color: var(--warning);
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.30);
}
@media (hover: hover) and (pointer: fine) {
  .test-mode-toggle[data-state="on"]:hover {
    background:
      linear-gradient(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.12)),
      var(--warning);
    border-color: var(--accent-copper-bright);
  }
}

/* Below 1440px the cluster + hero crowd the gap (cluster ≈ 24rem +
   hero ≈ 22rem + toggle ≈ 8rem + breathing rooms ≈ 6rem ≈ 60rem ≈
   1440px). Below that, drop the toggle below the hero (colour unchanged). */
@media (max-width: 1439px) {
  .test-mode-toggle {
    top: calc(max(0.9rem, env(safe-area-inset-top)) + 5.5rem);
    right: auto;
    left: 50%;
    transform: translateX(-50%);
  }
  @media (hover: hover) and (pointer: fine) {
    .test-mode-toggle:hover {
      transform: translateX(-50%) translateY(-1px);
    }
  }
  /* Compose press feedback with the centering transform (F-MOB-011). */
  .test-mode-toggle:active {
    transform: translateX(-50%) scale(0.97);
  }
}

@media (max-width: 767px) {
  /* Mobile — Petřin iteration #3 "ještě o polovinu blíže". 4rem →
     3.5rem → 3.25rem (gap from collapsed hero ~0.25rem ≈ 4px; tight
     but still a visible breathing line). Pre-collapse full hero will
     briefly overlap for ~1s before first interaction triggers
     mobile-collapsed body class (P-027).

     F-MOB-001 (Stage 7): vertical padding bumped 0.3rem → 0.55rem so the
     tap target clears WCAG/Apple HIG 44×44 minimum. Horizontal padding
     and font/dot/letter-spacing untouched — Petřin klidový vizuál (color,
     form, weight) preserved per "možná donutím vrátit vizuál" caveat. */
  .test-mode-toggle {
    top: calc(max(0.9rem, env(safe-area-inset-top)) + 3.25rem);
    padding: 0.55rem 0.7rem 0.55rem 0.55rem;
    gap: 0.4rem;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
  }
  .test-mode-toggle__check {
    width: 0.85rem;
    height: 0.85rem;
    font-size: 0.72rem;
    border-width: 1.3px;
  }
}

/* ================================================================
   M11.X-TOUR-MGMT Stage A (ADR-134) — Tour detail + form modals.
   Chrome (backdrop, close button, surface elevation) inherits from
   `.event-detail-modal` + `.event-create-dialog`. The blocks below
   only add tour-specific layout for body content.
   ================================================================ */

/* --- Tours table title link (button reset → link-look) ------------
   Reuses the same turquoise underlined look as `.admin-row__title`
   so the Tours tab feels consistent with Concerts/Online tabs. */
.admin-row__title-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
  color: var(--accent-turquoise);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-align: left;
}
.admin-row__title-link:hover,
.admin-row__title-link:focus-visible {
  color: var(--accent-turquoise-bright);
  outline: none;
}

/* --- Tour detail modal ------------------------------------------- */
.tour-detail-dialog {
  /* The Frost detail-modal default max-width is tuned for events; tour
     detail (description + concerts list) reads better a touch wider. */
  max-width: min(680px, 92vw);
}
.tour-detail {
  /* M12 Noir PR6-A — no inner padding; the .event-detail-modal shell
     already pads the panel. The old clamp() double-padded the content so
     this modal sat more inset than every other detail modal. */
  padding: 0;
  color: var(--text-dark);
}
.tour-detail__header {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 1rem;
}
.tour-detail__header-text {
  flex: 1 1 auto;
  min-width: 0;
}
.tour-detail__name {
  font-family: "Cormorant Garamond", "Cormorant", Georgia, serif;
  font-weight: 700;
  font-size: clamp(1.7rem, 3.2vw, 2.2rem);
  letter-spacing: 0.02em;
  line-height: 1.15;
  margin: 0 0 0.25rem;
  /* M12 Noir PR6-A — white moon title (was an undefined Frost token falling
     back to a hardcoded plum hex; invisible-failure landmine on dark). */
  color: var(--text-dark);
}
.tour-detail__dates {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.95rem;
  letter-spacing: 0.02em;
}
.tour-detail__edit-btn {
  flex: 0 0 auto;
  color: var(--accent-turquoise);
}
.tour-detail__edit-btn:hover {
  color: var(--accent-turquoise-bright);
}
.tour-detail__description {
  margin: 0 0 1rem;
  color: var(--text-dark);
  line-height: 1.55;
  white-space: pre-wrap;
}
.tour-detail__description--empty {
  color: var(--text-muted);
  font-style: italic;
}
.tour-detail__official {
  margin: 0 0 1.25rem;
}
.tour-detail__official a {
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.tour-detail__official a:hover {
  color: var(--accent-turquoise-bright);
}
.tour-detail__concerts {
  border-top: 1px solid var(--border-on-light);
  padding-top: 1.1rem;
}
.tour-detail__concerts-title {
  font-family: "Manrope", system-ui, sans-serif;
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 0.75rem;
}
.tour-detail__concerts-count {
  margin-left: 0.6rem;
  font-weight: 400;
  letter-spacing: 0.06em;
}
.tour-detail__concerts-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.tour-detail__concerts-list--past {
  margin-top: 0.5rem;
  opacity: 0.7;
}
.tour-detail__concert {
  display: grid;
  grid-template-columns: 7rem 1fr auto;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.4rem 0;
  border-bottom: 1px dashed var(--border-on-light);
  font-size: 0.92rem;
}
.tour-detail__concert:last-child {
  border-bottom: 0;
}
.tour-detail__concert--header {
  border-bottom: 1px solid var(--border-on-light);
  padding-bottom: 0.35rem;
  margin-bottom: 0.2rem;
}
.tour-detail__concert-col {
  font-family: "Manrope", system-ui, sans-serif;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
}
.tour-detail__concert-col--right {
  text-align: right;
}
.tour-detail__concert-date {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.tour-detail__concert-venue {
  color: var(--text-dark);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tour-detail__concert-city {
  color: var(--text-muted);
  text-align: right;
  white-space: nowrap;
}
.tour-detail__concert-flag {
  margin-left: 0.3rem;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--accent-turquoise);
}
.tour-detail__past {
  margin-top: 0.75rem;
}
.tour-detail__past > summary {
  cursor: pointer;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0.3rem 0;
  list-style: none;
}
.tour-detail__past > summary::-webkit-details-marker {
  display: none;
}
.tour-detail__empty,
.tour-detail__error,
.tour-detail__concerts-loading {
  margin: 0;
  color: var(--text-muted);
  font-style: italic;
  font-size: 0.9rem;
}
.tour-detail__error {
  /* token always resolves (--danger-deep = #a73030); the old raw-hex fallback
     was both a no-token violation and a stale literal. M12 Dávka-A 2026-05-25. */
  color: var(--danger-text);
}

/* --- Tour form modal --------------------------------------------- */
.tour-form-dialog {
  max-width: min(620px, 92vw);
}
.tour-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem; /* canonical dialog section gap (ADR-179) */
}
.tour-form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.9rem;
}
.tour-form__date-field {
  min-width: 0;
}
@media (max-width: 540px) {
  .tour-form__row {
    grid-template-columns: 1fr;
  }
}

/* --- M11.X-TOUR-MGMT Stage B — Tour chip + cross-link ----------- */
/* `.tour-chip` = small caps turquoise ghost-outline pill. Identity
   chrome lives in typography + chip border, not color rotation —
   per ADR-134 (no per-tour color/glyph). Standalone variant uses a
   muted slate border so the absence of a tour reads as a distinct
   atmospheric state without competing with the tour signal. */
.tour-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.5rem;
  border-radius: var(--radius-pill, 999px);
  /* M12 Noir PR4 (Petřin pokyn 2026-05-24 "názvy turné mají být fialová s
     glow" + rulebook: iris = tour chipy). Was teal — flipped to iris with
     a soft violet glow. */
  background: rgba(var(--accent-violet-rgb), 0.10);
  border: 1px solid var(--accent-violet-soft);
  color: var(--accent-violet-soft);
  box-shadow: 0 0 8px rgba(var(--accent-violet-rgb), 0.25);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.25;
  white-space: nowrap;
  vertical-align: middle;
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tour-chip--standalone {
  /* Petřin pokyn 2026-05-20 — grey rodina zachována, ale původní border
     0.28 + text-muted byl na hranici čitelnosti (zejména přes .event-
     popup--past opacity 0.85 cascade). Bumped both border + text alpha
     pro WCAG AA contrast na cream + past popup surface. */
  border-color: rgba(238, 240, 250, 0.5);
  color: var(--text-muted);
  letter-spacing: 0.1em;
}

/* Sidebar Concerts row — chip lives in the meta-top row, after flag
   + venue. Keep margin tight so the row's vertical rhythm stays. */
.feed-list__tour-chip {
  margin-left: 0.35rem;
}

/* M12 Noir PR5 ② — popup tour chip moved into the hero-chips slot as a
   canonical .chip-uppercase--iris; the old .event-popup__tour-chip-row is gone. */

/* Event detail header Row 2 — chip is a flex child of header-meta,
   no extra rules needed beyond the base. */

/* --- Cross-link "Part of {tour} →" + other stops mini list ----- */
.event-detail__tour-link {
  margin: 0.85rem 0 0;
  padding: 0.7rem 0.8rem;
  border-top: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-bottom: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  background: var(--accent-turquoise-tint, rgba(var(--accent-turquoise-rgb), 0.08));
  border-radius: var(--radius-md, 8px);
}
.event-detail__tour-link-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--accent-turquoise);
  background: transparent;
  border: none;
  width: 100%;
  cursor: pointer;
  text-align: left;
  padding: 0;
  letter-spacing: 0.01em;
}
.event-detail__tour-link-header:hover,
.event-detail__tour-link-header:focus-visible {
  color: var(--accent-turquoise-bright);
  outline: none;
}
.event-detail__tour-link-arrow {
  font-size: 1rem;
  line-height: 1;
  transition: transform 160ms ease;
}
.event-detail__tour-link-header[aria-expanded="true"] .event-detail__tour-link-arrow {
  transform: rotate(90deg);
}
.event-detail__tour-stops {
  margin: 0.6rem 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.event-detail__tour-stops--past {
  margin-top: 0.5rem;
}
.event-detail__tour-stops-heading {
  margin: 0.65rem 0 0.3rem;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 700;
}
.event-detail__tour-stop {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.25rem 0.35rem;
  border-radius: var(--radius-sm, 4px);
  font-size: 0.85rem;
  color: var(--text-dark);
  background: transparent;
  border: none;
  cursor: pointer;
  width: 100%;
  text-align: left;
  font-family: inherit;
  transition: background 120ms ease;
}
.event-detail__tour-stop:hover,
.event-detail__tour-stop:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.1);
  outline: none;
}
.event-detail__tour-stop--current {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  color: var(--accent-turquoise);
  font-weight: 600;
  cursor: default;
}
.event-detail__tour-stop--current:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
}
.event-detail__tour-stop--past {
  color: var(--text-muted);
}
.event-detail__tour-stop-date {
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  color: var(--text-muted);
  flex-shrink: 0;
  min-width: 4.2rem;
}
.event-detail__tour-stop--current .event-detail__tour-stop-date {
  color: var(--accent-turquoise);
}
.event-detail__tour-stop-where {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.event-detail__tour-stops-showall {
  margin-top: 0.4rem;
  background: transparent;
  border: none;
  color: var(--accent-turquoise);
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  padding: 0.2rem 0.35rem;
  cursor: pointer;
  font-family: inherit;
}
.event-detail__tour-stops-showall:hover,
.event-detail__tour-stops-showall:focus-visible {
  color: var(--accent-turquoise-bright);
  text-decoration: underline;
  outline: none;
}
.event-detail__tour-stops-error,
.event-detail__tour-stops-loading {
  margin: 0.45rem 0 0;
  font-size: 0.82rem;
  font-style: italic;
  color: var(--text-muted);
}

/* ─────────────────────────────────────────────────────────────────
   M11.X-TOUR-MGMT Stage C (ADR-134) — past event lifecycle
   ────────────────────────────────────────────────────────────────
   Past concerts preserve history on the map and in the panel (Petřin
   pokyn 2026-05-20: „nesmí zmizet úplně"), but get a dim+desaturate
   overlay so the eye reads them as archive at a glance. Detail modal
   is NOT dimmed — when the user opens it, full visibility.

   Constraints:
   • Mutually exclusive with ADR-083 freshness pulse (past wins).
   • Orthogonal to ADR-118 test-data red (both classes can stack;
     test-data red fill stays visible through past opacity).
   • Cancelled keeps its own line-through badge treatment, NOT bundled.
   ───────────────────────────────────────────────────────────────── */

/* Map marker — Petřin iter 2026-05-20:
   - v1-v5 = various opacity + saturate combos
   - v6 strategy (per Petřin pokyn „spíše přes bledost než průsvítnost"):
     lean on heavy desaturation + slight brightness boost to bleach the
     turquoise; keep opacity mostly intact so the marker stays a clear
     shape over the tile, the change reads as faded ink, not see-through. */
/* M12 Noir PR5 — past concert teardrop fades to a desaturated, dimmed
   silhouette (spec ".fm-marker--past"). Keeps the lift shadow so the shape
   stays grounded on the tile. */
.event-marker--past .event-marker__svg {
  filter: grayscale(0.7) opacity(0.65);
}

/* Sidebar row — full saturation drop on the whole row + title opacity
   reduction so the row reads "archive" but stays scannable. Modifier
   stacks with .feed-list__item base so default hover/focus still apply.
   Tour chip + status dot inside the past row are pulled along by the
   filter cascade — no per-element override needed. */
/* Past row — NO whole-row desaturation (Petřin pokyn 2026-05-27 — `saturate(0.65)`
   on the whole row was a bug: greyed the entity chip + content and even needed a
   counter-saturate band-aid on the icon tile). Now only a subtle title dim signals
   archive, alongside the PAST chip; the row keeps full colour + stays interactive. */
.feed-list__item--past .feed-list__title {
  opacity: 0.7;
}

/* M12 Noir PR5 ② — popup past fade now lives in the shared .popup--past rule;
   the Past pill became a canonical .chip-uppercase--muted in the hero. */

/* ================================================================
 * M11G — POI Propose modal + popup + actions + chip + marker
 * (Stage B per ADR-136 + scope dok)
 *
 * Marker = copper teardrop 30×44, Variant A per Petřin verdict 2026-05-20
 * (deep copper + 🏛️ glyph). Mirror eventu shape vocabulary; copper hue
 * carries the entity discriminator vs violet pins + turquoise concerts.
 * SVG defs (#poiCopper + #poiInnerHighlight) live in index.html so each
 * marker references them via url() — no per-marker parse cost.
 * ================================================================ */

.poi-marker {
  pointer-events: auto;
}

.poi-marker__svg {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* M12 Noir PR5 — grounding via the in-SVG url(#pinShadow) ellipse at the
     base (the column glyph is baked into the SVG; the old 🏛️ <span> is gone). */
}

/* --- POI Propose modal -------------------------------------------- */
/* Modal chrome (header, form layout, fields, test-data row, footer
   buttons) reuses the .event-create__* / .field / .test-data-field /
   .btn-primary / .btn-secondary vocabulary shared with Add a pin +
   Suggest event modals. Only POI-specific surfaces below: AI auto-fill
   button row, selected-location card, and cover preview block. */

/* M12 Noir PR6-A (mockup 11) — canonical two-column spread for create
   dialogs at the 720 wide tier: LEFT = identity/source group, RIGHT =
   place/time/map group. Each column stacks its fields with the canonical
   dialog section gap. Collapses to a single column below 600px (narrow
   desktop / tablet). Shared by Propose-landmark (poi-create), Add-concert
   AND Online-create (event-create) so all three read as one product —
   one grid pattern, not per-dialog variants (ui-standards §2). */
.poi-create__cols,
.event-create__cols {
  /* M12 (Petřin pokyn 2026-05-26) — concert + online create dialogs back to a
     SINGLE column. The two-column split was only a workaround for excessive
     form height; once the desktop density pass wins the vertical space, one
     column reads cleaner (no cik-cak, no unbalanced empty gap under the short
     side) and is simpler to maintain. Supersedes the planned M12.X-EVENT-2COL.
     Landmark keeps its own `.poi-create__cols` two-column layout (description +
     map side-by-side is intentional there). */
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.9rem;
  align-items: start;
}
.poi-create__col,
.event-create__col {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  min-width: 0;
}
@media (max-width: 600px) {
  .poi-create__cols,
  .event-create__cols {
    grid-template-columns: 1fr;
  }
}

/* Landmark name = the magic field (name drives location + AI description
   lookup) — reuses the concert paste-box affordance UNCHANGED, including
   the teal tint: it's an action affordance, and actions are teal app-wide
   (Petra 2026-06-06; a POI-copper re-tint was tried and rejected the same
   day — no scoped override here on purpose). */

.poi-create__ai-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}

.poi-create__ai-status {
  /* M14 unify (Petřin pokyn 2026-06-05) — AI info line = canonical
     whisper (✦ + --text-ai-note + 0.8rem), same as .ai-badge and the
     autofill notes. The .is-error variant below keeps danger color
     (actionable warning), sparkle stays. */
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--text-ai-note);
  line-height: 1.35;
}
.poi-create__ai-status::before {
  content: "✦ ";
}

.poi-create__ai-status.is-error {
  color: var(--danger-text);
}

/* ----------------------------------------------------------------
 * M13.X-PENDING (ADR-212) — canonical pending OVERLAY + stepper.
 *
 * The ONE app-wide "something is happening" pattern (ui-standards
 * "Loading state", previously ad-hoc). A prominent dimming overlay over
 * the create dialog with a checklist of steps the user watches tick off
 * while the slow enrichment runs (locate → AI venue/description →
 * Openverse cover). It sits ON TOP so the form can't be edited while it
 * runs (= the soft lock), and clears only once the fields + preview are
 * actually filled. Mounted as a direct child of the <dialog> (which is
 * position:fixed → a containing block), so inset:0 covers the modal.
 * ---------------------------------------------------------------- */
.pending-steps {
  position: absolute;
  inset: 0;
  /* ABOVE the Leaflet mini-map inside the venue card — Leaflet panes/controls
     run up to z-index ~700 and the map's container doesn't establish its own
     stacking context, so a low z-index here lets the mini-map punch through the
     overlay (Petřin screenshot 2026-06-01 "překryje to mapa"). Sit above it. */
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  /* Dim + slightly blur the form behind so the steps own the foreground. */
  background: rgba(8, 11, 26, 0.8);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  border-radius: inherit;
}

.pending-steps__card {
  width: min(380px, 94%);
  padding: var(--space-6);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.32);
  border-radius: var(--radius-lg, 18px);
  background: rgba(var(--cream-surface-rgb), 0.98);
  box-shadow:
    0 20px 56px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.08);
}

.pending-steps__title {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold, 700);
  color: var(--text-dark);
}

.pending-steps__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.pending-step {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--fs-md);
  line-height: 1.4;
  color: var(--text-muted);
}

.pending-step__mark {
  flex: 0 0 auto;
  width: 1.2em;
  height: 1.2em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1em;
}

.pending-step--pending {
  opacity: 0.7;
}
.pending-step--pending .pending-step__mark::before {
  content: "·";
  color: var(--text-muted);
}

/* active — teal spinner ring + brighter label */
.pending-step--active {
  color: var(--text-dark);
}
.pending-step--active .pending-step__mark {
  width: 0.95em;
  height: 0.95em;
  border: 2px solid rgba(var(--accent-turquoise-rgb), 0.3);
  border-top-color: var(--accent-turquoise);
  border-radius: 50%;
  animation: pending-spin 0.7s linear infinite;
}

.pending-step--done .pending-step__mark::before {
  content: "✓";
  color: var(--success);
  font-weight: 700;
}

.pending-step--error {
  color: var(--danger-text);
}
.pending-step--error .pending-step__mark::before {
  content: "✕";
  color: var(--danger-text);
  font-weight: 700;
}

/* Identical to .venue-card__change "Change location" link (Petřin pokyn
   2026-06-01 — "úplně stejné jako change location, stejný font, písmo"). */
.pending-steps__escape {
  appearance: none;
  margin-top: var(--space-2);
  background: transparent;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--accent-turquoise);
  padding: 0.3rem 0;
  text-align: left;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.pending-steps__escape:hover,
.pending-steps__escape:focus-visible {
  outline: none;
  color: var(--accent-turquoise-bright);
  text-decoration-thickness: 2px;
}

@keyframes pending-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .pending-step--active .pending-step__mark {
    animation: none;
  }
}

/* ----------------------------------------------------------------
 * M14 WARMUP — "Waking the map" cold-start card (warmupCard.js).
 *
 * Sibling of the pending overlay above: same scrim + card anatomy
 * (rgba dim, turquoise hairline, --cream-surface card), but it covers
 * the whole viewport (fixed) during the FIRST boot only — it appears
 * when the boot data takes > ~800 ms (cold Lambda ≈ 5 s) and fades as
 * soon as pins arrive. Pulsing canonical heart, three soft dots, one
 * rotating tip. Low-perf on purpose: opacity/transform animations
 * only, no blur on the moving parts.
 * ---------------------------------------------------------------- */
.warmup-overlay {
  position: fixed;
  inset: 0;
  z-index: 3000; /* above map chrome, below nothing relevant at boot */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  background: rgba(8, 11, 26, 0.72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 1;
  transition: opacity 350ms ease;
}
.warmup-overlay--out {
  opacity: 0;
  pointer-events: none;
}

.warmup-card {
  /* Wide enough that every tip fits ONE line on desktop (Petřin pokyn
     2026-06-05 — no mid-sentence wrap, no orphan word). Narrow screens
     wrap via text-wrap: balance below. */
  width: min(500px, 92%);
  padding: var(--space-6);
  text-align: center;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.32);
  border-radius: var(--radius-lg, 18px);
  background: rgba(var(--cream-surface-rgb), 0.98);
  box-shadow:
    0 20px 56px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.08);
}

.warmup-card__heart {
  color: var(--accent-magenta);
  animation: warmup-heart-pulse 1.6s ease-in-out infinite;
}
@keyframes warmup-heart-pulse {
  0%, 100% { transform: scale(1); opacity: 0.85; }
  50% { transform: scale(1.12); opacity: 1; }
}

.warmup-card__title {
  margin: var(--space-3) 0 0;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold, 700);
  color: var(--text-dark);
}

.warmup-card__dots {
  display: flex;
  justify-content: center;
  gap: 0.45rem;
  margin-top: var(--space-3);
}
.warmup-card__dots span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-turquoise);
  opacity: 0.35;
  animation: warmup-dot 1.4s ease-in-out infinite;
}
.warmup-card__dots span:nth-child(2) { animation-delay: 0.2s; }
.warmup-card__dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes warmup-dot {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 1; }
}

.warmup-card__tip {
  margin: var(--space-4) 0 0;
  min-height: 2.8em; /* two lines — card doesn't jump between tips */
  font-size: var(--fs-sm);
  color: var(--text-muted);
  /* M14 (Petřin pokyn 2026-06-05) — desktop: one line (card is wide
     enough); narrow screens: text-wrap balance splits into even halves,
     never an orphaned single word. */
  text-wrap: balance;
  opacity: 0;
  transition: opacity 300ms ease;
}
.warmup-card__tip--in {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .warmup-card__heart,
  .warmup-card__dots span {
    animation: none;
  }
}

/* ----------------------------------------------------------------
 * M15 step ③ — Welcome card (ADR-261 · approved designer handoff
 * docs/handoff/welcome-card/handoff/, variant A "Two acts").
 *
 * Package --wc-* tokens mapped onto ours per Petra's approval:
 * midnight→--cream-surface · moon→--text-dark · moon-muted→--text-muted
 * · mist→--text-light-muted · iris→--accent-violet ·
 * hairline→--border-on-light. Close = canonical .dialog-close; Sign up
 * = canonical .btn-warn (gold auth family, AUTO width — Petra's
 * deviation from the package's full-width iris ghost). The map is
 * reached by closing the card — never an "Explore" CTA (README §7).
 * ---------------------------------------------------------------- */
.welcome-dialog {
  /* Reset the UA dialog chrome — the card inside is the visual. */
  border: none;
  padding: 0;
  background: transparent;
  max-width: none;
  max-height: 100dvh;
  overflow: visible;
}
.welcome-dialog::backdrop {
  background: rgba(8, 11, 26, 0.72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.welcome-card {
  position: relative; /* anchors the canonical .dialog-close */
  width: min(468px, 92vw);
  max-height: min(86dvh, 680px);
  overflow-y: auto;
  padding: calc(var(--space-6) + var(--space-2))
    calc(var(--space-6) + var(--space-3)) var(--space-6);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-lg);
  /* Handoff shell: faint teal glow bleeding from the top edge over a
     lifted→surface diagonal fall. */
  background:
    radial-gradient(ellipse 120% 70% at 50% -10%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 60%),
    linear-gradient(165deg, var(--cream-lifted), var(--cream-surface) 70%);
  box-shadow:
    inset 0 1px 0 rgba(238, 240, 250, 0.04),
    0 26px 60px -24px rgba(0, 0, 0, 0.7);
  color: var(--text-dark);
  text-align: center;
}

/* Brandmark — the magenta Echo dot in a soft tinted tile. */
.welcome-card__mark {
  width: 46px;
  height: 46px;
  margin: 0 auto var(--space-4);
  display: grid;
  place-items: center;
  border: 1px solid rgba(var(--accent-mine-rgb), 0.34);
  border-radius: var(--radius-md);
  background: radial-gradient(circle at 50% 35%, rgba(var(--accent-mine-rgb), 0.28), rgba(var(--accent-mine-rgb), 0.1) 60%, transparent 75%);
}
.welcome-card__mark-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, var(--accent-mine), var(--accent-magenta));
  box-shadow:
    0 0 14px 1px rgba(var(--accent-mine-rgb), 0.7),
    0 0 0 5px rgba(var(--accent-mine-rgb), 0.12);
}

.welcome-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-semibold);
  /* Handoff: 33px desktop / 26px mobile, one-line (README §7) — clamp
     between the modal-title token and --fs-xl floor instead of a
     per-window media query. */
  font-size: clamp(var(--fs-xl), 7vw, var(--fs-modal-title));
  line-height: var(--lh-tight);
  color: var(--text-dark);
  white-space: nowrap;
}

.welcome-card__lead {
  margin: var(--space-2) auto 0;
  max-width: 40ch;
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  color: var(--text-muted);
  text-wrap: pretty;
}

/* "Two acts" — act 1: the free layer as a single calm teal line. */
.welcome-card__free {
  display: flex;
  align-items: center;
  /* FINAL (Petra 2026-06-06): desktop = ONE row — gap 12 + side padding
     12 give the text 261px vs the 252px it needs (measured). Mobile
     override below = HER layout (24px gap, wrap after "leave"). 480px
     card + stacked pill = rejected, do not retry. */
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding: var(--space-3);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.18);
  border-radius: var(--radius-md);
  background: rgba(var(--accent-turquoise-rgb), 0.06);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--text-muted);
  text-align: left;
}
/* Narrow-screen wrap is steered by NBSPs in welcome.free.text ("your own
   Echo." never splits) — the first line fills naturally up to "leave"
   (Petra 2026-06-06; balance pushed "leave" down, rejected). */
.welcome-card__free strong {
  /* "Echo" carries the mine identity (Petra 2026-06-06) + a touch more
     air after "own" — the plain space read too tight next to the bold. */
  color: var(--accent-mine);
  font-weight: var(--fw-bold);
  margin-left: 0.18em;
}
.welcome-card__tick {
  flex: 0 0 auto;
  padding: var(--space-1) var(--space-2);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.3);
  border-radius: 999px;
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  white-space: nowrap; /* mockup wraps the pill into two lines — kept whole */
}

/* Act 2: the sign-up perks under an iris eyebrow. */
.welcome-card__upsell {
  margin-top: var(--space-4);
  text-align: left;
}
.welcome-card__eyebrow {
  font: var(--text-eyebrow);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-violet);
}
.welcome-card__perks {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}
.welcome-card__perks li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--fs-md); /* = lead size (Petra 2026-06-06: stejně velké) */
  color: var(--text-dark);
}
.welcome-card__perk-text {
  padding-top: var(--space-1);
  line-height: var(--lh-snug);
}
.welcome-card__perk-text strong {
  font-weight: var(--fw-bold);
}
/* Manrope's & sits too tight against neighbouring words — give it air. */
.welcome-card__amp {
  margin: 0 0.14em;
}
.welcome-card__pi {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
}
.welcome-card__pi svg {
  display: block;
}
.welcome-card__pi--gold {
  color: var(--accent-copper);
  background: rgba(var(--accent-copper-rgb), 0.14);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.26);
}
.welcome-card__pi--magenta {
  color: var(--accent-mine);
  background: rgba(var(--accent-mine-rgb), 0.14);
  border: 1px solid rgba(var(--accent-mine-rgb), 0.26);
}
.welcome-card__pi--violet {
  color: var(--accent-violet);
  background: rgba(var(--accent-violet-rgb), 0.14);
  border: 1px solid rgba(var(--accent-violet-rgb), 0.26);
}
.welcome-card__pi--turquoise {
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.26);
}

/* Footer — gold auto-width Sign up + microcopy + quiet dismiss row.
   Sticky inside the card's scroll area (M15B mobile audit): on a short
   phone the card scrolls and the CTA must not hide below the fold.
   Negative margins swallow the card padding; the top fade masks list
   items scrolling underneath. */
.welcome-card__foot {
  position: sticky;
  bottom: calc(-1 * var(--space-6));
  margin-top: var(--space-5);
  margin-left: calc(-1 * (var(--space-6) + var(--space-3)));
  margin-right: calc(-1 * (var(--space-6) + var(--space-3)));
  margin-bottom: calc(-1 * var(--space-6));
  padding: var(--space-3) calc(var(--space-6) + var(--space-3)) var(--space-6);
  background:
    linear-gradient(180deg, rgba(var(--cream-surface-rgb), 0) 0%, rgba(var(--cream-surface-rgb), 0.97) 26%);
  text-align: center;
}
.welcome-card__signup,
.welcome-card__gotit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: var(--space-2) calc(var(--space-6) + var(--space-2));
}
.welcome-card__foot-help {
  margin: var(--space-3) 0 0;
  /* README §7: microcopy ≥12.5px in moon-muted → --fs-sm + --text-muted */
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
@media (max-width: 767px) {
  .welcome-card {
    padding: var(--space-6) var(--space-5) var(--space-5);
  }
  .welcome-card__lead {
    font-size: var(--fs-sm);
  }
  .welcome-card__perks li {
    font-size: var(--fs-sm); /* keeps the = lead pairing on mobile */
  }
  .welcome-card__free {
    /* Phone: Petra's layout (2026-06-06) — pill BESIDE the text with a
       wide gap; the text breaks "…and leave / your own Echo." (NBSPs in
       welcome.free.text keep that chunk whole). Stacked variant rejected. */
    gap: var(--space-6);
  }
  .welcome-card__foot {
    bottom: calc(-1 * var(--space-5));
    margin-left: calc(-1 * var(--space-5));
    margin-right: calc(-1 * var(--space-5));
    margin-bottom: calc(-1 * var(--space-5));
    padding-left: var(--space-5);
    padding-right: var(--space-5);
    padding-bottom: var(--space-5);
  }
}

/* ----------------------------------------------------------------
 * M14 WARMUP+PENDING (A11c) — admin queue "loading" placeholder.
 *
 * Quiet inline row shown in a tab's table/area while the first fetch
 * is in flight, so an opened tab is never silently blank on a cold
 * Lambda. Sibling of .warmup-card__dots above — same three turquoise
 * dots, same `warmup-dot` keyframes (NO duplicate @keyframes). Muted
 * label so it reads as a status, not content.
 * ---------------------------------------------------------------- */
.admin-queue-pending {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: var(--space-5);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.admin-queue-pending__dots {
  display: inline-flex;
  gap: 0.4rem;
}
.admin-queue-pending__dots span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-turquoise);
  opacity: 0.35;
  animation: warmup-dot 1.4s ease-in-out infinite;
}
.admin-queue-pending__dots span:nth-child(2) { animation-delay: 0.2s; }
.admin-queue-pending__dots span:nth-child(3) { animation-delay: 0.4s; }

@media (prefers-reduced-motion: reduce) {
  .admin-queue-pending__dots span {
    animation: none;
  }
}

/* M11G Stage A.2 — explicit "Try AI lookup again" trigger surfaced
   when the Tarja-relevance gate fired. Per Petřin pokyn 2026-05-20:
   user often just needs to edit name / location and retry. */
.poi-ai-retry {
  margin-left: 0.4rem;
  padding: 0.2rem 0.6rem;
  background: transparent;
  border: 1px solid currentColor;
  border-radius: var(--radius-xs, 5px);
  color: inherit;
  font-family: inherit;
  font-size: 0.78rem;
  cursor: pointer;
  opacity: 0.9;
  transition: opacity 150ms ease;
}
.poi-ai-retry:hover {
  opacity: 1;
}

.poi-create__location-card {
  margin-top: 0.4rem;
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius-sm, 8px);
  background: rgba(var(--accent-turquoise-rgb), 0.06);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.22);
  font-size: 0.85rem;
  color: var(--text-dark);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  justify-content: space-between;
}

.poi-create__location-clear {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0.1rem 0.35rem;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 1.15rem;
  line-height: 1;
}

.poi-create__location-clear:hover,
.poi-create__location-clear:focus-visible {
  color: var(--accent-turquoise);
  outline: none;
}

.poi-create__cover-preview {
  margin-top: 0.3rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

/* 🏛️ panteon placeholder shown while no AI cover is available
   (Petřin pokyn 2026-05-20). Mirrors .event-detail__cover--placeholder
   recipe with the POI copper glow instead of violet/turquoise. */
.poi-create__cover-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 140px;
  padding: 1.2rem 1rem;
  border-radius: var(--radius-md, 12px);
  background: linear-gradient(135deg,
    rgba(var(--accent-copper-rgb), 0.14) 0%,
    rgba(var(--accent-copper-rgb), 0.06) 100%);
  border: 1px dashed rgba(var(--accent-copper-rgb), 0.25);
}

.poi-create__cover-placeholder > span {
  font-size: 3.4rem;
  line-height: 1;
  filter: drop-shadow(0 4px 10px rgba(var(--accent-copper-rgb), 0.25));
}

.poi-create__cover-img {
  display: block;
  max-width: 100%;
  max-height: 200px;
  width: auto;
  border-radius: var(--radius-md, 12px);
  object-fit: cover;
}

.poi-create__cover-credit {
  margin: 0;
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1.3;
}

/* --- POI popup ---------------------------------------------------- */
/* Copper accent variant of .sigplace-popup; same shape but warmer. */

.poi-popup .leaflet-popup-content-wrapper {
  /* M12 Noir PR5 ② — gold entity tone (landmark). */
  --popup-edge: var(--accent-copper);
  --popup-edge-rgb: var(--accent-copper-rgb);
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-copper-rgb), 0.12), transparent 60%),
    var(--cream-surface);
  border-radius: 14px;
  border: 1px solid rgba(var(--accent-copper-rgb), 0.28);
  box-shadow:
    0 12px 32px rgba(0, 0, 0, 0.18),
    0 4px 12px rgba(var(--accent-copper-rgb), 0.14);
}

.poi-popup .leaflet-popup-content {
  margin: 0;
  padding: 0;
  color: var(--text-dark);
  /* M11.X-POPUP-LAYOUT v2 — DO NOT use `width: auto !important` here
     (legacy .sigplace-popup pattern carried it but caused ~150 px popup
     instead of 440 px on POI since Stage A). Leaflet's _updateLayout
     sets inline `style.width = clamp(content, minWidth, maxWidth)`
     after measuring with `whiteSpace: nowrap`. An `!important` override
     defeats that, leaving the popup at whatever the natural width of
     its narrowest child is (here the photo's `width:100%` makes parent
     width self-referential → collapses). The mobile clamp below still
     caps the inline 440 px on narrow viewports. */
  max-width: min(440px, calc(100vw - 32px));
}

/* M12 Noir PR5 ② — .poi-popup__photo + .poi-popup__name retired: the landmark
   popup now shares the place-popup hero (.popup__hero-img) + Cormorant title
   (.popup__title) with the concert popup. */

.poi-popup__description {
  margin: 0;
  padding: 0 0.9rem 0.5rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-dark);
  /* M11.X-POPUP-LAYOUT — guard against ultra-long unbroken strings (URLs,
     run-on words in user-submitted manual descriptions) that would push
     the popup wider than its maxWidth ceiling. */
  overflow-wrap: anywhere;
  word-break: break-word;
}

.poi-popup__description p {
  margin: 0 0 0.5rem;
}

.poi-popup__description p:last-child {
  margin-bottom: 0;
}

/* M12 Noir PR5 ② — .poi-popup__footer + .poi-popup__flag retired: location
   (flag + city) now renders via the shared .popup__location + .popup__flag. */

.poi-popup__attribution {
  margin: 0;
  padding: 0.45rem 0.9rem 0.7rem;
  font-size: 0.78rem;
  color: var(--text-muted);
  border-top: 1px solid rgba(238, 240, 250, 0.06);
}

.poi-popup__attribution-nick {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--accent-copper);
  font-weight: 600;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgba(var(--accent-copper-rgb), 0.55);
}

.poi-popup__attribution-nick:hover,
.poi-popup__attribution-nick:focus-visible {
  color: var(--accent-copper-bright);
  outline: none;
}

.poi-popup__status-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.12rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.poi-popup__status-chip--pending {
  background: rgba(var(--warning-rgb, 216, 138, 38), 0.14);
  color: var(--warning, #8a5a1e);
  border: 1px solid rgba(var(--warning-rgb, 216, 138, 38), 0.4);
}

.poi-popup__status-chip--rejected {
  background: rgba(var(--danger-rgb, 192, 65, 65), 0.12);
  color: var(--danger-text);
  border: 1px solid rgba(var(--danger-rgb, 192, 65, 65), 0.36);
}

/* --- POI actions row (inline icons in popup / panel / detail) ----- */

.poi-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.poi-actions--popup {
  margin: 0.35rem 0.9rem 0.65rem;
  padding-top: 0.4rem;
  border-top: 1px solid rgba(238, 240, 250, 0.06);
}

.poi-actions__btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}

.poi-actions__btn:hover,
.poi-actions__btn:focus-visible {
  outline: none;
  background: rgba(238, 240, 250, 0.05);
  color: var(--text-dark);
}

.poi-actions__btn--approve:hover,
.poi-actions__btn--approve:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--accent-turquoise);
  border-color: var(--accent-turquoise);
}

.poi-actions__btn--reject:hover,
.poi-actions__btn--reject:focus-visible {
  background: rgba(var(--danger-rgb, 192, 65, 65), 0.10);
  color: var(--danger-text);
  border-color: var(--danger);
}

.poi-actions__btn--edit:hover,
.poi-actions__btn--edit:focus-visible {
  background: rgba(var(--accent-copper-rgb), 0.10);
  color: var(--accent-copper);
  border-color: var(--accent-copper);
}

.poi-actions__btn--delete:hover,
.poi-actions__btn--delete:focus-visible {
  background: rgba(var(--danger-rgb, 192, 65, 65), 0.10);
  color: var(--danger-text);
  border-color: var(--danger);
}

.poi-actions--footer .poi-actions__btn {
  width: auto;
  padding: 0.35rem 0.75rem;
  gap: 0.35rem;
  border-radius: var(--radius-sm, 8px);
  border: 1px solid rgba(238, 240, 250, 0.18);
}

.poi-actions__label {
  font-size: 0.84rem;
  font-weight: 500;
}

/* --- POI chip (Yours panel, distinct from event chip) ------------- */

.poi-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: rgba(var(--accent-copper-rgb), 0.12);
  color: var(--accent-copper);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.35);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.poi-chip__icon {
  font-size: 0.85em;
  line-height: 1;
}

/* Event type chip — per ADR-140 Rozhodnutí 5 (Variant B: per-category
   brand color). Shape parity with .poi-chip so PLACE / ONLINE / CONCERT
   read as one chip family across public feed + Yours panel. */
.event-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  /* Fallback if neither modifier applied. */
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--accent-turquoise);
}

.event-chip--online {
  background: rgba(var(--accent-online-rgb), 0.12);
  color: var(--accent-online-bright);
  border-color: rgba(var(--accent-online-rgb), 0.35);
}

.event-chip--concert {
  background: rgba(var(--accent-turquoise-rgb), 0.12);
  color: var(--accent-turquoise);
  border-color: rgba(var(--accent-turquoise-rgb), 0.35);
}

/* --- Spider menu (round button + submenu, M11G logic skeleton) ----
   Visual polish (custom icons, animations, atmospheric glow) defers to
   M12 per ADR-136 R9 — this is functional skeleton only so the M11G
   flow can be exercised end-to-end. */

/* M11G chrome — songs pebble (🎵) + spider menu (🏛️) sit in the top-left
   zone between the backend-status dot (right of search) and the Frisson
   hero floating panel (Petřin pokyn 2026-05-20). Layout target:
     SEARCH …gap… backend-dot …gap… SONGS …gap… POI flush-left HERO
   Both pebbles use `position: fixed` so calc(50% - …) resolves against
   the viewport directly — NOT against the cluster they live in. This
   sidesteps the earlier `.top-right-cluster { position: absolute }`
   ancestor-positioning trap that pinned songs to the right cluster
   regardless of CSS overrides. */
/* Mobile (< 768px) default — Songs + POI vertical stack at bottom-left
   per Petřin sketch 2026-05-20: POI sits above Songs, both above the
   events panel tab bar. Add Pin FAB stays bottom-right (existing). */
.spider-menu {
  position: fixed;
  bottom: calc(5rem + 44px + 0.5rem); /* above songs pebble (44px tall + 8px gap) */
  left: 0.75rem;
  z-index: 1050;
  display: inline-flex;
  align-items: center;
}

/* (Mobile + desktop #songs-pebble repositioning removed — M13.X-SONGS-TAB
   ADR-237; the spider keeps its desktop strip anchor below.) */
/* Desktop / tablet+ reposition (≥ 768 px) — the spider pebble flips from
   its mobile corner position to a horizontal strip flush left of the
   Frisson hero floating panel. `position: fixed` so calc(50% - …)
   resolves against the viewport directly, NOT against the cluster's
   positioning context. debug-tag: poi-songs-strip-v3 */
@media (min-width: 768px) {
  .spider-menu {
    position: fixed !important;
    top: max(0.9rem, env(safe-area-inset-top)) !important;
    left: calc(50% - 300px) !important;
    bottom: auto !important;
    z-index: 1000;
  }
}

/* Match the songs-pebble glass surface so the two chrome pebbles read
   as siblings when they sit next to each other on desktop. Pebble itself
   is the spider-menu__button; only the size + glass change here, the
   open/close state styling is already handled above. */
.spider-menu__button {
  width: 44px;
  height: 44px;
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-copper-rgb), 0.18) 0%, transparent 60%),
    rgba(var(--cream-surface-rgb), 0.78);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.18);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow:
    0 0 14px rgba(var(--accent-copper-rgb), 0.18),
    0 4px 14px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 rgba(238, 240, 250, 0.55);
}

.spider-menu__button {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  /* Positioned with an explicit LOW z-index so the open submenu (z 1090)
     reliably paints ABOVE the button in every browser. The button's
     backdrop-filter creates its own stacking context; in some engines
     (notably iOS Safari) a non-positioned backdrop-filter sibling composites
     OVER a higher-z positioned sibling — so the gold pebble was drawn on top
     of the dropdown's corner (Petřin screenshot 2026-06-01 "menu se
     vykresluje pod pebble ikonou"). Making the button a positioned z:0 layer
     removes the ambiguity: 0 < 1090, list always wins. relative + no offsets
     = identical box, so layout is unchanged. */
  position: relative;
  z-index: 0;
  /* Noir PR3 — black base + warm copper glow (landmark family). Matches the
     songs pebble treatment: solid dark fill + family-colored halo (Petřin
     pokyn 2026-05-24 — POI měl černý podklad ale chyběl teplý glow). */
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-copper-rgb), 0.24) 0%, transparent 60%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.60);
  color: var(--accent-copper);
  cursor: pointer;
  box-shadow:
    0 0 20px rgba(var(--accent-copper-rgb), 0.50),
    0 4px 14px rgba(0, 0, 0, 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
  transition: transform 140ms ease, background 140ms ease, box-shadow 140ms ease;
}

.spider-menu__button:hover,
.spider-menu__button:focus-visible {
  outline: none;
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-copper-rgb), 0.30) 0%, transparent 60%),
    var(--cream-surface);
  transform: translateY(-1px);
  box-shadow:
    0 0 28px rgba(var(--accent-copper-rgb), 0.65),
    0 6px 18px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(238, 240, 250, 0.12);
}

.spider-menu__button[aria-expanded="true"] {
  /* Stay on the dark Noir base like the sibling pebbles (songs/join/locate
     never flip to a solid fill when active) — Petřin pokyn 2026-06-02 "měla
     být černá jako ostatní ikony". The old `background: var(--accent-copper)`
     turned the open pebble into a solid gold disc, so LANDMARKS read as a
     different, lighter pebble while its menu was open. Signal "open" with the
     same brighter copper border + glow used on hover, keeping the dark base
     and the copper glyph (inherited `color`) untouched. */
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-copper-rgb), 0.30) 0%, transparent 60%),
    var(--cream-surface);
  border-color: rgba(var(--accent-copper-rgb), 0.85);
  box-shadow:
    0 0 28px rgba(var(--accent-copper-rgb), 0.65),
    0 6px 18px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(238, 240, 250, 0.12);
}

/* M12.X-PROFILE-FIDELITY — emoji → sprite glyph; inherits copper from the
   button color. Sized to match the sibling songs pebble's note glyph (Petřin
   pokyn 2026-05-27) and the mobile rule — both viewports now share 24px.
   Descendant selector (`.spider-menu .spider-menu__icon`, 0,2,0) is REQUIRED:
   the bare `.spider-menu__icon` (0,1,0) tied the generic `.icon { width:16px }`
   on specificity and lost on source order (`.icon` is later in this file), so
   the desktop glyph silently rendered at 16px — tiny, lost in the disc. The
   extra class wins the cascade. Mirrors `.spider-menu .spider-menu__icon` in
   mobile.css so desktop + mobile are one source of truth. */
.spider-menu .spider-menu__icon {
  width: 24px;
  height: 24px;
}

/* Frost-styled dropdown — atmospheric 4-corner radial washes + cream
   surface + backdrop blur (same vocabulary jako .hero-strip / .events-panel
   / .songs-pebble per ADR-085 Vrstva B). Copper-tinted border + accent
   anchors the menu to the POI family. Default = pop DOWN (desktop /
   tablet+); mobile overrides to pop UP because spider is bottom-anchored
   there. Solid cream base (0.98) so it stays opaque against the map tile
   layer — atmospheric radial washes sit on top of the cream, not instead
   of it. */
.spider-menu__list {
  position: absolute;
  top: calc(100% + 0.6rem);
  bottom: auto;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.55rem;
  list-style: none;
  background:
    radial-gradient(ellipse 60% 70% at 0% 0%,
      rgba(var(--accent-copper-rgb), 0.16) 0%, rgba(var(--accent-copper-rgb), 0.04) 35%, transparent 65%),
    radial-gradient(ellipse 60% 70% at 100% 0%,
      rgba(var(--accent-turquoise-rgb), 0.12) 0%, rgba(var(--accent-turquoise-rgb), 0.04) 35%, transparent 65%),
    radial-gradient(ellipse 60% 70% at 100% 100%,
      rgba(var(--accent-violet-rgb), 0.12) 0%, rgba(var(--accent-violet-rgb), 0.04) 35%, transparent 65%),
    rgba(var(--cream-surface-rgb), 0.98);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.22);
  border-radius: 16px;
  box-shadow:
    0 18px 42px rgba(0, 0, 0, 0.22),
    0 4px 14px rgba(var(--accent-copper-rgb), 0.12),
    inset 0 1px 0 rgba(238, 240, 250, 0.6);
  min-width: 240px;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  z-index: 1090;
}

/* Speech-bubble tail — two stacked pseudo-element triangles point at
   the POI button so the dropdown reads as a tooltip belonging to it,
   not a free-floating dropdown. Outer (::before) = border color, inner
   (::after) = cream fill 1 px smaller so the border outlines the arrow.
   Desktop default = arrow at TOP pointing UP. Mobile flip below. */
.spider-menu__list::before,
.spider-menu__list::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 0;
  height: 0;
  pointer-events: none;
}

.spider-menu__list::before {
  top: -8px;
  transform: translateX(-50%);
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-bottom: 8px solid rgba(var(--accent-copper-rgb), 0.22);
}

.spider-menu__list::after {
  top: -7px;
  transform: translateX(-50%);
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-bottom: 7px solid rgba(var(--cream-surface-rgb), 0.98);
}

/* Mobile — spider is bottom-anchored at bottom-left of the viewport, so
   the submenu pops UP (above the button) instead of down, and the bubble
   tail flips to point DOWN at the button below. The default `left: 50%;
   transform: translateX(-50%)` would push the 240-px submenu past the
   left viewport edge (button center is ~34 px from left, so centered
   submenu's left edge sits at -86 px). Anchor the submenu to the button's
   LEFT edge instead and pin the arrow tip at +22 px so it still points
   at the button's centre. */
@media (max-width: 767px) {
  .spider-menu__list {
    top: auto;
    bottom: calc(100% + 0.6rem);
    left: 0;
    transform: none;
  }
  .spider-menu__list::before {
    top: auto;
    bottom: -8px;
    left: 22px;
    transform: none;
    border-top: 8px solid rgba(var(--accent-copper-rgb), 0.22);
    border-bottom: none;
  }
  .spider-menu__list::after {
    top: auto;
    bottom: -7px;
    left: 23px;
    transform: none;
    border-top: 7px solid rgba(var(--cream-surface-rgb), 0.98);
    border-bottom: none;
  }
}

.spider-menu__list[hidden] {
  display: none;
}

/* LI wrappers stretch to full UL width so the buttons inside can grow
   to fill the row (= hover highlight reaches the right edge). Without
   the explicit width: 100% the LI shrink-wraps around the inline-block
   button and leaves dead space on the right that the hover bg can't
   reach. */
.spider-menu__list > li {
  display: block;
  width: 100%;
}

.spider-menu__item {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 0.65rem 0.85rem;
  border-radius: 10px;
  border: 0;
  background: transparent;
  color: var(--text-dark);
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  text-align: left;
  cursor: pointer;
  transition: background 180ms ease, color 180ms ease, transform 180ms ease;
  white-space: nowrap;
}

/* Hover + focus state — uniform copper tint across the FULL item width
   (no radial-gradient fade) so the highlight reads as a clean selection
   row, not a half-painted swipe. Text stays dark (var(--text-dark)) for
   AA contrast against the light copper background; bumping font-weight
   adds the affordance feedback. */
.spider-menu__item:hover:not(:disabled),
.spider-menu__item:focus-visible:not(:disabled) {
  outline: none;
  background: rgba(var(--accent-copper-rgb), 0.14);
  color: var(--text-dark);
  font-weight: 600;
}

.spider-menu__item:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* M12.X-PROFILE-FIDELITY — emoji → sprite glyph, single copper tone
   (landmark world). Brightens + nudges on hover so the row feels alive. */
.spider-menu__item-icon {
  color: var(--accent-copper);
  flex-shrink: 0;
  transition:
    color 150ms ease,
    transform 150ms ease;
}
.spider-menu__item:not(:disabled):hover .spider-menu__item-icon,
.spider-menu__item:not(:disabled):focus-visible .spider-menu__item-icon {
  color: var(--accent-copper-bright);
  transform: scale(1.08);
}
.spider-menu__item:disabled .spider-menu__item-icon {
  color: var(--text-muted);
}

/* M15 ⑧ LANDMARK-HIGHLIGHT (ADR-266) — active-state checkmark (Petřina
   volba b 2026-06-06): the row keeps its label and gains a copper ✓ on the
   right while the landmark filter is ON; clicking the row again turns the
   filter off. Synced from body.map-landmarks-only on every menu open. */
/* Both rules carry 0,2,0 specificity on purpose: the shared `.icon` class
   (defined LATER in this file) sets display:inline-block at 0,1,0 and would
   beat a bare `.spider-menu__item-check { display:none }` by source order —
   the same cascade trap documented at the `.icon` rule itself. */
.spider-menu__item .spider-menu__item-check {
  margin-left: auto;
  color: var(--accent-copper);
  display: none;
}
/* Checked row reads as a clear ON state, not a hover ghost (Petřin pokyn
   2026-06-06 „čekala bych podbarvení a výraznější ikonu") — stronger copper
   wash than the 0.14 hover tint + brightened icon. The :hover/:focus arms
   keep the wash stable when the pointer passes over the active row (the
   hover rule above would otherwise dial it back down to 0.14). */
.spider-menu__item--checked,
.spider-menu__item--checked:hover:not(:disabled),
.spider-menu__item--checked:focus-visible:not(:disabled) {
  background: rgba(var(--accent-copper-rgb), 0.20);
  font-weight: 600;
}
.spider-menu__item--checked .spider-menu__item-icon {
  color: var(--accent-copper-bright);
}
.spider-menu__item--checked .spider-menu__item-check {
  display: inline-block;
}

.spider-menu__item-meta {
  margin-left: auto;
  padding: 0.18rem 0.5rem;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-copper);
  background: rgba(var(--accent-copper-rgb), 0.10);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.22);
  border-radius: 999px;
}

/* --- POI cluster orb (copper, mirrors event cluster shape) -------- */

.poi-cluster-frost {
  background: transparent;
  border: none;
}

.poi-cluster-frost__inner {
  width: var(--cluster-size, 38px);
  height: var(--cluster-size, 38px);
  border-radius: 50%;
  background: radial-gradient(
    circle at 38% 30%,
    var(--accent-copper-bright) 0%,
    var(--accent-copper) 55%,
    var(--accent-copper-deep) 100%
  );
  box-shadow:
    0 0 0 2px rgba(var(--accent-copper-rgb), 0.88),
    0 0 18px rgba(var(--accent-copper-rgb), 0.55),
    0 4px 12px rgba(0, 0, 0, 0.40);
  color: var(--text-light);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  font-size: 0.85rem;
  font-weight: 700;
  font-family: inherit;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: cluster-frost-breathe 5s ease-in-out infinite;
  transform-origin: center;
  transition: transform 220ms ease, box-shadow 220ms ease;
}

.poi-cluster-frost--sm { --cluster-size: 34px; font-size: 0.78rem; }
.poi-cluster-frost--md { --cluster-size: 42px; font-size: 0.86rem; }
.poi-cluster-frost--lg { --cluster-size: 52px; font-size: 0.96rem; }
.poi-cluster-frost--xl { --cluster-size: 64px; font-size: 1.08rem; }

@media (hover: hover) and (pointer: fine) {
  .poi-cluster-frost:hover .poi-cluster-frost__inner {
    transform: scale(1.08);
    box-shadow:
      0 0 0 1.5px rgba(var(--accent-copper-rgb), 0.90),
      0 0 28px rgba(var(--accent-copper-rgb), 0.70),
      0 4px 14px rgba(0, 0, 0, 0.30);
    animation-play-state: paused;
  }
}

@media (prefers-reduced-motion: reduce) {
  .poi-cluster-frost__inner {
    animation: none;
  }
}

/* --- Popup expandable description (M11.X-POPUP-LAYOUT, 2026-05-20) ------
   Shared "More / Less" toggle used by pin / event / POI popupy to keep
   long descriptions readable. Vanilla JS in textExpandable.js mounts the
   toggle on popupopen — ResizeObserver in buildPopup repositions the
   popup against the new content height.

   M11.X-EXPANDABLE-LISTS (2026-05-21) — přepsáno z .popup-expandable na
   .text-expandable + CSS line-clamp. Full text vždy v DOM; `--clamped`
   modifier visually clips na N řádků (popup=5, list-view=3). Šíře
   reusable napříč popup + Yours panel + admin queue + event detail. */
.text-expandable {
  margin: 0;
}

.text-expandable p {
  margin: 0 0 0.5rem;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.text-expandable p:last-child {
  margin-bottom: 0;
}

/* Body — when --clamped is on, restrict to N lines via -webkit-line-clamp.
   Browser support 95%+ (Chrome, Safari, Firefox 68+, Edge). Line count
   comes from inline --text-expandable-lines CSS variable set per call. */
.text-expandable__body {
  /* Default = unclamped (full text visible). The --clamped modifier
     below switches to line-clamp display. */
}

.text-expandable--clamped .text-expandable__body {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--text-expandable-lines, 3);
  line-clamp: var(--text-expandable-lines, 3);
  overflow: hidden;
}

/* List-view surface — jemná max-height transition pro non-popup. Popup
   zůstává instant (per Petřin pokyn — popup repositioning + anim trhá). */
.text-expandable--list-view .text-expandable__body {
  transition: max-height 150ms ease-out;
}

/* Toggle button — looks like a link, not a system button. Inherits the
   surface's text color family by default; pin / event / POI accent
   overrides nudge it to the family hue below. */
.text-expandable__toggle {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0.35rem 0 0;
  font: inherit;
  /* More/Less is a UI control, not content — keep it in the body UI font
     (Manrope, same as field labels) even when the surrounding moment text
     uses the handwriting/serif moment voice (M12, Petřin pokyn 2026-05-27). */
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent-violet-soft);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgba(var(--accent-violet-rgb), 0.45);
  transition: color 140ms ease, text-decoration-color 140ms ease;
}

.text-expandable__toggle:hover,
.text-expandable__toggle:focus-visible {
  color: var(--accent-violet-bright);
  text-decoration-color: currentColor;
  outline: none;
}

.text-expandable__toggle:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  border-radius: 2px;
}

/* M12 Noir PR5 ② — per-entity "More/Less" link (Petřin pokyn 2026-05-25):
   the toggle takes the popup's entity tone via --place-tone (concert teal /
   landmark gold), unifying the link colour across POI / Concerts / Online /
   Pins. Supersedes the old per-namespace copper override. Non-popup surfaces
   (Yours rejected reason etc.) keep the default iris from the base rule.
   Pin popup (not yet .popup--place) keeps the default iris = moment tone. */
.popup--place .text-expandable__toggle {
  color: var(--place-tone);
  text-decoration-color: rgba(var(--place-tone-rgb), 0.5);
}
.popup--place .text-expandable__toggle:hover,
.popup--place .text-expandable__toggle:focus-visible {
  color: var(--place-tone);
  text-decoration-color: currentColor;
}

/* ==========================================================================
   Yours panel — sjednocený 4-řádkový row layout
   Petřin pokyn 2026-05-20 evening: po předchozím ad-hoc patchi Yours rows
   vypadaly rozházeně (mixed chip velikosti, "Live now" v Pending, otazníky
   v různém stylu, lepící se na levý okraj). Tato sekce definuje koherentní
   anatomy: title-row → chips-row → location-row → date-row, uniform .chip
   baseline napříč POI / ONLINE / CONCERT / SUB / TOUR / TEST / RSVP.
   ========================================================================== */

/* Yours row body — vertical stack s rytmickými mezerami.
   Position: relative tak aby title-actions cluster (absolute) mohl
   ukotvit k row's top-right hraně bez ovlivnění title-row height. */
.feed-list__item--yours {
  padding: 0.95rem 1.1rem;
  align-items: stretch;
  position: relative;
}
.feed-list__item--yours .feed-list__body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}

/* Chips-row gap — explicit margin-top dává deterministic odsazení od
   title-row's text baseline (= shodné napříč 1-line a 2-line title).
   Public tabs (= legacy renderEventItem path) mají chips-row jako
   flow-child a stejný margin-top. */
.feed-list__chips-row {
  margin-top: 0.45rem;
}
.feed-list__item--yours .feed-list__chips-row {
  margin-top: 0;
}

/* Public Concerts/Online + Pins — apply the same title-row discipline
   jako Yours: action-row floats absolute v top-right rohu řádku, takže
   title-row height = jen výška textu, gap k chips-row je shodný napříč
   1-line a 2-line title (Petřin pokyn 2026-05-20: "uprav odsazení
   stejně i na online a concerts a v pinech"). */
.feed-list__item:has(.feed-list__action-row) {
  position: relative;
}
.feed-list__item .feed-list__action-row {
  position: absolute;
  top: 0.7rem;
  right: 0.9rem;
  margin-left: 0;
}
/* Reserve right-side padding on title-row so the title text doesn't
   overlap the action area. Slightly less than Yours reserve (5.5rem)
   protože legacy action-row je 2-3 buttons + status-dot stays inline. */
.feed-list__item:has(.feed-list__action-row) .feed-list__title-row {
  padding-right: 4.5rem;
}

/* Status-dot stays inline at title-row's right edge — it's small enough
   (10px) not to drive height, and sits next to the title text where
   the eye expects an at-a-glance state indicator. */

/* Public Concerts / Online / Pins — status-dot pin doprava na hranu
   řádku (= card padding 0.9rem). Pre-a956a50 byl status-dot v title-row
   jako flex space-between child, takže byl úplně na pravém okraji. Po
   a956a50 padding-right:4.5rem rezervace pro action-row začal posouvat
   dot doleva. Petřin pokyn 2026-05-21: vrátit indikátory zpět na pravý
   okraj „s určitým odsazením". Position absolute = dot na kraji, action-
   row hover je taky absolute na stejné pozici → action-row natural-ně
   překryje dot na hover (= interactive icons win over passive status
   indicator). Yours rows mají dot hidden zcela (9479-9491), tahle
   pravidla je vynechávají selectorem. */
.feed-list__item:not(.feed-list__item--yours):not(.feed-list__item--yours-rejected):not(.feed-list__item--yours-pending) .feed-list__status-dot {
  position: absolute;
  top: 0.95rem;
  right: 0.9rem;
}

/* Location-row extra breathing room — Petřin pokyn 2026-05-20: "lokalita
   přijde hodně nalepená" k chips. */
.feed-list__item--yours .feed-list__location-row {
  margin-top: 0.15rem;
}

/* -- Row 1: title + actions cluster -------------------------------
   Title-row v Yours kontextu = blok (= ne flex), title sám určuje
   výšku řádku. Actions cluster je absolute-positioned na row's top-
   right hraně → title-row height = jen výška textu, takže gap k
   chips-row je shodný napříč 1-line a 2-line titulu (Petřin pokyn
   2026-05-20: "odsazení chips od nadpisu se liší podle počtu řádků").
*/
.feed-list__item--yours .feed-list__title-row {
  display: block;
  min-width: 0;
  /* Reserve right-side space for the absolute-positioned actions
     cluster (3 buttons × 28px + gaps ≈ 88-95px). */
  padding-right: 5.5rem;
}
.feed-list__item--yours .feed-list__title {
  display: block;
  min-width: 0;
  /* Handoff m12-list-items.md §8 .fm-your-item__title — Cormorant italic
     600, ~14px, WHITE (--text-dark). Was violet upright 1.05rem. */
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  color: var(--text-dark);
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
/* M12 (Petřin pokyn 2026-05-27) — a deleted MOMENT title in Yours is user
   content → moment voice. Same specificity as the rule above + defined AFTER
   it, so it wins and overrides the Cormorant italic (font only). Tokens only.
   --moment-hand follows --moment so short moments get handwriting. */
.feed-list__item--yours .feed-list__title--moment {
  font-family: var(--font-moment-long);
  font-style: normal;
  font-size: var(--fs-moment-roman);
  font-weight: var(--fw-regular);
  line-height: var(--lh-moment-roman);
}
.feed-list__item--yours .feed-list__title--moment-hand {
  font-family: var(--font-moment);
  font-style: normal;
  font-size: var(--fs-moment-hand);
  font-weight: var(--fw-moment-hand);
  line-height: var(--lh-moment-hand);
}

/* Actions cluster — absolutně poziciované k row's top-right rohu tak,
   aby nepřispívalo do title-row's effective height. */
.feed-list__item--yours .feed-list__title-actions {
  position: absolute;
  top: 0.85rem;
  right: 1.1rem;
  display: inline-flex;
  align-items: center;
  /* M11.X-EXPANDABLE-LISTS follow-up 2026-05-21 — Petřin pokyn: ikony
     pencil + trash od sebe (= dříve 0.15rem byly téměř na sobě). */
  gap: 0.4rem;
  z-index: 1;
}

/* Title-action button — 28×28 ghost. Matches .feed-list__action geometry
   so all icon buttons across the app feel like one family. */
.feed-list__title-action {
  appearance: none;
  font-family: inherit;
  cursor: pointer;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 140ms ease, color 140ms ease;
  flex: 0 0 auto;
}
.feed-list__title-action:hover,
.feed-list__title-action:focus-visible {
  background: rgba(238, 240, 250, 0.06);
  outline: none;
}
.feed-list__title-action--info { color: var(--danger-text); }
.feed-list__title-action--info:hover,
.feed-list__title-action--info:focus-visible {
  background: rgba(var(--danger-rgb), 0.10);
  color: var(--danger-text);
}
.feed-list__title-action--danger:hover,
.feed-list__title-action--danger:focus-visible {
  background: rgba(var(--danger-rgb), 0.10);
  color: var(--danger-text);
}
.feed-list__title-action svg {
  width: 16px;
  height: 16px;
}
.feed-list__title-action--info svg {
  /* ACTION_ICON_QUESTION is 18×18 inside its viewBox; cap to row baseline */
  width: 18px;
  height: 18px;
}

/* Title-actions cluster nested action-row (from _renderEventActionRow) —
   strip the inherited margin-left:auto so it doesn't shove inside the
   cluster; the cluster itself owns the right-anchor. Also reveal always
   (no hover-only) in Yours since these are author-own actions. */
.feed-list__title-actions .feed-list__action-row {
  /* M11.X-EXPANDABLE-LISTS follow-up 2026-05-21 — když action-row sedí
     uvnitř title-actions clusteru (= Yours Pending row), MUSÍ se chovat
     jako natural flex child clusteru, ne jako standalone absolute-
     positioned action-row (= public Concerts/Online vzor). Tím se
     pencil + trash zarovnají na stejnou pozici jako standalone trash
     v Yours Rejected (= obě cesty teď konvergují na cluster's
     right:1.1rem). Interní gap zvýšen z 0.15rem na 0.4rem aby ikony
     nebyly na sobě (Petřin pokyn). */
  position: static;
  opacity: 1;
  margin-left: 0;
  gap: 0.4rem;
}
.feed-list__title-actions .feed-list__action {
  /* Match the title-action ghost color treatment for visual consistency. */
  color: var(--text-muted);
}
.feed-list__title-actions .feed-list__action:hover,
.feed-list__title-actions .feed-list__action:focus-visible {
  background: rgba(238, 240, 250, 0.06);
}

/* -- Row 2: chips-row -------------------------------------------- */
.feed-list__chips-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

/* Chips-row is always in the DOM (the live RSVP badge may appear after a
   popup/detail attendance toggle — M15 [BUG] fix); collapse it while every
   chip inside is hidden so empty rows add no stray gap. */
.feed-list__chips-row:not(:has(> :not([hidden]))) {
  display: none;
}

/* Unified .chip baseline — applied to every pill in chips-row regardless
   of source class (.poi-chip / .event-chip / .tour-chip / etc.). The
   modifier classes still carry color/border tokens; this baseline
   guarantees equal padding/font/height across all variants.
   Petřin pokyn 2026-05-20 (iterace): "tay chips udělat menší" — font
   0.62→0.56rem, padding shrunk so the chip row reads as quiet metadata,
   not loud labels. */
.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  line-height: 1.5;
  border: 1px solid transparent;
  white-space: nowrap;
  vertical-align: middle;
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* display:inline-flex above would beat the UA [hidden] rule — keep the
   hidden state authoritative (live RSVP badge toggles it, M15 [BUG] fix). */
.chip[hidden] {
  display: none;
}
/* M12D.X-REPORT-INDICATORS (bod 5 — Petřin pokyn 2026-05-30) — list-row reported
   chip. The reported state is the LOUD one everywhere, so unlike the tinted
   status chips this is a SOLID danger badge + halo: a moderator spots a flagged
   moment while scrolling, not only after opening it. Mod-gated in JS. */
.chip.feed-list__reported-chip {
  gap: 0.25rem;
  background: var(--danger);
  color: var(--text-on-accent);
  border-color: var(--danger);
  box-shadow: var(--halo-danger);
}
.chip.feed-list__reported-chip svg { display: block; flex: none; }
/* Handoff m12-list-items.md §7 .chip-kind tone map — ONLINE=azure,
   CONCERT=teal, LIVESTREAM=mint, PLACE=gold. Online/concert WERE SWAPPED
   (Petřin pokyn 2026-05-24 "barvy chipů neodpovídají"); online iris→azure
   per Petřin pokyn 2026-05-31. */
/* Punchier chip coat (Petřin pokyn 2026-05-27 "chipy nevýrazné") — fill
   bumped 0.10→0.18 + label switched to the -bright tone so each chip reads
   as a confident badge, not a faint outline. Applied uniformly across all
   tones (iris was just the weakest, surfaced first). */
.chip--place {
  background: rgba(var(--accent-copper-rgb), 0.18);
  color: var(--accent-copper-bright);
  border-color: rgba(var(--accent-copper-rgb), 0.50);
}
.chip--online {
  /* Side-panel online type chip = azure for parity with .chip--concert (teal)
     + .chip--place (gold). Petřin pokyn 2026-05-31 ("přebarvi jen to, co si
     obdobně přebarvuje landmark a concert — má to být ekvivalentní") completes
     the unify follow-up noted in ui-standards §371; supersedes the 2026-05-29
     ADR-188 chip carve-out. */
  background: rgba(var(--accent-online-rgb), 0.18);
  color: var(--accent-online-bright);
  border-color: rgba(var(--accent-online-rgb), 0.50);
}
.chip--concert {
  background: rgba(var(--accent-turquoise-rgb), 0.18);
  color: var(--accent-turquoise-bright);
  border-color: rgba(var(--accent-turquoise-rgb), 0.50);
}
.chip--livestream {
  background: rgba(var(--success-rgb), 0.20);
  color: var(--live-bright);
  border-color: rgba(var(--success-rgb), 0.50);
}
.chip--sub {
  /* Subcategory chip stays VIOLET — secondary classification, distinct from
     the azure online world tone (.chip--online). Not all online chips are
     azure (Petřin pokyn 2026-05-31 "není účelem mít všechny chipy azure"). */
  background: rgba(var(--accent-violet-rgb), 0.18);
  color: var(--accent-violet-bright);
  border-color: rgba(var(--accent-violet-rgb), 0.48);
  font-weight: 600;
}
.chip--tour {
  /* Handoff m12-list-items.md §3 + rulebook (iris = tour chipy) — tour name
     chips are IRIS (violet), not teal. This rule sits after .tour-chip so it
     is the one that actually paints; keep them in sync. */
  background: rgba(var(--accent-violet-rgb), 0.20);
  color: var(--accent-violet-bright);
  border-color: rgba(var(--accent-violet-rgb), 0.55);
  box-shadow: 0 0 10px rgba(var(--accent-violet-rgb), 0.30);
}

/* M11.X-DELETED-PINS (ADR-156) — pin chip. Identifies pin entity in
   Yours/Deleted Pins rows (= mixed view with POI chip; not currently
   used elsewhere, ready for forward parity). Muted blue-grey tone so
   it doesn't compete with POI copper or event turquoise/violet — pin
   is the most generic entity, neutral surface matches that role. */
.chip.pin-chip {
  background: rgba(238, 240, 250, 0.06);
  color: var(--text-muted);
  border-color: rgba(238, 240, 250, 0.20);
}

/* M11.X-DELETED-PINS V2 (2026-05-23) — deletion category chip. Renders
   next to [PIN] on Yours/Deleted Pins rows when the delete came from
   the reports queue (= carries the reporter's flagged category like
   Spam / Inappropriate / Hate speech / Personal info / Other). Red
   --danger variant signals "removed for cause" at a glance and stays
   visually distinct from neutral [PIN]. Per Petřin pokyn 2026-05-23:
   replaces the unreadable composite "Inline delete via reports queue —
   pin removed. Reporter reason: X" string. */
.chip.deletion-category-chip {
  background: rgba(var(--danger-rgb), 0.10);
  color: var(--danger-text);
  border-color: rgba(var(--danger-rgb), 0.32);
}

/* When legacy chip classes also carry .chip, force the baseline padding/
   font over their old declarations (which were 0.7rem font / 0.15rem
   0.55rem padding). */
.chip.poi-chip,
.chip.tour-chip,
.chip.feed-list__rsvp-badge,
.chip.feed-list__subcategory {
  padding: 0.1rem 0.5rem;
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.10em;
  line-height: 1.5;
}

/* TEST pill (class .test-data-pill, from testDataBadge.js) sits in the
   chips-row alongside other chips. Force baseline dimensions so it
   matches its siblings (its default global style is slightly larger). */
.feed-list__chips-row .test-data-pill {
  font-size: 0.56rem;
  letter-spacing: 0.10em;
  padding: 0.1rem 0.5rem;
  line-height: 1.5;
  font-weight: 700;
}
/* RSVP badge variants — identical chips-row treatment in BOTH the public
   Concerts/Online feed and the Yours/Saved feed (Petřin pokyn 2026-05-25:
   chip přesunut z meta-řádku do chips-row, sjednoceno napříč). The .chip
   baseline resets border to transparent, so these chips-row-scoped rules
   (0,2,0) re-assert the per-state fill + border. Going = turquoise solid
   ("I'll be there"); Attended = muted slate. (Interested chip removed
   2026-06-04 — the like heart carries that state.) */
.feed-list__chips-row .feed-list__rsvp-badge--going {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  border-color: var(--accent-turquoise);
}
.feed-list__chips-row .feed-list__rsvp-badge--attended {
  background: rgba(238, 240, 250, 0.08);
  color: var(--text-muted);
  border-color: rgba(238, 240, 250, 0.20);
}

/* -- Row 3: location-row (flag + city · venue) ------------------- */
.feed-list__location-row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dark);
  opacity: 0.78;
  min-width: 0;
}
.feed-list__location-row .feed-list__flag {
  flex: 0 0 auto;
}
.feed-list__location-row .feed-list__where {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* -- Row 4: date-row (right-aligned) ----------------------------- */
.feed-list__date-row {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* M13.X-EVENT-LIKE-LIST — when the Yours row carries the like heart, split the
   date line: heart LEFT, date RIGHT (parity with the Concerts/Online + Echo feed
   rows, whose --right meta-row does the same via :has(.echo-like)). The date text
   keeps its right alignment inside its own span. */
.feed-list__date-row:has(.echo-like) {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

/* Past row in Yours — only a subtle title dim (NO whole-row saturate; Petřin pokyn
   2026-05-27 — keep the row colour + interactive, archive signalled by PAST chip). */
.feed-list__item--yours.feed-list__item--past .feed-list__title { opacity: 0.7; }

/* STANDALONE chip stays hidden in Yours (mirror existing rule with the
   new --yours base class so any future yours-* row variant inherits). */
.feed-list__item--yours .tour-chip--standalone {
  display: none !important;
}

/* Live-banner / status-dot leaks — Yours rows never render these via the
   new builder, but if a legacy code path adds one anyway, hide it
   inside any --yours row so a stray "LIVE NOW" dot can't appear in the
   submissions feed (Petřin pokyn 2026-05-20). */
.feed-list__item--yours .feed-list__status-dot,
.feed-list__item--yours .feed-list__mod-badge {
  display: none !important;
}

/* Inside the new chips-row the gap handles spacing; the legacy
   margin-left:0.35rem on .feed-list__tour-chip would shove it. Reset. */
.feed-list__item--yours .feed-list__chips-row .feed-list__tour-chip {
  margin-left: 0;
}

/* ==========================================================================
   Yours Submissions sub-section headers (Pending / Rejected)
   Petřin pokyn 2026-05-20 evening — "frosty design, moderní", aby zapadalo.
   Parent "Your Submissions" už používá .events-section__title (Cormorant
   uppercase teal). Sub-headings jsou semantically <h4> a měly default UA
   styling = looked broken. Tato sekce dělá:
     • Cormorant 600 uppercase, menší než parent (= hierarchy signal)
     • Tiny accent dot (copper pro pending, danger pro rejected) — state
       hint at-a-glance bez per-row badge duplikace
     • Hair-line separator + jemný atmospheric wash background v sub-
       sekci, aby Pending vs Rejected čteli jako vlastní atmospheric
       "rooms" pod parent headingem
   ========================================================================== */

/* Parent heading "Your Submissions" — display-scale Cormorant with an
   atmospheric fade-down background that visually anchors the sub-sections
   below (Petřin pokyn 2026-05-20: "Your Submissions mi připadá oddělené,
   udělej z toho nadpis + ikonu pryč"). The fade ties the header to the
   sub-section bands underneath, creating a "submitted by you" room. */
.events-section__title--yours-submissions {
  /* Handoff m12-list-items.md §8 (mockup 23) — "YOUR SUBMISSIONS" is a plain
     Cormorant italic heading aligned with the group boxes below, NOT a
     full-width tinted band (Petřin pokyn 2026-05-24 "nalepený, má být jako
     box" = same left gutter as the boxes, no edge-to-edge banner). */
  padding: 0.9rem 0.7rem 0.5rem;
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  color: var(--accent-violet-soft);
  display: block;
}
.events-section__title--yours-submissions::before {
  display: none;
}

/* Pull the first sub-section flush against the parent — no air gap so
   the fade-down reads as continuous. */
.events-section--yours-submissions > .events-section--yours-pending:first-of-type,
.events-section--yours-submissions > .events-section:first-of-type {
  margin-top: 0;
}

/* Sub-section header sits on its own band — solid tinted fill (per state)
   so the label reads as a distinct section divider, not muted body text.
   Petřin pokyn 2026-05-20 evening: "ztrácí se, nevypadá to jako nadpis". */
.events-section__subtitle {
  margin: 0;
  padding: 0.7rem 1.05rem 0.65rem;
  font-family: var(--font-display);
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--text-dark);
  display: flex;
  align-items: center;
  gap: 0.6rem;
  position: relative;
  border-top: 1px solid transparent;
  border-bottom: 1px solid transparent;
}

/* Accent dot — larger than first iteration so it reads as a deliberate
   state token, with a soft outer ring for the frost glow feel. */
.events-section__subtitle::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--text-muted);
  flex-shrink: 0;
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.06);
}

/* M11.X-EXPANDABLE-LISTS follow-up 2026-05-21 — Petřin pokyn: Pending
   + Rejected sub-sekce sbalitelné, chevron stejné velikosti jako filtry
   v adminu (audit-filter-pill__chevron pattern: SVG 0.9rem, fill, 180°
   rotate on collapse). Button uvnitř h4 zachová semantic heading
   hierarchy + interactive affordance. */
/* Handoff m12-list-items.md §8 .fm-status-group__count — count pill in the
   band head, tone-colored (inherits the band's currentColor). Hidden when 0. */
.events-section__subtitle-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  padding: 0 0.35rem;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  color: currentColor;
  background: rgba(238, 240, 250, 0.08);
  border: 1px solid currentColor;
}
.events-section__subtitle-count:empty {
  display: none;
}

/* M12.X-YOURS-INDICATORS — per-section status pip in the band head, mirroring
   the Yours-tab dots so the user sees WHICH box lit the tab. Sits between the
   count pill and the chevron. Attention = the moderation dot (rejected /
   deleted); live = mint, a saved event on air (canonical --live, parity with
   .events-section__dot--live). Toggled via the hidden attribute in JS. */
.events-section__pip {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  pointer-events: none;
}
.events-section__pip[hidden] {
  display: none;
}
.events-section__pip--attention {
  background: var(--danger-bright);
  box-shadow: 0 0 7px 2px rgba(var(--danger-rgb), 0.9);
}
.events-section__pip--live {
  background: var(--live);
  box-shadow: 0 0 7px 2px rgba(var(--live-rgb), 0.9);
}

.events-section__subtitle-toggle {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: 1 1 auto;
  min-width: 0;
  text-align: left;
}

.events-section__subtitle-toggle:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  border-radius: 2px;
}

.events-section__subtitle-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 0.9rem;
  height: 0.9rem;
  margin-left: auto;
  color: currentColor;
  transition: transform 140ms ease;
  flex-shrink: 0;
}

.events-section__subtitle-chevron svg {
  width: 100%;
  height: 100%;
  fill: currentColor;
}

/* Collapsed = chevron rotates -90° (= points right), list hides. */
.events-section__subtitle-toggle[aria-expanded="false"] .events-section__subtitle-chevron {
  transform: rotate(-90deg);
}

.events-section--yours-pending[data-collapsed="true"] .feed-list,
.events-section--yours-rejected[data-collapsed="true"] .feed-list,
.events-section--yours-deleted[data-collapsed="true"] .feed-list,
.events-section--yours-saved[data-collapsed="true"] .feed-list {
  display: none;
}

/* Pending — warm copper band. */
.events-section--yours-pending .events-section__subtitle {
  color: var(--accent-copper);
  background: linear-gradient(
    180deg,
    rgba(var(--accent-copper-rgb), 0.16) 0%,
    rgba(var(--accent-copper-rgb), 0.09) 100%
  );
  /* No top border — the box outline caps the band; a band border-top here
     would read as a doubled top edge (Petřin "nečistý start" 2026-05-25). */
  border-bottom-color: rgba(var(--accent-copper-rgb), 0.18);
  /* Handoff §8 — 3px tone accent on the left edge of the band head. */
  border-left: 3px solid var(--accent-copper);
}
.events-section--yours-pending .events-section__subtitle::before {
  background: var(--accent-copper);
  box-shadow: 0 0 0 3px rgba(var(--accent-copper-rgb), 0.20);
}

/* Rejected — danger red band. */
.events-section--yours-rejected .events-section__subtitle {
  /* Brighter rose --danger (was --danger-deep, read dull/dark vs the gold
     PENDING band) — Petřin pokyn 2026-05-24 "rejected má jinou barvu". */
  color: var(--danger-text);
  background: linear-gradient(
    180deg,
    rgba(var(--danger-rgb), 0.14) 0%,
    rgba(var(--danger-rgb), 0.07) 100%
  );
  border-bottom-color: rgba(var(--danger-rgb), 0.18);
  border-left: 3px solid var(--danger);
}
.events-section--yours-rejected .events-section__subtitle::before {
  background: var(--danger);
  box-shadow: 0 0 0 3px rgba(var(--danger-rgb), 0.20);
}

/* ── Yours status-group boxes — shared geometry (mockup 23 §8). ──────────
   padding:0 overrides the inherited `.events-section { padding: 0.4rem 0 }`
   so the coloured header band sits FLUSH against the box top. That inherited
   padding was painting a strip of box-fill above (and below) each band — the
   "grey strip / container nezačíná čistě" Petra reported 2026-05-25.
   Each box also carries a faint per-category tint (set below) so the band is
   saturated and the box a whisper of the same hue — at-a-glance orientation
   per mockup 23 (Pending warm, Rejected brick, Saved magenta, Deleted ash).
   Depth on the dark panel comes from the tint gradient + tinted hairline, NOT
   a drop shadow (black shadow is invisible on dark — memory
   reference_depth_on_dark_needs_gradient_not_shadow). */
.events-section--yours-saved,
.events-section--yours-pending,
.events-section--yours-rejected,
.events-section--yours-deleted {
  margin: 0 0.7rem 0.6rem;
  padding: 0;
  border: 1px solid var(--hairline-dark-lo);
  border-radius: var(--radius-md);
  overflow: hidden;
}

/* Pending — whisper of the copper band hue across the box. */
.events-section--yours-pending {
  background: linear-gradient(
    180deg,
    rgba(var(--accent-copper-rgb), 0.07) 0%,
    rgba(var(--accent-copper-rgb), 0.025) 100%
  );
  border-color: rgba(var(--accent-copper-rgb), 0.18);
}

/* Rejected — whisper of brick danger. */
.events-section--yours-rejected {
  background: linear-gradient(
    180deg,
    rgba(var(--danger-rgb), 0.07) 0%,
    rgba(var(--danger-rgb), 0.025) 100%
  );
  border-color: rgba(var(--danger-rgb), 0.16);
}

/* M12 Noir PR4 — rows under the coloured section bands are now plain
   cards (mockup 23): the band carries the state colour, the cards stay
   neutral so the colour load doesn't read as "barevný šum". The old
   per-section zebra/hover tints are retired (base .feed-list__item card
   + hover apply). */

/* M11.X-DELETED-PINS (ADR-156) — Deleted Pins sub-section.
   Muted bluish-grey band instead of another red shade — keeps the
   visual hierarchy clear (Pending=copper warning, Rejected=danger,
   Deleted=archival/archeological) so the row doesn't read as a third
   layer of "barevný cirkus" (per feedback_related_ctas_same_hue
   adjacent reasoning + Petřin "co bylo, je pryč" framing).
   Accent token: --text-muted (= already existing greyed tone used
   in muted body text). */
.events-section--yours-deleted .events-section__subtitle {
  color: var(--text-muted);
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0.10) 0%,
    rgba(0, 0, 0, 0.04) 100%
  );
  border-bottom-color: rgba(238, 240, 250, 0.10);
  border-left: 3px solid var(--text-muted);
}
.events-section--yours-deleted .events-section__subtitle::before {
  background: var(--text-muted);
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.10);
}

/* Deleted — archival ash; cool neutral whisper, no warm/danger hue so it
   reads as "gone / archived", not a third alert layer (mockup 23 grey group). */
.events-section--yours-deleted {
  background: linear-gradient(
    180deg,
    rgba(238, 240, 250, 0.04) 0%,
    rgba(238, 240, 250, 0.015) 100%
  );
  border-color: rgba(238, 240, 250, 0.13);
}

/* Saved events — magenta band in the lighter "my moments" marker magenta
   (--accent-mine), tying the section to your own-moment identity. NOT the
   deeper heart magenta — it read too dark for the band title (Petřin pokyn
   2026-05-25). Same .fm-status-group box (handoff §8) as Pending/Rejected/
   Deleted, but it sits at the TOP of the Yours pane (= your forward-looking
   collection, above the moderation-history "Your Submissions" groups). The
   going chips inside stay turquoise, interested chips share this magenta —
   the band is the section identity, chips are per-row state. */
.events-section--yours-saved .events-section__subtitle {
  color: var(--accent-mine);
  background: linear-gradient(
    180deg,
    rgba(var(--accent-mine-rgb), 0.16) 0%,
    rgba(var(--accent-mine-rgb), 0.09) 100%
  );
  border-bottom-color: rgba(var(--accent-mine-rgb), 0.18);
  border-left: 3px solid var(--accent-mine);
}
.events-section--yours-saved .events-section__subtitle::before {
  background: var(--accent-mine);
  box-shadow: 0 0 0 3px rgba(var(--accent-mine-rgb), 0.20);
}
/* Saved — whisper of own-moment magenta, tying the box to your identity. */
.events-section--yours-saved {
  background: linear-gradient(
    180deg,
    rgba(var(--accent-mine-rgb), 0.07) 0%,
    rgba(var(--accent-mine-rgb), 0.025) 100%
  );
  border-color: rgba(var(--accent-mine-rgb), 0.18);
}

/* M12 Noir PR4 — deleted rows are plain cards too (band carries the
   archival grey identity); old per-section zebra/hover tints retired. */

/* ---------------------------------------------------------------
 * M11.X-ADMIN-VIEW-PAGING Fáze 3 (ADR-142 R4) — invisible sentinel
 * appended at the end of each public infinite-scroll context (Pins
 * panel, Concerts pane, Online pane, Yours sub-sections). Used by
 * frontend/js/infiniteScroll.js with IntersectionObserver to trigger
 * the next cursor page. No visible chrome — UX is "seamless scroll".
 * --------------------------------------------------------------- */
.feed-list__sentinel {
  display: block;
  min-height: 1px;
  margin: 0;
  padding: 0;
  pointer-events: none;
}
.feed-list__sentinel[hidden] {
  display: none;
}

/* ---------------------------------------------------------------
 * Admin Audit log polish (Petřin pokyn 2026-05-21).
 * Fixed column widths + structured two-line cells + filter bar.
 * Scoped to the audit pane so the existing reports/events tables
 * keep their fluid layout.
 * --------------------------------------------------------------- */

.admin-toolbar--audit {
  align-items: flex-start;
}

.audit-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
}

.audit-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  position: relative;
  font-size: 0.88rem;
  color: var(--text-dark);
}

.audit-filter--actions > .audit-filter__summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  padding: 0.42rem 0.75rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
}
.audit-filter--actions > .audit-filter__summary::-webkit-details-marker {
  display: none;
}
.audit-filter--actions > .audit-filter__summary::after {
  content: "▾";
  font-size: 0.8em;
  color: var(--text-muted);
}
.audit-filter--actions[open] > .audit-filter__summary {
  border-color: var(--accent-turquoise);
}

.audit-filter__count {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  font-size: 0.72rem;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  font-weight: 500;
}

.audit-filter__panel {
  position: absolute;
  top: calc(100% + 0.3rem);
  left: 0;
  z-index: 60;
  min-width: 280px;
  max-height: 360px;
  overflow-y: auto;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 0.5rem 0;
}

.audit-filter__panel-head {
  display: flex;
  justify-content: flex-end;
  padding: 0 0.6rem 0.4rem;
  border-bottom: 1px solid var(--border-on-light);
  margin-bottom: 0.3rem;
}

.audit-filter__clear-btn {
  background: transparent;
  border: none;
  color: var(--accent-turquoise);
  font-size: 0.82rem;
  cursor: pointer;
  padding: 0.15rem 0.4rem;
  border-radius: var(--radius-sm);
}
.audit-filter__clear-btn:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.08);
}

.audit-filter__group-heading {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  padding: 0.4rem 0.75rem 0.2rem;
}

.audit-filter__option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.32rem 0.75rem;
  cursor: pointer;
  font-size: 0.88rem;
}
.audit-filter__option:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.06);
}
.audit-filter__option input[type="checkbox"] {
  margin: 0;
  accent-color: var(--accent-turquoise);
}

.audit-filter__select {
  background: var(--cream-surface);
  color: var(--text-dark);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  padding: 0.42rem 0.7rem;
  font-size: 0.88rem;
  font-family: var(--font-body);
}
.audit-filter__select:focus {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
  border-color: var(--accent-turquoise);
}

.audit-filter__date {
  background: var(--cream-surface);
  color: var(--text-dark);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  padding: 0.36rem 0.5rem;
  font-size: 0.85rem;
  font-family: var(--font-body);
}
.audit-filter__sep {
  color: var(--text-muted);
}

/* Audit table — fixed layout so column widths are honoured + long
   target IDs / reasons wrap cleanly inside their cell instead of
   blowing the action column out of its slot. */
.admin-table--audit {
  table-layout: fixed;
  width: 100%;
}
.admin-table--audit thead th {
  white-space: nowrap;
}
.admin-table--audit .admin-table__col-when {
  /* Petřin pokyn 2026-05-21 — bumped 9.5rem → 11.5rem so date+time
     stack stays on two clean lines without wrapping. */
  width: 11.5rem;
  white-space: nowrap;
}
.admin-table--audit .admin-table__col-actor {
  width: 7rem;
}
.admin-table--audit .admin-table__col-action {
  width: 9rem;
}
.admin-table--audit .admin-table__col-target {
  width: 9rem;
}
.admin-table--audit .admin-table__col-reason {
  /* Petřin pokyn 2026-05-21 — Details přetékají + tlačí Actions za roh.
     Cap to ~3 lines using line-clamp; full text accessible via the
     audit detail dialog (lupa icon) so we're not losing information. */
  width: auto;
}
.admin-table--audit td.admin-table__col-reason {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: 4.8rem;
  line-height: 1.4;
}
.admin-table--audit td {
  vertical-align: top;
  word-break: break-word;
  overflow-wrap: anywhere;
}

/* Legacy UUID-chip style kept for the post-deploy cache-gap window
   when an old bundle might still try to render a `code.audit-target`.
   New bundles emit `<span class="audit-target">` with a kind chip +
   label instead (no monospace UUID anywhere). */
.admin-table--audit .admin-table__col-target code.audit-target {
  display: inline-block;
  font-size: 0.78rem;
  background: rgba(0, 0, 0, 0.04);
  padding: 0.12rem 0.4rem;
  border-radius: var(--radius-sm);
  color: var(--text-dark);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Round-8 Target chip — backend ships (target_kind, target_label).
   The cell renders as a small kind chip followed by the readable
   label. No UUIDs anywhere; when target_label is null (hard-deleted
   target), only the kind chip is shown. */
.admin-table--audit .audit-target {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  max-width: 100%;
  font-family: inherit;       /* override the legacy monospace */
  font-size: inherit;
  color: var(--text-dark);
}
.audit-target-kind {
  display: inline-block;
  flex: 0 0 auto;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--cream-mist);
  color: var(--text-dark);
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  /* M12 PR6 Dávka E — undefined --border-soft black fallback was invisible
     on Noir (same pattern as audit-detail field-chip). Light hairline. */
  border: 1px solid var(--border-on-light);
}
.audit-target-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.88rem;
  color: var(--text-dark);
}

.admin-table--audit .audit-target--derived {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-style: italic;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.audit-action {
  font-weight: 500;
  font-size: 0.88rem;
}

.audit-cell__primary {
  font-size: 0.92rem;
  line-height: 1.35;
}
.audit-cell__secondary {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin-top: 0.2rem;
  line-height: 1.3;
}

/* Audit row "show full record" affordance per Petřin pokyn 2026-05-21.
   Narrow last column carries a single ⋯ button that opens the
   admin-audit-detail-dialog with every field unfolded. */
.admin-table--audit .admin-table__col-actions {
  /* Wide enough for the "ACTIONS" header label not to clip
     (Petřin pokyn 2026-05-21 — Manrope bold uppercase + letter-spacing
     at 0.85rem needs ~5.5rem column). */
  width: 5.5rem;
  text-align: center;
}
.audit-detail-btn {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.audit-detail-btn:hover,
.audit-detail-btn:focus-visible {
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border-color: var(--accent-turquoise);
  color: var(--text-dark);
  outline: none;
}

/* Detail dialog layout — label + value pairs stacked. Long target
   IDs and raw reasons can break out of words but never overflow the
   dialog. Switched to a smaller label column + min-width:0 on the
   value cell per Petřin pokyn 2026-05-21 ("vnitřek je oříznutý").
   Labels lose the all-small-caps glyph so the missing-key fallback
   doesn't render as "ADMIN.AUDIT.DETAIL.WHEN" before en.json catches
   up on the user's browser. */
.admin-audit-detail {
  /* .frost-modal sets padding:0 and relies on an inner <form> element
     to carry the padding (1.5rem 1.4rem). This dialog has no form
     wrap (read-only), so we re-add the inner padding here per Petřin
     pokyn 2026-05-21 ('obsah nalepený na okraje, tlačítko uříznuté'). */
  max-width: 760px;
  width: min(760px, 94vw);
  padding: 1.5rem 1.4rem 1.4rem;
}
.admin-audit-detail .form-actions {
  /* Inset the footer slightly from the dialog edge so the Close pill
     button doesn't crop against the rounded corner. */
  padding-top: 0.4rem;
}
.admin-audit-detail__body {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  margin: 0.75rem 0 1.25rem;
  max-width: 100%;
}
.admin-audit-detail__row {
  /* Wider label column so multi-word headers (REJECTION REASON,
     FIELDS CHANGED, ACTIVE CONCERTS, …) fit without overflowing
     into the value column — that overflow caused the "label glued
     to value" effect Petra called out in the round-8 screenshot. */
  display: grid;
  grid-template-columns: 9rem minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
.admin-audit-detail__label {
  font-family: var(--font-display);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  /* Defensive wrap for any future label that exceeds the 9rem
     column — better to break to 2 lines than overflow the value. */
  word-wrap: break-word;
  overflow-wrap: anywhere;
  white-space: nowrap;
}
.admin-audit-detail__value {
  font-size: 0.92rem;
  color: var(--text-dark);
  word-break: break-word;
  overflow-wrap: anywhere;
  min-width: 0;
}
.admin-audit-detail__target-kind {
  display: inline-block;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--cream-mist);
  color: var(--text-muted);
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  margin-right: 0.4rem;
  vertical-align: middle;
}
.admin-audit-detail__target-id {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.82rem;
}
.admin-audit-detail__details {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}
.admin-audit-detail__details-primary {
  font-size: 0.95rem;
  color: var(--text-dark);
  line-height: 1.35;
}
.admin-audit-detail__details-secondary {
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.3;
}
.admin-audit-detail__details-plain {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.85rem;
  background: var(--cream-mist);
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  white-space: pre-wrap;
  color: var(--text-dark);
}

.admin-audit-detail__pairs {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-top: 0.4rem;
  padding-top: 0.5rem;
  border-top: 1px dashed var(--border-on-light);
}
.admin-audit-detail__pair {
  display: flex;
  gap: 0.45rem;
  align-items: baseline;
}
.admin-audit-detail__pair-key {
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--accent-turquoise);
  min-width: 6rem;
  font-size: 0.85rem;
}
.admin-audit-detail__pair-val {
  font-size: 0.9rem;
  word-break: break-word;
}
.admin-audit-detail__pair-val--list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}
.admin-audit-detail__pair-item {
  word-break: break-all;
  overflow-wrap: anywhere;
}
.admin-audit-detail__pair-item a {
  color: var(--accent-turquoise);
  text-decoration: none;
}
.admin-audit-detail__pair-item a:hover,
.admin-audit-detail__pair-item a:focus-visible {
  text-decoration: underline;
}
.admin-audit-detail__raw {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.82rem;
  background: var(--cream-mist);
  padding: 0.5rem 0.6rem;
  border-radius: var(--radius-sm);
  white-space: pre-wrap;
}

/* Per-action field rendering (Petřin pokyn 2026-05-21 round 7 — flat
   per-action layout replacing the generic primary/secondary Details
   block). Field values mount under the same __value column as the
   header rows, with type-specific visual treatment. */
.admin-audit-detail__value--list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}
.admin-audit-detail__list-item {
  word-break: break-all;
  overflow-wrap: anywhere;
}
.admin-audit-detail__list-item a {
  color: var(--accent-turquoise);
  text-decoration: none;
}
.admin-audit-detail__list-item a:hover,
.admin-audit-detail__list-item a:focus-visible {
  text-decoration: underline;
}
.admin-audit-detail__value--longtext {
  white-space: pre-wrap;
  line-height: 1.4;
}
.admin-audit-detail__value--transition {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.88rem;
  color: var(--text-muted);
}
.admin-audit-detail__field-chip {
  display: inline-block;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  background: var(--cream-mist);
  color: var(--text-dark);
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  /* M12 PR6 Dávka E — --border-soft is undefined → its black fallback was
     invisible on the Noir dark surface (Dávka B incident pattern). Use the
     canonical light hairline. */
  border: 1px solid var(--border-on-light);
}

@media (max-width: 540px) {
  .admin-audit-detail__row {
    grid-template-columns: 1fr;
    gap: 0.25rem;
  }
}

/* ---------------------------------------------------------------
 * Reddit-style audit filter pills + dialogs (Petřin pokyn 2026-05-21
 * round 4). Four independent rounded oval buttons in the toolbar,
 * each opens its own <dialog>.
 * --------------------------------------------------------------- */

.audit-filter-pill {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.42rem 0.95rem;
  border: 1px solid var(--border-on-light);
  background: var(--cream-surface);
  color: var(--text-dark);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease;
}
.audit-filter-pill:hover,
.audit-filter-pill:focus-visible {
  background: var(--cream-mist);
  border-color: var(--text-dark);
  outline: none;
}
.audit-filter-pill__icon {
  font-size: 0.95rem;
  line-height: 1;
}
.audit-filter-pill__value {
  color: var(--accent-turquoise);
  font-weight: 600;
}
/* "label · value" — a muted middot separates the static label from its live
   value so every filter pill reads the same across tabs (Petřin pokyn
   2026-05-28). Only when a value is present (the multi-select Actions pill
   uses a count badge instead and stays dot-less). */
.audit-filter-pill__value:not(:empty)::before {
  content: "·";
  margin: 0 0.35rem 0 0.05rem;
  color: var(--text-muted);
  font-weight: 400;
}
.audit-filter-pill__count {
  background: var(--accent-turquoise);
  color: var(--text-on-accent);
  font-size: 0.72rem;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  font-weight: 500;
}
.audit-filter-pill__chevron {
  /* Petřin pokyn 2026-05-21 round 6 — chevron unified to an SVG so it
     renders consistently at the larger size (text glyphs differ a lot
     across fonts + zoom levels). Filled to read as an affordance, not
     a decorative line. Rotates 180° when the dropdown is open. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 0.9rem;
  height: 0.9rem;
  margin-left: 0.15rem;
  color: var(--accent-turquoise);
  transition: transform 140ms ease;
}
.audit-filter-pill__chevron svg {
  width: 100%;
  height: 100%;
  fill: currentColor;
}
.audit-filter-pill[aria-expanded="true"] .audit-filter-pill__chevron {
  transform: rotate(180deg);
}

/* Anchored filter dropdown pattern (Petřin pokyn 2026-05-21 — replaced
   the modal `<dialog>` open behaviour with an inline anchored popover
   under each filter pill). Used by audit log filters + Test Data filters
   + future admin lists. Same multi-select content; only the opening UX
   changed. Shared adminFilterDropdown.js module wires open/close +
   outside-click + Escape. */

.filter-pill-wrap {
  position: relative;
  display: inline-block;
}

.filter-dropdown {
  position: absolute;
  top: calc(100% + 0.4rem);
  left: 0;
  z-index: 200;
  /* Width adapts to content per Petřin pokyn 2026-05-21 — short filters
     (Time / Role / Status / Type) render narrow; wide filters (Actions
     search) opt into the --actions modifier below. */
  width: max-content;
  min-width: 160px;
  max-width: min(420px, 92vw);
  padding: 0.85rem 1rem 0.75rem;
  background: var(--cream-base);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.16),
              0 2px 4px rgba(0, 0, 0, 0.06);
  font-family: var(--font-body);
}
.filter-dropdown[hidden] {
  display: none;
}
.filter-dropdown--align-right {
  /* Flipped horizontally by adminFilterDropdown.js when the natural
     left-aligned position would clip past the viewport's right edge. */
  left: auto;
  right: 0;
}
.filter-dropdown--above {
  /* Flipped vertically by adminFilterDropdown.js when the dropdown
     would push past the viewport bottom AND there's more room above
     the pill than below (M12.X-MOBILE-POPUPS audit 2026-05-28). */
  top: auto;
  bottom: calc(100% + 0.4rem);
}
.filter-dropdown--actions {
  /* Audit actions filter has a search + grouped checkboxes (~40 actions)
     so it needs more width than the simple radio dropdowns. */
  min-width: 360px;
  max-width: min(420px, 94vw);
}
.filter-dropdown__body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
}
.filter-dropdown__search {
  margin-bottom: 0.7rem;
}
.filter-dropdown__search input,
.filter-dropdown--username input[type="text"],
.audit-time-custom input[type="datetime-local"] {
  width: 100%;
  padding: 0.55rem 0.85rem;
  border: 1px solid var(--border-on-light);
  border-radius: 999px;
  background: var(--cream-mist);
  font-size: 0.92rem;
  font-family: var(--font-body);
  color: var(--text-dark);
}
.filter-dropdown__search input:focus,
.filter-dropdown--username input[type="text"]:focus,
.audit-time-custom input[type="datetime-local"]:focus {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 1px;
  border-color: var(--accent-turquoise);
}
.filter-dropdown__footer {
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.4rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border-on-light);
}
.filter-dropdown__footer .btn-primary {
  border-radius: 999px;
}
.filter-dropdown__reset {
  appearance: none;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.88rem;
  text-align: center;
  cursor: pointer;
  padding: 0.35rem 0.6rem;
  border-radius: var(--radius-sm);
}
.filter-dropdown__reset:hover {
  background: var(--cream-mist);
  color: var(--text-dark);
}

/* Mobile — center under the pill instead of overflowing horizontally. */
@media (max-width: 640px) {
  .filter-dropdown {
    /* Reserve a thin viewport margin; if the pill is near the edge the
       --align-right modifier kicks in, otherwise center-shift. */
    width: min(380px, calc(100vw - 1.6rem));
  }
}

.audit-time-option,
.audit-actions-master,
.audit-actions-group__option {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.3rem;
  cursor: pointer;
  font-size: 0.92rem;
}
.audit-time-option:hover,
.audit-actions-master:hover,
.audit-actions-group__option:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.06);
  border-radius: var(--radius-sm);
}
.audit-time-option input,
.audit-actions-master input,
.audit-actions-group__option input,
.audit-actions-group__master {
  accent-color: var(--accent-turquoise);
  margin: 0;
}
.audit-actions-master {
  font-weight: 600;
  padding: 0.5rem 0.3rem;
  border-bottom: 1px solid var(--border-on-light);
  margin-bottom: 0.4rem;
}
.audit-time-custom {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.3rem 0 0;
}
.audit-time-custom label {
  display: flex;
  flex-direction: column;
  font-size: 0.82rem;
  color: var(--text-muted);
  gap: 0.25rem;
}

/* Hierarchical Actions dialog — entity groups with colored pill chips,
   chevron expand, per-group master checkbox. */
.audit-actions-groups {
  max-height: 320px;
  overflow-y: auto;
  margin-bottom: 1rem;
  padding-right: 0.25rem;
}
.audit-actions-group {
  border-bottom: 1px solid var(--border-on-light);
}
.audit-actions-group:last-of-type {
  border-bottom: none;
}
.audit-actions-group__head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.2rem;
  cursor: pointer;
  list-style: none;
}
.audit-actions-group__head::-webkit-details-marker {
  display: none;
}
.audit-actions-group__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.18rem 0.65rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  flex: 1;
  min-width: 0;
}
.audit-actions-group__chip-icon { font-size: 0.95rem; }
/* M12 PR6 cross-dialog — entity category chips aligned to the established
   per-entity colour language used by the side-panel + popup chips
   (.chip-uppercase tones): tone-coloured text on a faint tone tint, no
   hardcoded rgba. Mapping mirrors the worlds — concert=teal, moment=magenta,
   landmark=copper, tours=iris; meta categories take utility tones —
   users=brick (discipline), reports=warm gold (attention, landmark family
   per the original two-warm-tones intent), admin=muted neutral. */
.audit-actions-group__chip--pins    { background: rgba(var(--accent-mine-rgb), 0.14);      color: var(--accent-mine); }
.audit-actions-group__chip--users   { background: rgba(var(--danger-rgb), 0.14);           color: var(--danger-text); }
.audit-actions-group__chip--events  { background: rgba(var(--accent-turquoise-rgb), 0.16); color: var(--accent-turquoise); }
.audit-actions-group__chip--tours   { background: rgba(var(--accent-violet-rgb), 0.16);    color: var(--accent-violet-soft); }
.audit-actions-group__chip--places  { background: rgba(var(--accent-copper-rgb), 0.16);    color: var(--accent-copper); }
.audit-actions-group__chip--reports { background: rgba(var(--accent-copper-rgb), 0.24);    color: var(--accent-copper); }
.audit-actions-group__chip--admin   { background: rgba(238, 240, 250, 0.06);               color: var(--text-muted); }
.audit-actions-group__count {
  color: var(--text-muted);
  font-size: 0.8rem;
}
.audit-actions-group__chevron {
  /* Match the pill chevron bump (Petřin pokyn 2026-05-21 round 5). */
  color: var(--accent-turquoise);
  font-size: 0.75rem;
  line-height: 1;
  transition: transform 160ms ease;
}
.audit-actions-group[open] > .audit-actions-group__head .audit-actions-group__chevron {
  transform: rotate(180deg);
}
.audit-actions-group__sub {
  padding: 0.2rem 0 0.6rem 2.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

/* Actor role chip rendered in the Actor cell next to @username. */
.audit-actor {
  margin-right: 0.45rem;
}
.audit-role-badge {
  display: inline-block;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  vertical-align: middle;
  text-transform: uppercase;
}
.audit-role-badge--moderator {
  background: rgba(var(--accent-turquoise-rgb), 0.16);
  color: var(--accent-turquoise);
}
.audit-role-badge--user {
  /* M12 PR6 Dávka E — stale Frost copper rgba(184,115,51) → token form. */
  background: rgba(var(--accent-copper-rgb), 0.20);
  color: var(--accent-copper);
}

@media (max-width: 540px) {
  .audit-filters {
    gap: 0.4rem;
  }
  .audit-filter-pill {
    padding: 0.36rem 0.7rem;
    font-size: 0.84rem;
  }
}

/* ──────────────────────────────────────────────────────────────────
   M11B Moderators tab — granular permissions (ADR-147 + ADR-149).
   Visual polish lives in M12C; these styles are functional baseline.
   ────────────────────────────────────────────────────────────────── */

.admin-moderators {
  padding: 1.25rem 1.5rem;
  /* M11B Stage N polish 2026-05-22 — mesh gradient parity per Petřin
     "dáme naopak všude ten mesh gradient jako je v limits". */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-copper) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.admin-moderators__header {
  margin-bottom: 1.1rem;
}
.admin-moderators__header h2 {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-dark);
}
.admin-moderators__subtitle {
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.admin-moderators__username {
  font-weight: 600;
}
.admin-moderators__email {
  font-size: 0.82rem;
  color: var(--text-muted);
}
.admin-moderators__chip {
  display: inline-block;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.admin-moderators__chip--admin {
  /* M12 PR6 Dávka E — stale Frost copper rgba(184,115,51) → token form. */
  background: rgba(var(--accent-copper-rgb), 0.18);
  color: var(--accent-copper);
}
.admin-moderators__chip--mod {
  background: rgba(var(--accent-turquoise-rgb), 0.16);
  color: var(--accent-turquoise);
}
.admin-moderators__perm-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.admin-moderators__perm-chip {
  padding: 0.12rem 0.5rem;
  border-radius: 4px;
  /* M12 PR6 Dávka E — stale Frost violet rgba(83,50,124) → token (matches the
     lighter Noir iris the chip text already uses). Same sweep as suggestion hover. */
  background: rgba(var(--accent-violet-rgb), 0.12);
  color: var(--accent-violet-soft);
  font-size: 0.78rem;
  font-weight: 500;
  cursor: help;
}
.admin-moderators__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
}
.admin-moderators__btn {
  font-size: 0.82rem;
  padding: 0.32rem 0.7rem;
}
.admin-moderators__muted {
  color: var(--text-muted);
  font-size: 0.82rem;
}

/* Add modal — search suggestions + selected echo.
   M11B Moderators UX pivot 2026-05-22 evening (Petřin pokyn) — dropdown
   floats as an absolute overlay above the PERMISSIONS fieldset instead
   of sitting in normal flow. Previous inline layout caused the second
   suggestion row to be visually clipped/overlapped by the fieldset
   border, blocking clicks. The parent .frost-modal__label needs
   `position: relative` to anchor the overlay (added below). */
.admin-moderators__search-field {
  /* Anchor for the absolute suggestions overlay. Without this the
     dropdown anchored to the overflow:hidden .frost-modal dialog and was
     clipped out of view — the "Add crew member search finds nobody" bug.
     Mirrors `.admin-users__search-label` in the Users tab. */
  position: relative;
}
.admin-moderators__suggestions {
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 100;
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  background: var(--cream-base);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
/* The <label> wrapping the username input + suggestions <ul> needs to
   anchor the absolutely-positioned overlay. Scoped to the Add dialog
   so other frost-modal labels don't get unintended positioning. */
#admin-moderators-add-dialog .frost-modal__label:has(#admin-moderators-add-search) {
  position: relative;
}
/* Avatar inside each suggestion row — fixed 24×24 size so flex layout
   stays predictable; circular crop for the picked-icon SVGs. */
.admin-moderators__suggestion-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  flex: 0 0 24px;
  object-fit: contain;
}
/* Crew list (Moderators table) identity cell — picked avatar + username
   on one row. ADR-192 unified avatar treatment; glow added in the shared
   avatar-unification rule above. */
.admin-moderators__identity {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.admin-moderators__row-avatar {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: 50%;
  object-fit: contain;
}
.admin-moderators__suggestion {
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border-bottom: 1px solid var(--border-on-light);
}
.admin-moderators__suggestion:last-child {
  border-bottom: none;
}
.admin-moderators__suggestion:hover,
.admin-moderators__suggestion:focus {
  /* M12 PR6 Dávka E — was stale Frost violet rgba(83,50,124) (near-invisible
     on the Noir dark popover); switch to the teal hover the sibling filter
     dropdown options use (.audit-time-option:hover). */
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  outline: none;
}
.admin-moderators__suggestion--empty {
  cursor: default;
  color: var(--text-muted);
  font-style: italic;
}
.admin-moderators__suggestion small {
  color: var(--text-muted);
  font-size: 0.82rem;
}
/* The suggestions <ul> now lives INSIDE the uppercase-caption
   .frost-modal__label (= .admin-moderators__search-field). Neutralise the
   inherited caption styling (text-transform:uppercase + 0.18em tracking +
   500 weight + --fs-xs) so usernames render verbatim — "testBot152", not
   "T E S T B O T 1 5 2". Scoped to the modal; the Users tab dropdown lives
   in a plain label and is unaffected. */
.admin-moderators__search-field .admin-moderators__suggestions {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  font-size: 0.92rem;
}
.admin-moderators__selected {
  margin: 0.45rem 0 0;
  font-size: 0.85rem;
  color: var(--accent-turquoise);
  font-weight: 600;
}

/* Permission checkbox grid (Add + Edit modals share this). */
.admin-moderators__perms {
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  padding: 0.7rem 0.9rem;
  margin: 0.75rem 0;
  background: rgba(var(--cream-surface-rgb), 0.6);
}
.admin-moderators__perms[disabled] {
  opacity: 0.5;
  pointer-events: none;
}
.admin-moderators__perms legend {
  /* M12 PR6 cross-dialog sweep — align tracking/weight to the canonical
     field label (was 0.85rem/600/0.06em); already muted+uppercase. */
  padding: 0 0.4rem;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.18em;
}
.admin-moderators__perm-row {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.4rem 0.2rem;
  cursor: pointer;
  /* M12 PR6 Dávka E — was warm-gray rgba(155,145,130) hairline (wrong
     direction on the dark surface); use the canonical light hairline. */
  border-bottom: 1px solid var(--border-on-light);
}
.admin-moderators__perm-row:last-child {
  border-bottom: none;
}
.admin-moderators__perm-row input[type="checkbox"] {
  margin-top: 0.2rem;
  flex-shrink: 0;
}
.admin-moderators__perm-label {
  display: block;
  font-size: 0.88rem;
  line-height: 1.35;
}
.admin-moderators__perm-label strong {
  font-weight: 600;
}
.admin-moderators__perm-label small {
  color: var(--text-muted);
  font-size: 0.8rem;
}

/* ============================================================
 * M11B Stage N polish (2026-05-22, ADR-154) — admin pane card
 * baseline + Users tab section card + Frost-styled search input.
 *
 * Per Petřin pokyn "celá ta záložka je hezky udělaná, chci at to
 * vypadá stejně" + "tahle záložka je taky velmi hezká" + "těm
 * záložkám chybí nadpisy. Každá ta záložka ma mit nadpis":
 *
 *   * `.admin-pane--card` opt-in class — applies the Moderators-
 *     style cream card surface (padding + bg + border + shadow)
 *     to panes that don't have their own internal section card
 *     (Reports / Audit / Tours / Concerts / Online / Sigplaces).
 *   * `.admin-pane__header` / `.admin-pane__title` / `.admin-
 *     pane__subtitle` — Cormorant display title + muted subtitle
 *     in the Frost typography baseline; clone of the existing
 *     .admin-moderators__header rhythm.
 *   * `.admin-users` — own internal section card mirroring
 *     .admin-moderators 1:1 per Petřin "chci at Users vypadá
 *     stejně" 2026-05-22.
 *   * `.admin-users__search-input` — Frost form input styling
 *     (cream-sunken bg + 10px radius + turquoise focus). Without
 *     it the input fell back to UA defaults (Petřin screenshot).
 *   * `.admin-users__search-label` — position-relative anchor for
 *     the toolbar-inline autocomplete dropdown (mirrors the
 *     .frost-modal__label trick used by Moderators Add modal).
 * ============================================================ */
.admin-pane--card {
  padding: 1.25rem 1.5rem;
  /* M11B Stage N (ADR-154) — atmospheric mesh gradient per Petřin
     pokyn 2026-05-22 "dáme naopak všude ten mesh gradient jako je v
     limits". Cloned 1:1 from .admin-limits (copper top-right + teal
     bottom-left wash on cream-surface). */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-copper) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.admin-pane__header {
  margin-bottom: 1.1rem;
}
.admin-pane__title {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-dark);
}
.admin-pane__subtitle {
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.5;
}

/* ============================================================
 * M15.X-ADMIN-HEADER (ADR-263) — sticky pane band.
 *
 * Every admin tab gets one strip pinned under the Backstage page
 * header: tab title + labelled count on the left, the tab's
 * actions (Save / + Add / Refresh) on the right. The strip is
 * position:sticky inside the pane (or the pane's section card),
 * so the actions never scroll away — Petřin pokyn 2026-06-05
 * ("uživatel je má jednou dole, jednou nahoře").
 *
 * --admin-band-top is measured by adminUI.js (desktop = page
 * header height; mobile = app bar + tab strip) so the band
 * tucks exactly under the sticky chrome at any viewport.
 * ============================================================ */
.admin-pane__band {
  position: sticky;
  top: var(--admin-band-top, 76px);
  z-index: 6; /* above table content, below .admin-header (10) */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  /* Fuse to the parent card's top + side edges (.admin-pane--card and
     the section cards all use 1.25rem 1.5rem-ish padding). */
  margin: -1.25rem -1.5rem 1.1rem;
  padding: 0.8rem 1.5rem;
  background: rgba(var(--cream-surface-rgb), 0.92);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-on-light);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
/* Bare variant — pane has no card wrapper (Limits: band floats above the
   section cards as its own strip). */
.admin-pane__band--bare {
  margin: 0 0 1.1rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
/* Section cards have slightly different padding than .admin-pane--card —
   match the fuse per host so the band sits flush. */
.admin-moderators > .admin-pane__band,
.admin-users > .admin-pane__band {
  margin: -1.25rem -1.5rem 1.1rem;
}
.admin-test-data > .admin-pane__band {
  margin: -1.6rem -1.4rem 1.1rem;
  padding: 0.8rem 1.4rem;
}
.admin-pane__band-main {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  min-width: 0;
}
.admin-pane__band .admin-pane__title {
  margin: 0;
  white-space: nowrap;
}
.admin-pane__band-count {
  /* Primary text + semibold — the queue-size tally carries real signal
     (Petřin pokyn 2026-05-28 "čísla v adminu moc slabá"; design review
     2026-06-05 flagged the desktop/mobile inconsistency). */
  font-size: 0.85rem;
  color: var(--text-dark);
  font-weight: 600;
  white-space: nowrap;
}
.admin-pane__band-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-left: auto;
}

/* Chart YouTube editor (M12.X-CHART-REDESIGN, ADR-198) — one row per track
   (title · URL field) + a single bulk Save in the toolbar (Petřin pokyn
   2026-05-31). Reuses the canonical .field input + .btn-primary (teal). */
.admin-chart-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.75rem;
}
.admin-chart-state {
  margin: 1rem 0;
  color: var(--text-muted);
  font-size: var(--fs-md);
}
.admin-chart-row {
  display: grid;
  grid-template-columns: minmax(160px, 1fr) minmax(0, 2.4fr);
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.25rem;
  border-bottom: 1px solid var(--border-on-light);
}
.admin-chart-row__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-base);
  color: var(--text-dark);
}
@media (max-width: 600px) {
  .admin-chart-row {
    grid-template-columns: 1fr;
  }
}

/* ============================================================
 * M19.X-ADMIN-EMAIL (ADR-307) — operational email tool.
 * Reuses the canonical .field / .field__label form styling; only the
 * recipient radios, the markdown toolbar and the preview box are bespoke.
 * ============================================================ */
.admin-email {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  max-width: 640px;
  margin-top: 0.75rem;
}
.admin-email__recipients {
  gap: 0.5rem;
}
.admin-email__radio {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--fs-base);
  color: var(--text-dark);
  cursor: pointer;
}
.admin-email__radio input {
  accent-color: var(--accent-turquoise);
}
.admin-email__self {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-style: italic;
}
.admin-email__self:empty {
  display: none;
}
.admin-email__body-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.admin-email__md-toolbar {
  display: flex;
  gap: 0.35rem;
}
.admin-email__md-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 30px;
  padding: 0 0.5rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-sm);
  background: var(--cream-sunken);
  color: var(--text-dark);
  font-size: var(--fs-md);
  line-height: 1;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease;
}
.admin-email__md-btn:hover {
  border-color: var(--accent-turquoise);
}
.admin-email__md-btn svg {
  width: 16px;
  height: 16px;
  display: block;
}
.admin-email__textarea {
  min-height: 180px;
  resize: vertical;
  line-height: 1.55;
}
.admin-email__hint {
  margin: 0.3rem 0 0;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.admin-email__preview {
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: var(--cream-sunken);
  padding: 0.85rem 1rem;
  min-height: 60px;
  color: var(--text-dark);
  font-size: var(--fs-base);
  line-height: 1.55;
}
.admin-email__preview p {
  margin: 0 0 0.6rem;
}
.admin-email__preview p:last-child {
  margin-bottom: 0;
}
.admin-email__preview a {
  color: var(--accent-turquoise);
}
.admin-email__preview-empty {
  color: var(--text-muted);
  font-style: italic;
}
.admin-email__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.85rem;
}
/* Send result/progress status — mint + bold so the "Sent to N" confirmation
   is noticeable (Petřin pokyn 2026-06-16: tlumená barva se snadno přehlédla).
   Uses the --live mint family (same as the app's live dots). Errors render
   separately in the red #admin-email-error, so mint here is always positive. */
#admin-email-status {
  color: var(--live);
  font-weight: 600;
}

.admin-users {
  padding: 1.25rem 1.5rem;
  /* M11B Stage N polish 2026-05-22 — mesh gradient parity. */
  background:
    radial-gradient(at 100% 0%, var(--frost-wash-copper) 0%, transparent 55%),
    radial-gradient(at 0% 100%, var(--frost-wash-teal) 0%, transparent 50%),
    var(--cream-surface);
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.admin-users__header {
  margin-bottom: 1.1rem;
}
.admin-users__header h2 {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.55rem;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: var(--text-dark);
}
.admin-users__subtitle {
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.admin-users__username {
  font-weight: 600;
}
/* Reason column — truncate long reasons; full text shown via row
   title attribute on hover (same affordance as admin Tours desc). */
.admin-users__reason {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Search label = positioning anchor for the absolute suggestions
   list. Mirrors the .frost-modal__label position-relative trick
   the Moderators Add modal uses to anchor its dropdown. */
.admin-users__search-label {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 0 1 280px;
  min-width: 220px;
}
.admin-users__search-input {
  background: var(--cream-sunken);
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  padding: 0.55rem 0.85rem;
  color: var(--text-dark);
  font: inherit;
  font-size: 1rem;
  width: 100%;
  transition: border-color 200ms ease, box-shadow 200ms ease, background 200ms ease;
}
.admin-users__search-input:focus {
  outline: none;
  background: var(--cream-surface);
  border-color: var(--accent-turquoise);
  box-shadow: 0 0 0 4px rgba(var(--accent-turquoise-rgb), 0.18);
}
/* Override the Moderators-default left:0 / right:0 so the dropdown
   matches the label width (= search input width), not the entire
   toolbar row. */
.admin-users__suggestions {
  right: auto;
  width: 100%;
}

/* ------------------------------------------------------------
 * Add ban + Edit ban dialogs — duration chip-pill picker.
 *
 * Per Petřin pokyn 2026-05-22 "radiobuttony v tomto okně mají
 * hrozný design" — replaces UA-default radio circles with the
 * Frost chip-pill pattern used by .deletion-request__chip.
 * Native radio is visually-hidden; the label is the affordance.
 * ------------------------------------------------------------ */
.admin-users__duration {
  border: 0;
  padding: 0;
  margin: 0 0 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.admin-users__duration legend {
  /* M12 PR6 cross-dialog sweep — was white-bold sentence-case; align to the
     canonical field label (muted/uppercase/tracked, HARD RULE 9) so it
     matches the REASON label below it and the ban dialog's DURATION. */
  padding: 0;
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.admin-users__duration-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.admin-users__duration-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-pill);
  background: var(--cream-surface);
  color: var(--text-dark);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  min-height: 34px;
  user-select: none;
  transition: background 160ms ease, border-color 160ms ease,
              color 160ms ease, box-shadow 160ms ease;
}
.admin-users__duration-chip input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  overflow: hidden;
  white-space: nowrap;
}
.admin-users__duration-chip:hover {
  border-color: var(--accent-turquoise);
  color: var(--accent-turquoise);
  background: var(--accent-turquoise-tint);
}
.admin-users__duration-chip:has(input[type="radio"]:checked) {
  border-color: var(--accent-turquoise);
  background: var(--accent-turquoise-tint);
  color: var(--accent-turquoise);
  box-shadow: inset 0 0 0 1px var(--accent-turquoise);
}
.admin-users__duration-chip:has(input[type="radio"]:focus-visible) {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* ------------------------------------------------------------
 * Pin type chip — admin Reports table + detail dialog.
 *
 * Per Petřin pokyn 2026-05-22 "oddel to vizualne, ruzne chipy"
 * narrowed 2026-05-22 to just two outcomes ("child je zbytený,
 * deleted taky"):
 *   Anchor    — copper (= matches the parent-kotva marker accent)
 *   Pin       — turquoise (= everything else, including child pins
 *                inside an anchor)
 * ------------------------------------------------------------ */
.pin-type-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.18rem 0.6rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid transparent;
  white-space: nowrap;
}
/* M12.X — gathering + echo share the STANDARDIZED iris/violet entity tone
   (ui-standards §registry — moment/gathering = --accent-violet, ADR-140 chip
   fill + fanMomentSvg marker fill). The earlier copper (anchor) / turquoise
   (standalone) split read as wrong colours for the entity — copper is POI/
   system, turquoise is Concert/Save. The chip TEXT ("Gathering (N echoes)" vs
   "Echo") carries the anchor-vs-standalone distinction; the colour does not.
   Used in the admin Reports table + report-detail dialog + mobile card. */
.pin-type-chip--anchor,
.pin-type-chip--standalone {
  background: rgba(var(--accent-violet-rgb), 0.18);
  /* TEXT = --accent-violet-soft (#cba5fb), NOT base --accent-violet (#ad77f2):
     the base iris is the FILL/marker tone and reads DIM as a foreground on the
     dark card; -soft is the canonical "label/icon on noir" variant (same one
     the active ECHOES tab uses). Petřin pokyn 2026-05-29 — match the bright
     tab violet, no dim remnant. */
  color: var(--accent-violet-soft);
  border-color: rgba(var(--accent-violet-rgb), 0.3);
}

/* ==========================================================================
   Frisson Noir — Icon utility classes (M12 PR1, from frisson-handoff/icons/icons.css)
   Pair with /assets/icons-sprite.svg. Drop-in for new code; existing inline-SVG
   icons in JS modules migrate incrementally in PR3.
     .icon       = base 16×16, currentColor   (--sm 14, --lg 20, --xl 24)
     .icon-btn   = 32×32 square button wrapper (44 on touch per P-030/P-036)
     .icon-btn--danger  = destructive variant (magenta hover via --accent-mine)
   ========================================================================== */

.icon {
  /* Default 16px — matches Frost-era inline SVG width="16" height="16". */
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: middle;
  fill: currentColor;
  /* M12.X-PROFILE-FIDELITY — removed `color: inherit` here. It was redundant
     (color inherits by default) but, sharing 0,1,0 specificity with tone
     classes like .auth-user-menu__icon / .spider-menu__item-icon defined
     EARLIER in this file, it won the cascade by source order and forced
     every sprite icon to inherit its parent's color — killing all tone
     coding (menu + landmark icons rendered grey instead of teal/copper). */
}

.icon--sm { width: 14px; height: 14px; }
.icon--lg { width: 20px; height: 20px; }
.icon--xl { width: 24px; height: 24px; }

/* .icon-btn — square button wrapper for icon-only actions
   (trash, edit, report, cancel in popups/dialogs). */
.icon-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-muted);
  display: inline-grid;
  place-items: center;
  cursor: pointer;
  transition: background 160ms ease,
              border-color 160ms ease,
              color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .icon-btn:hover {
    background: rgba(238, 240, 250, 0.06);
    border-color: var(--border-on-light);
    color: var(--text-dark);
  }
}

.icon-btn:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
  color: var(--text-dark);
}

/* Destructive variant — trash, remove, ban, "X" cancel-with-intent.
   Inherits .icon-btn base; hover/focus promote to magenta tint. */
@media (hover: hover) and (pointer: fine) {
  .icon-btn--danger:hover {
    background: var(--accent-mine-wash);
    border-color: var(--accent-mine);
    color: var(--accent-mine);
  }
}

.icon-btn--danger:focus-visible {
  background: var(--accent-mine-wash);
  border-color: var(--accent-mine);
  color: var(--accent-mine);
  outline: 2px solid var(--accent-mine);
  outline-offset: 2px;
}

/* Brand-tone icon button (rare; primary-action icon-only contexts
   like map controls or floating CTAs). */
@media (hover: hover) and (pointer: fine) {
  .icon-btn--primary:hover {
    background: var(--accent-turquoise-tint);
    border-color: var(--accent-turquoise);
    color: var(--accent-turquoise);
  }
}

.icon-btn--primary:focus-visible {
  background: var(--accent-turquoise-tint);
  border-color: var(--accent-turquoise);
  color: var(--accent-turquoise);
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* Tap target enforcement on mobile (per P-030, P-036): standalone
   icon buttons must be ≥ 44×44 px on touch devices. */
@media (pointer: coarse) {
  .icon-btn {
    width: 44px;
    height: 44px;
  }
}

/* ==========================================================================
   M12.X-MAP-ACTIONS round 2 — desktop action rail under #map-search
   (Petřin pokyn 2026-05-28). Three horizontal slots since M13.X-SONGS-TAB
   (ADR-237 moved Songs to a panel tab): LOCATE · JOIN · PLACES. Each
   slot = pebble (44×44 round) + pill caption below. Reuses the existing
   pebble glass treatment from .spider-menu; only the WRAPPER and the
   caption pills are new.
   - Position: absolute under #map-search (top-left corner of the map).
   - Phones neutralise the rail layout; fixed M14-rail positions (in
     mobile.css) take over there.
   - Tone tokens: PLACES=gold (--accent-copper), JOIN=magenta
     (--accent-mine), LOCATE=mist (--text-muted).
   - JOIN is a design-only placeholder; click is a no-op. Functional
     follow-up tracked in docs/todo.md (M12 follow-ups).
   ========================================================================== */
.map-actions {
  position: absolute;
  /* M12.X-MAP-ACTIONS round 2 iter 5 (Petřin pokyn 2026-05-28):
     pebbles 44 × 44 preserved + rail sits on the SAME top line as the
     search pill and the hero strip ("uprostřed mezi search pole a
     hero", per Petřin earlier direction). Gap between search-end
     (334 px) and hero-left (497 px) at 1280 viewport is ~163 px;
     pebbles 4 × 44 = 176 px with no gap, so the rail centres on the
     midpoint and bleeds ~6 px into each neighbour edge — accepted
     cosmetic tradeoff (Petra confirmed before this iter). Hero
     measured at 286 px wide → left edge = `50% − 143px`. */
  top: max(0.9rem, env(safe-area-inset-top));
  /* [BUG] 2026-06-07 (Petřin report „rozhodily se pebbles") — the original
     constant assumed a 286px hero (half = 143px), but the ADR-242 tagline
     widened the hero to ~398px, so the stale midpoint landed the rail ON
     the hero at every width up to ~1536px. 199px = half of the current
     hero. This calc is only the FIRST-PAINT fallback: actionRail.js
     measures the real hero/search rects after boot and overrides `left`
     inline (or flips to .map-actions--stacked when the row can't fit),
     so future hero width changes (BETA badge removal, copy edits, JOIN
     reveal) can't re-break the layout. */
  left: calc((max(0.75rem, env(safe-area-inset-left)) + 320px + 50% - 199px) / 2);
  transform: translateX(-50%);
  z-index: 1000;
  display: flex;
  flex-direction: row;
  /* Petřin pokyn 2026-05-28: "dej pebbly od sebe dál, ať se netlučou".
     16 px gap between the four slots so each pebble + its caption has
     clear air around it. */
  gap: var(--space-4);
  align-items: flex-start;
  pointer-events: none; /* slots opt back in below */
}

/* M13.X-CHROME-REFLOW (Petřin narrow-window bug 2026-06-03) — desktop mid
   range. The wide-width rail uses a hand-computed midpoint between the search
   pill and the centred hero (the `left: calc(... 50% - 143px ...)` above),
   tuned for ~1280px+. Below that the hero slides left and the rail's midpoint
   lands ON the hero title — exactly the overlap Petra hit. There isn't room for
   the captioned rail BESIDE a centred hero under ~1280px, so in this band we
   re-anchor the rail under the search pill (top-left), drop the captions to go
   icon-only (like the phone rail), and tighten the gap so the four pebbles
   clear the hero's left edge. ≥1280px keeps the approved centred placement;
   ≤767px is the mobile rail. Mirrors the existing "drop below" idiom that the
   Test Mode toggle already uses at ≤1439px. */
/* [BUG] 2026-06-07 — the fixed 768–1279px band above proved fragile twice
   (hero width changed with the ADR-242 tagline and the rail can widen at
   runtime when JOIN reveals), so the stacked mode is now driven by a CLASS
   that actionRail.js toggles from live hero/search measurements instead of
   a hardcoded viewport range. Same approved look as the former media band:
   icon-only rail under the search pill. ≤767px stays the mobile capsule
   (mobile.css); the class is never applied there. */
@media (min-width: 768px) {
  .map-actions.map-actions--stacked {
    /* Under the 320px search pill (top-left), not the fragile centred midpoint. */
    top: calc(max(0.9rem, env(safe-area-inset-top)) + 3.6rem);
    left: max(0.75rem, env(safe-area-inset-left));
    transform: none;
    gap: var(--space-2); /* 8px — four 44px pebbles fit clear of the hero */
  }
  /* Icon-only in this band: the caption pills would push the rail back into
     the hero. The pebbles keep their aria-labels, so nothing is lost to AT. */
  .map-actions.map-actions--stacked .map-actions__label {
    display: none;
  }
}

.map-actions__slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  pointer-events: auto;
  /* M13.X-LOCATE-JOIN (Petřin pokyn 2026-06-01 "pilulky musí mít stejný padding
     od sebe") — each slot HUGS its caption pill (width: max-content), so the
     uniform --space-4 row gap lands BETWEEN the pills as an EQUAL gap no matter
     how long the word is. Every caption pill (incl. the 4-char "JOIN") is wider
     than the 44 px pebble once its padding is counted, so the PILL drives the
     slot width and the pebble just centres over it — pebble spacing then follows
     automatically (Petřin "podle toho se odsadí i pebbles"). */
  width: max-content;
}

/* M13.X-PEBBLES (ADR-247) — conditional slots. `display: flex` above would
 * beat the UA [hidden] rule, so make hidden explicit: JOIN ships [hidden]
 * and actionRail.js reveals it once the loaded data contains an open
 * gathering; the legend slot is [hidden] on desktop (the toggle lives
 * bottom-left there) and revealed on phones. */
.map-actions__slot[hidden] {
  display: none !important;
}

/* One-way reveal (ADR-247 R2 "appear yes, disappear no") — a soft fade-in
 * when JOIN joins the rail mid-session. */
.map-actions__slot--reveal {
  animation: map-actions-slot-reveal 400ms ease-out;
}
@keyframes map-actions-slot-reveal {
  from { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .map-actions__slot--reveal {
    animation: none;
  }
}

/* Landmark slot needs no width override — it hugs its "LANDMARKS" caption via
   the base `width: max-content` like every other slot, so the equal pill gap
   holds for the long word too (M13.X-LOCATE-JOIN, Petřin pokyn 2026-06-01). */

/* Caption pill — dark glass pill chrome (matched to .badge-beta family)
   with TIGHTENED typography. Petřin pokyn 2026-05-28 iter 6: "JEN
   menší text uvnitř, pill background ponechat" — earlier "strip the
   pill" iteration removed the readable surface entirely and the
   captions disappeared on the light map. Pill stays; only the text
   inside is light + airy: regular weight, roomy padding, sized to its
   own content so each caption reads as a separate breathing chip. */
.map-actions__label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* M13.X-LOCATE-JOIN (Petřin pokyn 2026-06-01) — pill is at least as wide as
     the 44 px pebble, so the PILL always drives its slot's max-content width and
     the equal --space-4 gap lands evenly between captions (a short word like
     JOIN can't float in a wider slot and break the rhythm). */
  min-width: 44px;
  /* Roomy padding so each caption breathes — text never touches the pill
     edge. Petřin pokyn 2026-05-28: "vzdušnější pills, ne namačkaně".
     Pills are sized to their content (not stretched to the slot) so each
     action sits as its own separated chip with clear air around it. */
  padding: var(--space-1) var(--space-2);  /* 4 px × 8 px */
  border-radius: var(--radius-pill);
  background: var(--glass-strong);
  border: 1px solid var(--border-on-light);
  color: var(--text-light);
  font-family: var(--font-body);
  font-size: var(--fs-2xs);            /* 11 px — was --fs-xs (12 px) */
  font-weight: 400;                    /* light + subtle — Petřin "jemnější, ne tlusté" */
  letter-spacing: 0.04em;              /* modest tracking for the caps */
  text-transform: uppercase;
  line-height: 1.1;
  white-space: nowrap;
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-overlay-bar);
  user-select: none;
}

/* Override the legacy desktop fixed positioning for .spider-menu when it
   lives inside .map-actions. The `!important` matches the equally-
   !important legacy rules. Guard at ≥768 px so the override does NOT win
   on mobile, where the mobile.css rules pin the spider onto the M14
   vertical rail with `position: fixed`. Without the guard our higher-
   specificity selector beat the mobile rules at every viewport and the
   mobile tile disappeared from the rail — Petřin smoke 2026-05-28.
   M13.X-CHROME-REFLOW — bumped 481 → 768 in lock-step with the unified
   mobile breakpoint. (Songs pebble override removed by M13.X-SONGS-TAB
   ADR-237 — the pebble itself is gone.) */
@media (min-width: 768px) {
  /* Spider must be RELATIVE (not static) so the absolutely-positioned submenu
     + its speech-bubble tail anchor to THIS pebble. When the spider was static,
     the nearest positioned ancestor was `.map-actions` (the whole rail), so the
     menu's `left: 50%` + the tail's `left: 50%` resolved against the rail width
     → the dropdown centred on the rail and the tail pointed up between
     SONGS/JOIN instead of at LANDMARKS (Petřin screenshot 2026-06-02). relative
     + no offsets = identical box, just a new containing block for the menu. */
  .map-actions__slot--places .spider-menu {
    position: relative !important;
    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
  }

  /* Submenu MUST pop DOWN whenever the spider lives in the top map-actions
     rail. The legacy `@media (max-width: 767px)` rule (~line 20885) flips the
     list to pop UP (`bottom: 100%`) — written when the spider sat bottom-left,
     before M12.X-MAP-ACTIONS moved it to the TOP rail. In the 481–767 px band
     that pop-up rule still fired, so the menu opened ABOVE the rail and ran
     off the top of the viewport (measured top: -156 px). At ≤480 px mobile.css
     already re-flips to pop-down; at ≥768 px the default is pop-down — only
     this tablet band was left broken. Force pop-down + tail-up here so all
     rail viewports behave the same. */
  .map-actions__slot--places .spider-menu .spider-menu__list {
    top: calc(100% + 0.6rem);
    bottom: auto;
    left: 50%;
    transform: translateX(-50%);
  }
  .map-actions__slot--places .spider-menu .spider-menu__list::before {
    top: -8px;
    bottom: auto;
    left: 50%;
    transform: translateX(-50%);
    border-top: none;
    border-bottom: 8px solid rgba(var(--accent-copper-rgb), 0.22);
  }
  .map-actions__slot--places .spider-menu .spider-menu__list::after {
    top: -7px;
    bottom: auto;
    left: 50%;
    transform: translateX(-50%);
    border-top: none;
    border-bottom: 7px solid rgba(var(--cream-surface-rgb), 0.98);
  }
}

/* JOIN + LOCATE pebbles — same 44 × 44 cream-surface + accent-glow
   recipe as the songs / spider pebbles, only the tone token differs.
   Keeps the rail visually homogeneous (4 siblings, 4 tones, 1 shape,
   1 size — Petřin pokyn 2026-05-28: tlačítka zůstávají velká, jen
   popisky pod nimi se ztiší). */
.map-actions__btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  cursor: pointer;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  transition: background 200ms ease, transform 200ms ease, box-shadow 200ms ease;
}

/* JOIN — violet tone (Petřin pokyn 2026-06-10): carries the open-gathering
   identity (designer's violet/magenta teardrop marker). HUB took teal. */
.map-actions__btn--join {
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-violet-rgb), 0.24) 0%, transparent 60%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-violet-rgb), 0.60);
  color: var(--accent-violet);
  box-shadow:
    0 0 20px rgba(var(--accent-violet-rgb), 0.50),
    0 4px 14px rgba(0, 0, 0, 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
}
@media (hover: hover) and (pointer: fine) {
  .map-actions__btn--join:hover {
    background:
      radial-gradient(at 100% 0%, rgba(var(--accent-violet-rgb), 0.30) 0%, transparent 60%),
      var(--cream-surface);
    box-shadow:
      0 0 28px rgba(var(--accent-violet-rgb), 0.65),
      0 6px 18px rgba(0, 0, 0, 0.35),
      inset 0 1px 0 rgba(238, 240, 250, 0.12);
    transform: translateY(-1px);
  }
}
.map-actions__btn--join:focus-visible {
  outline: 2px solid var(--accent-violet);
  outline-offset: 2px;
}
/* Desktop JOIN glyph bigger (Petřin pokyn 2026-06-10) — the designer's ringed
   marker keeps its content inside the viewBox, so at the 22px sibling size it
   read smaller than the Places/HUB glyphs. 28px in the 44px pebble matches
   their visual weight. Mobile keeps 24px via the higher-specificity id rule. */
.map-actions__btn--join svg {
  width: 28px;
  height: 28px;
  transform: translateY(2px);   /* marker is top-heavy (rings up top) — nudge down to sit centred (Petřin pokyn 2026-06-10) */
}

/* LOCATE — teal tone (M13.X-LOCATE-JOIN, ADR-214): "recentre on me" reads as
   the app's neutral/primary action, distinct from the magenta JOIN sibling. */
.map-actions__btn--locate {
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.22) 0%, transparent 60%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.55);
  color: var(--accent-turquoise);
  box-shadow:
    0 0 18px rgba(var(--accent-turquoise-rgb), 0.42),
    0 4px 14px rgba(0, 0, 0, 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
}
@media (hover: hover) and (pointer: fine) {
  .map-actions__btn--locate:hover {
    background:
      radial-gradient(at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.30) 0%, transparent 60%),
      var(--cream-surface);
    box-shadow:
      0 0 26px rgba(var(--accent-turquoise-rgb), 0.60),
      0 6px 18px rgba(0, 0, 0, 0.35),
      inset 0 1px 0 rgba(238, 240, 250, 0.12);
    transform: translateY(-1px);
  }
}
.map-actions__btn--locate:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* COMMUNITY (HUB) pebble — teal tone (Petřin pokyn 2026-06-10; was violet
   per ADR-247 R4). Same glass-pebble recipe as locate/join. */
.map-actions__btn--community {
  background:
    radial-gradient(at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.22) 0%, transparent 60%),
    var(--cream-surface);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.55);
  color: var(--accent-turquoise);
  box-shadow:
    0 0 18px rgba(var(--accent-turquoise-rgb), 0.42),
    0 4px 14px rgba(0, 0, 0, 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
}
@media (hover: hover) and (pointer: fine) {
  .map-actions__btn--community:hover {
    background:
      radial-gradient(at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.30) 0%, transparent 60%),
      var(--cream-surface);
    box-shadow:
      0 0 26px rgba(var(--accent-turquoise-rgb), 0.60),
      0 6px 18px rgba(0, 0, 0, 0.35),
      inset 0 1px 0 rgba(238, 240, 250, 0.12);
    transform: translateY(-1px);
  }
}
.map-actions__btn--community:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}

/* ==========================================================================
   COMMUNITY HUB (M13.X-HUB-REDESIGN, ADR-252 — designer handoff Direction A)
   Source: docs/handoff/community_hub/ (HANDOFF.md + community-hub.css).
   Handoff tokens mapped to project Noir tokens (precedent ADR-236):
   midnight=--cream-surface · navy=--cream-sunken · lifted=--cream-lifted ·
   ink=--cream-base · moon=--text-dark · muted=--text-muted ·
   mist=--text-light-muted · teal=--accent-turquoise.
   Four bands: band header (fixed) → canonical card (fixed) → feed (the
   ONLY scroll region) → share-a-link (fixed footer band).
   ========================================================================== */
dialog#community-hub-dialog {
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.15);
  border-radius: var(--radius-lg);
  padding: 1.5rem 1.4rem 0;
  max-width: 95vw;
  width: 520px;
  background:
    radial-gradient(circle at 100% 0%, rgba(var(--accent-turquoise-rgb), 0.08), transparent 55%),
    radial-gradient(circle at 0% 100%, rgba(var(--accent-violet-rgb), 0.06), transparent 55%),
    var(--cream-surface);
  color: var(--text-dark);
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.22),
    0 60px 120px rgba(var(--accent-turquoise-rgb), 0.10),
    0 0 0 1px rgba(var(--accent-turquoise-rgb), 0.04);
}
dialog#community-hub-dialog::backdrop {
  background:
    radial-gradient(circle at center, rgba(var(--accent-turquoise-rgb), 0.18) 0%, transparent 50%),
    radial-gradient(circle at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
/* Canonical content-modal title (ui-standards §2 tier) — the shared
   `.event-create-dialog .event-create__header h2` rule doesn't reach this
   dialog (no .event-create-dialog class), so mirror it here. */
#community-hub-dialog h2 {
  margin: 0 0 0.4rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-style: italic;
  font-size: var(--fs-modal-title);
  letter-spacing: 0.025em;
  line-height: 1.15;
  color: var(--text-dark);
}
/* Column shell — fixed height so the feed (flex:1) is what scrolls, per
   the mockup ("Fixed-height dialog. The feed is its own scroll region"). */
.community-hub__body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: min(58vh, 540px);
}
/* Gate / loading copy — quiet single line. Uses --text-muted (the
   empty-state convention), NOT --text-light-muted (placeholder/tertiary —
   read too dim on noir, Petřin nález 2026-06-04). */
.community-hub__gate,
.community-hub__loading {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-align: center; /* match the centered empty-state copy above */
}
.hub-add .community-hub__gate {
  margin: 0;
}

/* ── Canonical pinned card — r/tarjaturunen (permanent, ADR-098) ────── */
.hub-canon {
  position: relative;
  display: flex;
  align-items: center;
  gap: 13px;
  flex-shrink: 0;
  width: 100%;
  text-align: left;
  margin-top: 14px;
  padding: 14px 14px 14px 15px;
  border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--accent-turquoise) 32%, transparent);
  border-left: 3px solid var(--accent-turquoise);
  background:
    linear-gradient(180deg, rgba(var(--accent-turquoise-rgb), 0.10), rgba(238, 240, 250, 0.02)),
    var(--cream-lifted);
  box-shadow: inset 0 1px 0 rgba(238, 240, 250, 0.05);
  cursor: pointer;
  color: inherit;
  text-decoration: none;
  transition: border-color 160ms ease, box-shadow 160ms ease, transform 120ms ease;
}
.hub-canon:hover {
  transform: translateY(-1px);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent-turquoise) 26%, transparent),
    0 10px 26px rgba(0, 0, 0, 0.35);
}
.hub-canon__glyph {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  color: var(--accent-turquoise-bright);
  background: linear-gradient(180deg, rgba(var(--accent-turquoise-rgb), 0.24), rgba(var(--accent-turquoise-rgb), 0.07));
  border: 1px solid color-mix(in srgb, var(--accent-turquoise) 40%, transparent);
}
/* color:currentColor — override the global `.platform-icon { color: violet }`
   (same pattern as .online-event-detail__platform-icon) so the glyph takes
   the tile's per-platform tint. */
.hub-canon__glyph svg { width: 22px; height: 22px; color: currentColor; }
.hub-canon__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
/* Eyebrow bumped 9px → 10px vs the handoff — the v1 "REDDIT · OFFICIAL"
   line was Petřin readability nález (2026-06-04); teal + a touch more size. */
.hub-canon__eyebrow {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 700 10px/1 var(--font-body);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  margin-bottom: 5px;
  white-space: nowrap;
}
.hub-canon__name {
  display: block;
  font: 700 16px/1.1 var(--font-body);
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hub-canon__desc {
  display: block;
  margin-top: 3px;
  font: 500 11.5px/1.35 var(--font-body);
  color: var(--text-muted); /* ne light-muted - necitelne (Petra 2026-06-04) */
}
.hub-canon__chev {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(238, 240, 250, 0.05);
  border: 1px solid var(--hairline-dark);
  color: var(--accent-turquoise);
}
.hub-canon__chev svg { width: 14px; height: 14px; }

/* OFFICIAL badge */
.hub-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 7px 3px 6px;
  border-radius: 999px;
  font: 700 8.5px/1 var(--font-body);
  letter-spacing: 0.16em;
  color: var(--accent-turquoise-bright);
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  border: 1px solid color-mix(in srgb, var(--accent-turquoise) 45%, transparent);
}

/* ── Feed head — "LATEST LINKS  N ────────" ─────────────────────────── */
.hub-feed-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  margin: 18px 0 9px;
}
.hub-feed-head .fm-section-label,
.hub-add__bar .fm-section-label {
  margin: 0;
  white-space: nowrap;
  /* o stupen vetsi nez kanon --fs-xs - „LATEST LINKS je hrozne male“
     (Petrin pokyn 2026-06-04 vecer); jen v hubu, kanon nemenime. */
  font-size: var(--fs-sm);
  font-weight: 600;
}
.hub-feed-head__count {
  font: 700 11px/1 var(--font-body);
  letter-spacing: 0.04em;
  color: var(--accent-turquoise);
  font-variant-numeric: tabular-nums;
}
.hub-feed-head__rule {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--hairline-dark), transparent);
}

/* ── Feed — the inner (and only) scroll container ───────────────────── */
.hub-feed {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 2px 4px 14px 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  /* edge fade hints there's more to scroll (mask = alpha-only; the rgba
     black is a mask stop, not a paint colour) */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, rgba(0, 0, 0, 1) 12px, rgba(0, 0, 0, 1) calc(100% - 14px), transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, rgba(0, 0, 0, 1) 12px, rgba(0, 0, 0, 1) calc(100% - 14px), transparent 100%);
}
.hub-feed::-webkit-scrollbar { width: 9px; }
.hub-feed::-webkit-scrollbar-track { background: transparent; }
.hub-feed::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--accent-turquoise) 28%, transparent);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.hub-feed::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--accent-turquoise) 45%, transparent);
  background-clip: padding-box;
}

/* ── Link row ───────────────────────────────────────────────────────── */
.hub-link {
  display: flex;
  align-items: center;
  gap: 4px;
  /* ⚠️ NO --platform default here — the row tint comes from the app-wide
     [data-platform] block (ADR-255; same specificity, this rule is LATER in
     the file, so a local default would silently win the cascade and grey
     out every row — caught by the ADR-255 render probe). Unknown keys fall
     to each consumer's var(--platform, fallback). */
}
/* Per-platform --platform values come from the app-wide [data-platform]
   block (ADR-255) — the hub-local copies are gone; tokens carry the same
   hub tones + the platforms the hub didn't define (instagram/x/spotify/…
   no longer fall to grey). */
.hub-link__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 10px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
  background: transparent;
  border: 1px solid transparent;
  transition: background 140ms ease, border-color 140ms ease;
}
.hub-link__main:hover {
  background: rgba(238, 240, 250, 0.045);
  border-color: var(--hairline-dark-lo);
}
.hub-link__ic {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  /* Fallback = muted (rows always carry data-platform, this is belt &
     braces after the ADR-255 default move). */
  color: var(--platform, var(--text-muted));
  background: color-mix(in srgb, var(--platform, var(--text-muted)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--platform, var(--text-muted)) 30%, transparent);
}
.hub-link__ic svg { width: 17px; height: 17px; color: currentColor; }
.hub-link__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.hub-link__title {
  display: block;
  max-width: 100%;
  font: 600 13.5px/1.3 var(--font-body);
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hub-link__meta {
  margin-top: 2px;
  display: flex;
  align-items: center;
  gap: 7px;
  font: 500 11px/1.2 var(--font-body);
  /* zpet na light-muted - plny muted tu byl moc svetly (Petra 2026-06-04
     vecer); identitu vlastnictvi nese magenta You nize. */
  color: var(--text-light-muted);
  overflow: hidden;
  white-space: nowrap;
}
.hub-link__platform {
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--platform, var(--text-muted));
  font-size: 9.5px;
}
.hub-link__meta .hub-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--text-light-muted);
  flex-shrink: 0;
}
.hub-link__meta .hub-meta-part {
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Own rows - magenta You = canonical mine tone (jako srdce/echo). */
.hub-link__meta .hub-meta-part--you {
  color: var(--magentaLift);
  font-weight: 600;
}
.hub-link__chev {
  flex-shrink: 0;
  color: var(--text-muted); /* feed parity (Petra 2026-06-04 - moc svetle) */
  display: grid;
  place-items: center;
}
.hub-link__chev svg { width: 15px; height: 15px; }

/* Row actions — pencil (rename) + trash (delete). Hidden until hover; on
   the viewer's OWN links they idle at low opacity so manageability is
   discoverable (and touch devices see them — no hover there). */
.hub-link__actions {
  flex-shrink: 0;
  display: flex;
  gap: 2px;
  /* Vzdy viditelny - kebab je jediny vstup k akcim radku (feed parity;
     hover-reveal z handoffu byl na kebab moc skryty + moc svetly). */
}
.hub-link__act {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-muted); /* feed parity */
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
.hub-link__act svg { width: 15px; height: 15px; }
.hub-link__kebab:hover,
.hub-link__kebab:focus-visible {
  /* feed-parity neutral hover (.feed-list__row-menu-btn) */
  background: rgba(238, 240, 250, 0.08);
  color: var(--text-dark);
  outline: none;
}

/* Inline rename — the title becomes a field with save (✓) / cancel (✕) */
.hub-link--editing {
  padding: 7px 8px 7px 10px;
  border-radius: var(--radius-sm);
  background: rgba(var(--accent-turquoise-rgb), 0.06);
  border: 1px solid color-mix(in srgb, var(--accent-turquoise) 30%, transparent);
  gap: 8px;
}
.hub-rename {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 11px;
  min-height: 40px;
  border-radius: var(--radius-sm);
  background: var(--cream-base);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.55);
  box-shadow: 0 0 0 3px rgba(var(--accent-turquoise-rgb), 0.13);
}
.hub-rename > svg { width: 14px; height: 14px; color: var(--accent-turquoise); flex-shrink: 0; }
.hub-rename input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: 0;
  color: var(--text-dark);
  font: 600 13.5px/1 var(--font-body);
}
.hub-rename-btn {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  cursor: pointer;
  border: 1px solid transparent;
  background: transparent;
}
.hub-rename-btn svg { width: 15px; height: 15px; }
.hub-rename-btn--save {
  color: var(--text-on-accent);
  background: linear-gradient(180deg, var(--accent-turquoise-bright), var(--accent-turquoise));
  box-shadow: 0 0 12px rgba(var(--accent-turquoise-rgb), 0.3);
}
.hub-rename-btn--cancel {
  color: var(--text-light-muted);
  border-color: var(--hairline-dark);
}

/* ── Empty state (inside the feed band) ─────────────────────────────── */
.hub-empty {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 9px;
  padding: 34px 28px;
}
.hub-empty__glyph {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--accent-turquoise);
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border: 1px solid color-mix(in srgb, var(--accent-turquoise) 26%, transparent);
}
.hub-empty__glyph svg { width: 24px; height: 24px; }
.hub-empty__title {
  font: italic 600 19px/1.1 var(--font-display);
  color: var(--text-dark);
}
.hub-empty__sub {
  font: 500 12.5px/1.5 var(--font-body);
  color: var(--text-muted);
  max-width: 250px;
}

/* ── Share-a-link band — fixed footer; collapsed trigger → 2-field form ── */
.hub-add {
  flex-shrink: 0;
  margin: 0 -1.4rem;
  padding: 14px 1.4rem 1.3rem;
  border-top: 1px solid var(--hairline-dark-lo);
  background: linear-gradient(180deg, transparent, rgba(10, 13, 30, 0.45));
}
.hub-add__trigger {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 46px;
  border-radius: var(--radius-md);
  background: rgba(var(--accent-turquoise-rgb), 0.08);
  border: 1px dashed color-mix(in srgb, var(--accent-turquoise) 50%, transparent);
  color: var(--accent-turquoise-bright);
  font: 700 13px/1 var(--font-body);
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}
.hub-add__trigger:hover {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  border-color: var(--accent-turquoise);
}
.hub-add__trigger svg { width: 16px; height: 16px; }
.hub-add__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 9px;
}
.hub-add__close {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(238, 240, 250, 0.05);
  border: 1px solid var(--hairline-dark);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.hub-add__close:hover { background: rgba(238, 240, 250, 0.12); color: var(--text-dark); }
.hub-add__close svg { width: 14px; height: 14px; }
.hub-add__field {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  min-height: 44px;
  border-radius: var(--radius-md);
  background: var(--cream-sunken);
  border: 1px solid var(--hairline-dark);
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.hub-add__field:focus-within {
  border-color: rgba(var(--accent-turquoise-rgb), 0.6);
  box-shadow: 0 0 0 3px rgba(var(--accent-turquoise-rgb), 0.13);
}
.hub-add__field > svg { width: 16px; height: 16px; color: var(--accent-turquoise); flex-shrink: 0; }
.hub-add__field input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: 0;
  outline: 0;
  color: var(--text-dark);
  font: 400 14px/1 var(--font-body);
}
.hub-add__field--url { margin-bottom: 8px; }
.hub-add__namerow {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.hub-add__namerow .hub-add__field { flex: 1; min-width: 0; }
/* "AUTO" tag — shows while the name still holds the fetched title */
.hub-add__auto {
  flex-shrink: 0;
  font: 700 8.5px/1 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
  padding: 3px 6px;
  border-radius: 999px;
  background: rgba(var(--accent-turquoise-rgb), 0.13);
  border: 1px solid color-mix(in srgb, var(--accent-turquoise) 40%, transparent);
}
/* Add = canonical .btn-primary (teal, HARD RULE 12) — only sized here. */
.hub-add__btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 16px;
  min-height: 44px;
  border-radius: var(--radius-md);
  white-space: nowrap;
}
.hub-add__btn svg { width: 15px; height: 15px; }
.hub-add__warn {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin-top: 8px;
  font: 500 11px/1.4 var(--font-body);
  color: var(--accent-copper-bright);
}
.hub-add__warn svg { width: 13px; height: 13px; flex-shrink: 0; margin-top: 1px; }
.hub-add__hint {
  margin-top: 8px;
  font: 500 11px/1.4 var(--font-body);
  color: var(--text-muted); /* parity s canon desc */
}
.hub-add__hint b { color: var(--accent-turquoise); font-weight: 700; }
.hub-add__error {
  margin-top: 8px;
  font: 500 11.5px/1.4 var(--font-body);
  color: var(--danger-text);
}
/* Fetching spinner (name field) */
.hub-spin {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--accent-turquoise) 30%, transparent);
  border-top-color: var(--accent-turquoise);
  animation: hub-spin 700ms linear infinite;
}
@keyframes hub-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .hub-spin { animation: none; }
  .hub-canon { transition: none; }
}

/* ==========================================================================
   YOUR FRISSON JOURNEY + 20-STAR RATING (M13.X-FRISSON-JOURNEY ADR-217 +
   M13.X-SONG-RATING ADR-218). Lives in the Profile dialog.
   Source of truth: docs/handoff/frisson-journey-handoff/mockups/
   your-frisson-journey*.png + README ADDENDUM.
   Two signals per tile: GOLD star = rating (--accent-copper); MAGENTA check =
   echoed (--accent-mine). Never the heart (that stays the Echo like, magenta).
   ========================================================================== */
.journey {
  margin-top: 1.1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--hairline-dark-lo);
}

/* "Your Frisson Journey" own dialog (M13.X — opened from the Songs pebble menu,
   moved out of Profile). Roomy on desktop so the song tiles don't over-wrap. */
.journey-dialog {
  position: relative;
  width: 600px;
  max-width: 92vw;
  max-height: 88vh;
  overflow-y: auto;
  padding: 1.4rem 1.5rem 1.3rem;
  border: 1px solid var(--border-on-light);
  border-radius: 24px;
  background: linear-gradient(176deg, var(--surface-plum) 0%, var(--cream-surface) 60%, var(--cream-base) 100%);
  color: var(--text-dark);
  box-shadow: var(--shadow-modal), 0 40px 90px rgba(0, 0, 0, 0.55);
}
.journey-dialog::backdrop {
  background: rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
/* In its own dialog the block doesn't need the Profile section divider. */
.journey-dialog .journey {
  margin-top: 0;
  padding-top: 0.2rem;
  border-top: none;
}

/* (Songs pebble dropdown menu removed — M13.X-SONGS-TAB ADR-237; the
   Songs panel tab replaced it.) */
.journey__state {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.journey__state--error { color: var(--danger-text); }

/* === Songs tab pane — M13.X-SONGS-TAB (ADR-237) =========================
   Lightweight hub inside the panel: two entry cards (Chart / Your Frisson
   Journey) that open the EXISTING dialogs untouched, plus a signed-in
   progress strip. Mirrors the composer grouping-card anatomy (tonal round
   glyph · copy · chevron) in the song world's gold tone — no new visual
   language, just the existing card idiom re-toned. */
.songs-pane {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-2);
}
.songs-pane__card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  text-align: left;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--glass-strong);
  border: 1px solid var(--border-on-light);
  border-left: 3px solid rgba(var(--accent-copper-rgb), 0.65);
  color: var(--text-light);
  cursor: pointer;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .songs-pane__card:hover {
    border-color: rgba(var(--accent-copper-rgb), 0.55);
    border-left-color: var(--accent-copper);
    box-shadow: 0 0 14px rgba(var(--accent-copper-rgb), 0.18);
  }
}
.songs-pane__card:focus-visible {
  outline: 2px solid var(--accent-copper);
  outline-offset: 2px;
}
.songs-pane__glyph {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background:
    radial-gradient(circle at 35% 30%,
      rgba(var(--accent-copper-rgb), 0.28),
      rgba(var(--accent-copper-rgb), 0.08) 85%);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.55);
  color: var(--accent-copper-bright);
}
.songs-pane__glyph .icon {
  width: 20px;
  height: 20px;
}
.songs-pane__copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.songs-pane__title {
  font: 600 var(--fs-md) var(--font-body);
  /* Petřin pokyn 2026-06-03 (smoke) — card titles join the rating world's
     gold (same bright-copper TEXT variant the stars stat below uses). */
  color: var(--accent-copper-bright);
}
.songs-pane__sub {
  font-size: var(--fs-sm);
  /* Tertiary, not secondary (Petra 2026-06-04 — --text-muted subs read too
     bright next to the copper titles; the lightened tertiary token is the
     measured middle she converged on). */
  color: var(--text-light-muted);
}
.songs-pane__chevron {
  margin-left: auto;
  color: var(--text-muted);
  font-size: 1.2rem;
  line-height: 1;
}
/* ── Stats card (M13.X-CHART-SPACE v2, ADR-253 — designer handoff
   docs/handoff/songs_stats Direction C). One quiet card under the two entry
   cards, three bands: two stat meters (signed-in) → hairline rule → live
   module (teal eyebrow + gold-ruled pull-quote + one adaptive CTA). Anon
   skips the meters — the live module leads and the sign-in nudge sits
   below it, INSIDE the card (no floating text under it). Designer tokens
   mapped per the ADR-236/252 precedent: midnight → --glass-strong (same
   hue), hairline-lo → --hairline-dark-lo, moon → --text-light, mist →
   --text-light-muted, gold/teal → the copper / turquoise accent families. */
.songs-pane__pulse-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-2);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--glass-strong);
  border: 1px solid var(--hairline-dark-lo);
}
/* Two labelled meters — Stars given on gold, Songs echoed on the Echo
   magenta (Petřin pokyn 2026-06-05 — the mockup's teal drifted off the Echo
   identity; magenta ties it to the Echo world). Same anatomy as the Your
   Stars budget meter (label row + pill bar), one size quieter. */
.songs-pane__meters {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.songs-pane__meter {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.songs-pane__meter-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}
.songs-pane__meter-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-light-muted);
}
.songs-pane__meter-icon {
  width: 12px;
  height: 12px;
}
.songs-pane__meter-val {
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--text-light);
  font-variant-numeric: tabular-nums;
}
.songs-pane__meter-val small {
  font-size: var(--fs-2xs);
  font-weight: 600;
  color: var(--text-light-muted);
  margin-left: 2px;
}
.songs-pane__meter-bar {
  height: 6px;
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.songs-pane__meter-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  transition: width 0.2s ease;
}
.songs-pane__meter--gold .songs-pane__meter-bar {
  background: rgba(var(--accent-copper-rgb), 0.16);
}
.songs-pane__meter--gold .songs-pane__meter-fill {
  background: linear-gradient(90deg, var(--accent-copper), var(--accent-copper-bright));
  box-shadow: 0 0 10px rgba(var(--accent-copper-rgb), 0.4);
}
.songs-pane__meter--mine .songs-pane__meter-bar {
  background: rgba(var(--accent-mine-rgb), 0.14);
}
.songs-pane__meter--mine .songs-pane__meter-fill {
  background: linear-gradient(90deg, var(--accent-mine), var(--accent-mine-bright));
  box-shadow: 0 0 10px rgba(var(--accent-mine-rgb), 0.4);
}
.songs-pane__anon {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-align: center;
}
/* Inline "Sign in" link inside the nudge sentence (Petra 2026-06-04) —
   inherit the line's size + drop .btn-link's vertical padding so the
   link sits in the text flow, not as a button-sized island. The bare
   word space read too tight next to the underline → small margin. */
.songs-pane__anon .btn-link {
  font-size: inherit;
  padding: 0;
  margin-right: 0.3rem;
}
/* ADR-244 benefit line — quiet link under the progress strip for fans with
   no declared country ("Fly your own flag on the Chart — pick your country."),
   click opens Preferences. Same voice as the strip, underline = affordance. */
.songs-pane__country {
  display: block;
  width: 100%;
  background: none;
  border: 0;
  margin: 0;
  padding: 0 var(--space-3) var(--space-2);
  font-family: inherit;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-align: center;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.songs-pane__country:hover {
  color: var(--accent-turquoise);
}
/* ── Live module (M13.X-CHART-SPACE v2, ADR-253 — Direction C) — the
   rotating community insight (ADR-251 catalog), anchored inside the card:
   teal live eyebrow with a pulsing dot, the sentence as a gold-ruled
   pull-quote, one adaptive CTA pill below. A hairline rule separates it
   from the meters above (signed-in only — anon has no meters, the module
   leads the card). */
.songs-pane__live {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: flex-start;
}
[data-songs-pane-meters]:not([hidden]) + .songs-pane__live {
  border-top: 1px solid var(--hairline-dark-lo);
  padding-top: var(--space-4);
}
/* Teal micro-heading with the pulsing live dot — one generic title for
   every insight ("Live from Frisson World", Petra 2026-06-04, supersedes
   the copper eyebrow). Rendered from JS via t() (user-facing text never
   lives in CSS `content`). */
.songs-pane__pulse-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font: var(--fw-bold) var(--fs-2xs) / 1.15 var(--font-body);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent-turquoise);
}
.songs-pane__pulse-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-turquoise);
  box-shadow: 0 0 0 0 rgba(var(--accent-turquoise-rgb), 0.55);
  animation: songs-pane-pulse 2.2s ease-out infinite;
}
@keyframes songs-pane-pulse {
  0% { box-shadow: 0 0 0 0 rgba(var(--accent-turquoise-rgb), 0.55); }
  70% { box-shadow: 0 0 0 7px rgba(var(--accent-turquoise-rgb), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--accent-turquoise-rgb), 0); }
}
@media (prefers-reduced-motion: reduce) {
  .songs-pane__pulse-dot { animation: none; }
}
/* The insight as a Cormorant italic pull-quote on a gold left rule —
   a composed literary moment, NOT a muted caption (hard rule:
   --text-muted never carries standalone content). */
.songs-pane__pulse-text {
  margin: 0;
  padding: var(--space-1) 0 var(--space-1) var(--space-4);
  border-left: 2px solid rgba(var(--accent-copper-rgb), 0.55);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: var(--fs-lg);
  line-height: 1.45;
  color: var(--text-light);
}
/* Song name inside the sentence — the line's ONE visual anchor, carrying
   the canonical song identity (--song-name + --font-song, ADR-238; NOT
   the mockup's Manrope — project canon wins). Inside the Cormorant quote
   the mono sits a notch smaller, upright, to match the serif's optical
   baseline. */
.songs-pane__pulse-songname {
  font-family: var(--font-song);
  font-size: 0.82em;
  font-style: normal;
  color: var(--song-name);
}
/* The adaptive invite CTA — ghost teal pill (teal = the primary-action
   role, HARD RULE 12; ghost treatment per the mockup so it invites without
   shouting next to the gold entry cards). */
.songs-pane__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  background: rgba(var(--accent-turquoise-rgb), 0.1);
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.45);
  color: var(--accent-turquoise-bright);
  font: var(--fw-bold) var(--fs-sm) / 1 var(--font-body);
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .songs-pane__cta:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.18);
    border-color: var(--accent-turquoise);
  }
}
.songs-pane__cta:focus-visible {
  outline: 2px solid var(--accent-turquoise);
  outline-offset: 2px;
}
.songs-pane__cta-arrow {
  font-size: var(--fs-md);
  line-height: 1;
}
/* Sign-in nudge inside the card (anon) — sits under the live module,
   separated by the same hairline the meters use. */
.songs-pane__live:not([hidden]) + .songs-pane__anon {
  border-top: 1px solid var(--hairline-dark-lo);
  padding-top: var(--space-4);
}

.journey__head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0 0 0.4rem;
}
/* (Note glyph removed — Petřin pokyn 2026-06-03, header audit: title only.) */
.journey__title {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-bold);
  font-size: var(--fs-modal-title); /* content modal — big tier (M13.X-TITLE-TIERS) */
  letter-spacing: 0.02em;
  color: var(--text-dark);
}

.journey__lead {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
}
.journey__intro {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--text-muted);
}
/* Stat meters card — "STARS GIVEN ★ X /50" + "SONGS ECHOED N /12", two
   stacked label-row + pill-bar meters mirroring the Songs tab stats card
   (handoff songs_stats Direction C; Petřin pokyn 2026-06-05 — the echoed
   counter + bar moved here from the header band, label "Stars given" kills
   the "Your Stars" title stutter). Gold = stars, magenta = the Echo world. */
.journey__meter {
  display: flex;
  flex-direction: column;
  gap: 1rem; /* row↔row air — a touch more than the in-row 0.4rem so the
                label+bar pairs read as two groups (Petřin pokyn 2026-06-05) */
  margin-bottom: 0.9rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--border-on-light);
  border-radius: var(--radius-md);
  background: rgba(0, 0, 0, 0.18);
}
.journey__meter-row {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.journey__meter-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
}
.journey__meter-label {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Muted light — parity with the Songs tab meter labels (Petřin pokyn
     2026-06-05; was the teal --label-color field-label tone, 2026-06-02). */
  color: var(--text-light-muted);
}
.journey__meter-left {
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  color: var(--accent-copper);
  font-weight: var(--fw-bold);
}
.journey__meter-left b { font-size: var(--fs-lg); }
.journey__meter-star { width: 16px; height: 16px; }
.journey__meter-of {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-light-muted);
}
.journey__meter-sub {
  margin: 0; /* the card's column gap supplies the rhythm */
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.journey__meter-sub--empty {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--accent-copper-bright);
}
.journey__meter-sub--empty .icon { width: 14px; height: 14px; }

/* Continuous budget bar in the copper star tone. Replaces the per-star pip
   row: a 50-star budget (ADR-227) would render as ~50 hairline pips that read
   as a solid block; the fill carries the spent/free split, the "X /50" count
   the exact number. */
.journey__budget-bar {
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(var(--accent-copper-rgb), 0.18);
  overflow: hidden;
}
.journey__budget-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--accent-copper);
  transition: width 0.2s ease;
}
/* Echo-coverage twin — same anatomy, the Echo magenta. */
.journey__echoed-bar {
  height: 4px;
  border-radius: var(--radius-pill);
  background: rgba(var(--accent-mine-rgb), 0.16);
  overflow: hidden;
}
.journey__echoed-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--accent-mine);
  transition: width 0.2s ease;
}
/* Echoed count — the shared Echo-count magenta (echo likes / event likes). */
.journey__meter-left--mine {
  color: var(--magentaLift);
}

/* Tile grid — chrome lifted from .chart-row (radius 14, tabular rank, italic
   serif title). 3-up desktop, 2-up mobile (below). */
.journey__grid {
  display: grid;
  /* 2 cols (was 3) — in the 600px Journey dialog 3 cols cramped long feat.
     titles into too many lines (Petřin pokyn 2026-06-02). */
  grid-template-columns: repeat(2, 1fr);
  /* Tiles in a row stretch to equal height; "Tap to rate" pins to the bottom
     (foot margin-top:auto) so it sits at one vertical line (Petřin pokyn
     2026-06-02). */
  align-items: stretch;
  gap: 0.55rem;
}
.journey-tile {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  min-height: 72px;
  padding: 0.6rem 0.65rem;
  text-align: left;
  border: 1px solid var(--border-on-light);
  border-radius: 14px;
  background: var(--cream-surface);
  color: var(--text-dark);
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .journey-tile:not(.journey-tile--disabled):hover {
    border-color: rgba(var(--accent-copper-rgb), 0.5);
  }
}
.journey-tile__top-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.journey-tile__num {
  font-family: var(--font-body); /* clean Manrope numerals (Petřin pokyn 2026-06-02) */
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--text-light-muted);
}
/* Echoed "✓ →" pill — designer echo-corner handoff (Variant A,
   docs/handoff/echo-corner-handoff). A REAL button jumping to the fan's
   echoes for the song; the arrow signals a link, not just status. Designer
   palette maps to tokens: J.magenta = --accent-mine, J.magentaClean =
   --accent-magenta. Negative margin keeps the top-row height as before. */
.journey-tile__echo {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 7px 3px 4px;
  margin: -4px 0;
  border: 1px solid rgba(var(--accent-mine-rgb), 0.53);
  border-radius: var(--radius-pill);
  background: rgba(var(--accent-magenta-rgb), 0.11);
  color: var(--accent-mine);
  cursor: pointer;
  transition: background-color 0.14s ease, border-color 0.14s ease;
  /* Anchor for the invisible touch hit-zone below (M13 review MOB-004). */
  position: relative;
}
/* M13 review MOB-004 — the pill renders ~21px tall INSIDE the big "rate"
 * tile button; fingers need ≥44px without inflating the designer visual.
 * Invisible overlay extends the hit area on touch devices only; clicks on
 * a pseudo-element target the pill button, and the existing
 * stopPropagation keeps the tile's rate-action out of the way. */
@media (pointer: coarse) {
  .journey-tile__echo::after {
    content: "";
    position: absolute;
    inset: -12px -6px;
  }
}
@media (hover: hover) and (pointer: fine) {
  .journey-tile__echo:hover {
    background: rgba(var(--accent-magenta-rgb), 0.2);
    border-color: var(--accent-mine);
  }
}
/* The check keeps the canonical circled-check sprite as the pill's anchor —
   its own ring stands in for the designer's 1px circle border. */
.journey-tile__echo-check {
  display: grid;
  place-items: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: rgba(var(--accent-magenta-rgb), 0.18);
}
.journey-tile__echo-check .icon { width: 15px; height: 15px; }
.journey-tile__echo-arrow { display: block; flex-shrink: 0; }
.journey-tile__title {
  /* 0 0 auto (was 1 1 auto) — growing pushed "Tap to rate" to the bottom and
     opened a big gap under short titles (Petřin pokyn 2026-06-02). */
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-bold);
  font-size: var(--fs-lg); /* bigger — was --fs-md, read too small on mobile (Petřin pokyn) */
  line-height: 1.15;
  color: var(--text-dark);
}
/* The "(feat. …)" credit drops to its OWN line under the main title and stays
   readable — inline + tiny was unreadable on mobile (Petřin pokyn 2026-06-02).
   Relative em so it scales with whichever title it sits in (tile + rating
   dialog song). */
.title-feat {
  display: block;
  margin-top: 1px;
  font-size: 0.82em;
  font-weight: var(--fw-medium);
  font-style: italic;
  color: var(--text-muted);
}
.journey-tile__foot {
  display: flex;
  align-items: center;
  /* stars left, MY TOP chip pushed to the bottom-right corner of the tile
     (Petřin pokyn 2026-06-02 — top-right is taken by the echoed ✓). */
  justify-content: space-between;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-top: auto; /* pin to tile bottom so "Tap to rate" aligns across tiles */
}
.journey-tile__stars {
  display: inline-flex;
  align-items: center;
  gap: 0.22rem;
  font-family: var(--font-body); /* clean Manrope numerals (Petřin pokyn) */
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--accent-copper);
  font-weight: var(--fw-bold);
}
.journey-tile__stars .icon { width: 14px; height: 14px; }
.journey-tile__rate {
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  font-size: var(--fs-sm);
  color: var(--text-light-muted);
}
.journey-tile__rate .icon { width: 13px; height: 13px; }
/* MY TOP = the list .chip recipe (faint tinted fill + bright tone text + tone
   border — exactly like .chip--sub / .chip--online), in gold (Petřin pokyn
   2026-06-02 — match the list chips, NOT a solid pill). */
.journey-tile__top {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.5;
  background: rgba(var(--accent-copper-rgb), 0.18);
  color: var(--accent-copper-bright);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.48);
  white-space: nowrap;
}
/* Echoed = ONLY the corner "✓ →" pill — no tile wash (designer echo-corner
   handoff + Petřin pokyn 2026-06-04: the magenta surface overpowered the
   gold rating signal). Gold ★ = rating, magenta = Echo; the tile SURFACE
   belongs to the rating. */
/* Rated = soft gold wash + gold rank (designer demo: gradient + faint ring). */
.journey-tile--rated {
  border-color: rgba(var(--accent-copper-rgb), 0.55);
  background:
    linear-gradient(
      165deg,
      rgba(var(--accent-copper-rgb), 0.18) 0%,
      rgba(var(--accent-copper-rgb), 0.05) 65%,
      transparent
    ),
    var(--cream-surface);
  box-shadow: 0 0 0 1px rgba(var(--accent-copper-rgb), 0.12);
}
.journey-tile--rated .journey-tile__num { color: var(--accent-copper); }
/* Top-rated = a stronger gold frame on top of the rated wash (the MY TOP
   pill carries the label). No magenta to fight since the echoed wash is gone. */
.journey-tile--top {
  border-color: rgba(var(--accent-copper-rgb), 0.7);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-copper-rgb), 0.25);
}
.journey-tile--disabled {
  opacity: 0.4;
  cursor: default;
}

/* "Give stars" slider popover — a small focused modal opened from a tile. */
.rating-dialog {
  position: relative;
  width: 420px;
  max-width: 92vw;
  padding: 1.1rem 1.2rem 1rem;
  border: 1px solid var(--border-on-light);
  border-radius: 20px;
  background: linear-gradient(170deg, var(--surface-plum) 0%, var(--cream-surface) 100%);
  color: var(--text-dark);
  box-shadow: var(--shadow-modal), 0 30px 70px rgba(0, 0, 0, 0.5);
}
.rating-dialog::backdrop {
  background: rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.rating-dialog.is-saving { opacity: 0.85; }
.rating-dialog__close {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--border-on-light);
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease,
              border-color 150ms ease;
}
.rating-dialog__close .icon { width: 15px; height: 15px; }
.rating-dialog__eyebrow {
  margin: 0 0 0.2rem;
  font-size: var(--fs-2xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--label-color); /* soft-teal label tone, like modal field labels (Petřin pokyn) */
}
.rating-dialog__song {
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-bold);
  font-size: var(--fs-dialog-title); /* small tier — per-track give-stars popup, not a content modal (M13.X-TITLE-TIERS) */
  color: var(--text-dark);
}
.rating-dialog__count {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0 0 0.15rem;
  font-size: var(--fs-md);
  color: var(--text-muted);
}
.rating-dialog__count-star { width: 18px; height: 18px; color: var(--accent-copper); }
.rating-dialog__count-n {
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--accent-copper);
}
.rating-dialog__howto {
  margin: 0.5rem 0 0.4rem;
  font-size: var(--fs-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted); /* was --text-light-muted — read too dim (Petřin pokyn) */
}
.rating-slider {
  width: 100%;
  height: 22px;
  cursor: pointer;
  accent-color: var(--accent-copper);
}
/* "still to give" left, ± steppers right (Petřin pokyn 2026-06-10 — the teal ✓
   moved out of this row into the gold "Rate" button in the actions row below). */
.rating-dialog__foot {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.7rem;
}
.rating-dialog__left {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.rating-dialog__steppers { display: flex; gap: 0.4rem; justify-self: end; }
/* ✓ confirm = identical shape to a stepper but teal (vs the copper ±). Still
   used by the tour-flow confirm (.event-create__tour-new-foot). */
/* Two classes so the teal beats the later `.rating-step` copper rule (same
   single-class specificity would otherwise let copper win — Petřin pokyn). */
.rating-step.rating-step--confirm {
  background: rgba(var(--accent-turquoise-rgb), 0.14);
  border-color: rgba(var(--accent-turquoise-rgb), 0.5);
  color: var(--accent-turquoise);
}
.rating-step--confirm svg { width: 17px; height: 17px; }
.rating-step {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  font-size: var(--fs-lg);
  line-height: 1;
  border: 1px solid var(--border-on-light);
  border-radius: 10px;
  background: rgba(var(--accent-copper-rgb), 0.12);
  color: var(--accent-copper-bright);
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .rating-step:hover { background: rgba(var(--accent-copper-rgb), 0.2); }
}

/* M13.X-SONGS-TAB (ADR-237) Fáze 2 + Petřin pokyn 2026-06-10 — two equal-width
   actions side by side under the rating controls: the magenta ghost "Leave an
   Echo" and the gold-filled "Rate" (primary). Same height/radius/font so they
   read as a matched pair. */
.rating-dialog__actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.rating-dialog__echo,
.rating-dialog__rate {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: 10px;
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease;
}
/* Echo = magenta ghost. Magenta = the Echo/"Add a moment" world (HARD RULE 12);
   ghost treatment so it never outshouts the gold "Rate". */
.rating-dialog__echo {
  border: 1px solid rgba(var(--accent-mine-rgb), 0.45);
  background: rgba(var(--accent-mine-rgb), 0.10);
  color: var(--accent-mine);
}
/* Rate = gold filled (primary save+close). Reuses the canonical gold gradient
   (.btn-warn / .auth-btn--primary family) — gold = the star/rating world, so it
   outranks the ghost Echo as the window's main action (Petřin pokyn 2026-06-10). */
.rating-dialog__rate {
  border: 1px solid var(--accent-copper);
  background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper-deep) 100%);
  color: var(--surface-ink);
  font-weight: 700;
  box-shadow:
    0 4px 14px rgba(var(--accent-copper-rgb), 0.32),
    inset 0 1px 0 rgba(238, 240, 250, 0.30);
}
@media (hover: hover) and (pointer: fine) {
  .rating-dialog__echo:hover {
    background: rgba(var(--accent-mine-rgb), 0.18);
    border-color: var(--accent-mine);
  }
  .rating-dialog__rate:hover {
    background: linear-gradient(180deg, var(--accent-copper-bright) 0%, var(--accent-copper) 100%);
    transform: translateY(-1px);
  }
}
.rating-dialog__echo:focus-visible {
  outline: 2px solid var(--accent-mine);
  outline-offset: 2px;
}
.rating-dialog__rate:focus-visible {
  outline: 2px solid var(--accent-copper);
  outline-offset: 2px;
}
.rating-dialog__echo .icon,
.rating-dialog__rate .icon {
  width: 16px;
  height: 16px;
}

/* Mobile — 2-up tiles + the budget meter pinned while the grid scrolls. */
@media (max-width: 767px) {
  .journey__grid { grid-template-columns: repeat(2, 1fr); }
  /* The hero is a neutral block here (no band) — the header bar that used
     to supply this gap moved into the meter card (Petřin pokyn 2026-06-05). */
  .journey__hero { margin-bottom: 0.9rem; }
  .journey__meter {
    position: sticky;
    top: 0;
    z-index: 2;
  }
}
/* M13.X-LOCATE-JOIN (ADR-214) — JOIN active (open-gatherings filter ON).
   M13.X-JOIN-DISCOVERY (Petřino rozhodnutí 2026-06-03, po zamítnutém
   pressed-relief i filled pokusu): the resting look stays, ONLY the magenta
   ring thickens (full-opacity border + 1.5px spread ring — a wider border
   would shift the 44px layout) and the glow goes bold. */
.map-actions__btn--join.map-actions__btn--active {
  border-color: var(--accent-mine);
  box-shadow:
    0 0 0 2.5px var(--accent-mine),
    0 0 30px rgba(var(--accent-mine-rgb), 0.95),
    0 0 56px rgba(var(--accent-mine-rgb), 0.45),
    0 4px 14px rgba(0, 0, 0, 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
}

/* M15 ⑧ LANDMARK-HIGHLIGHT (ADR-266, Petřin pokyn 2026-06-06) — while the
   landmark filter is ON, the Places pebble gets the SAME active treatment
   as JOIN above, in its own family colour (copper, landmark world): resting
   look stays, only the ring thickens + glow goes bold. Toggled by map.js
   alongside body.map-landmarks-only. */
.spider-menu__button--active {
  border-color: var(--accent-copper);
  box-shadow:
    0 0 0 2.5px var(--accent-copper),
    0 0 30px rgba(var(--accent-copper-rgb), 0.95),
    0 0 56px rgba(var(--accent-copper-rgb), 0.45),
    0 4px 14px rgba(0, 0, 0, 0.30),
    inset 0 1px 0 rgba(238, 240, 250, 0.10);
}

/* LOCATE busy (waiting for a geolocation fix) — gentle pulse so the user sees
   the request is in flight without a separate spinner. */
.map-actions__btn--busy { animation: map-action-busy-pulse 1s ease-in-out infinite; }
@keyframes map-action-busy-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}
@media (prefers-reduced-motion: reduce) {
  .map-actions__btn--busy { animation: none; opacity: 0.7; }
}

/* M13.X-LOCATE-JOIN (ADR-214) — JOIN open-gatherings filter. When active, dim
   every marker EXCEPT open gatherings so the user can spot one to join. The
   divIcon className lands on .leaflet-marker-icon, so open-gathering heads
   carry .pin-marker-wrap--open right on that element; everything else
   (other moments, concerts, landmarks, clusters) fades back. App-wide so the
   desktop pebble + mobile tile share one behaviour. */
/* M13.X-JOIN-DISCOVERY (Petřin pokyn 2026-06-03 round 3) — while the filter
   is ON, open gatherings are PULLED OUT of the cluster group by map.js
   (_setOpenGatheringsUnclustered) and stand individually with their sonar
   rings at every zoom; clusters never highlight (the round-2 orb rings were
   rejected), they just dim with everything else. */
body.map-gatherings-only .leaflet-marker-icon:not(.pin-marker-wrap--open) {
  /* !important is required here: markercluster's clusterShow/Hide animations
     write INLINE opacity onto marker elements (re-triggered by the pull-out
     regrouping), and inline style beats any stylesheet rule otherwise. */
  opacity: 0.28 !important;
  transition: opacity 220ms ease;
}
body.map-gatherings-only .leaflet-marker-icon.pin-marker-wrap--open {
  opacity: 1 !important; /* beat a stray mid-animation inline opacity */
  transition: opacity 220ms ease;
  z-index: 650 !important;
}

/* M15 ⑧ LANDMARK-HIGHLIGHT (ADR-266) — sister filter to JOIN above, entered
   from the Places spider menu: landmarks (POI teardrops) stay lit, everything
   else dims. POIs are pulled OUT of poiClusterGroup by map.js
   (_setLandmarksUnclustered) so a cluster orb can never swallow a highlighted
   landmark. The two filters are mutually exclusive in JS — both body classes
   at once would dim everything. Same !important rationale as the join rules
   above (markercluster writes inline opacity onto marker elements). */
body.map-landmarks-only .leaflet-marker-icon:not(.poi-marker) {
  opacity: 0.28 !important;
  transition: opacity 220ms ease;
}
body.map-landmarks-only .leaflet-marker-icon.poi-marker {
  opacity: 1 !important;
  transition: opacity 220ms ease;
  z-index: 650 !important;
}


/* =====================================================================
 * M13.X-ECHO-MODAL-REDESIGN (ADR-236) — "Leave an Echo" designer redesign.
 * Source of truth: docs/handoff/echo_modal_redesign/handoff/ (Variant A).
 * Visual-only: group rhythm + header eyebrow + rating stepper + grouping
 * cards + map card + custom checkboxes. Behaviour untouched.
 * Petřiny volby (ADR-236): title = --fs-modal-title (ADR-231) · field
 * labels = canon 0.82rem · cards only when a candidate exists (ADR-228).
 * ===================================================================== */

/* ── Header — atmospheric mesh (the "NEW ECHO" eyebrow was removed per
      Petřin pokyn 2026-06-03 večer — superfluous, the title is enough) ── */

.pin-form__header {
  align-items: flex-start;
  padding-bottom: 0.9rem;
}
#pin-form .pin-form__close {
  margin-top: 0.15rem;
}

/* ── M13.X-HEADER-UNIFY (2026-06-04) — canonical big-modal header band ──
   The echo composer's designer header mesh (ADR-236) promoted to the shared
   canon for ALL large content modals: two-corner wash over a plum→transparent
   fade + hairline rule on the band's bottom edge. The band replaces the old
   "+" title badge as the carrier of each window's world identity.
   Desktop only — the mobile glass sticky bar keeps its own chrome (its
   #id[open] rules would out-rank this class anyway). */
@media (min-width: 768px) {
  .modal-band {
    border-bottom: 1px solid color-mix(in srgb, var(--border-on-light) 50%, transparent);
    background:
      radial-gradient(ellipse 90% 120% at 0% 0%, rgba(var(--band-a-rgb), 0.16) 0%, transparent 58%),
      radial-gradient(ellipse 80% 120% at 100% 100%, rgba(var(--band-b-rgb), 0.10) 0%, transparent 60%),
      linear-gradient(180deg, color-mix(in srgb, var(--surface-plum) 55%, transparent), transparent);
  }
  /* World tone pairs (Petřino rozhodnutí 2026-06-04) — A = top-left wash,
     B = bottom-right wash. Echo unchanged (= ADR-236 designer values). */
  .modal-band--echo     { --band-a-rgb: var(--accent-turquoise-rgb); --band-b-rgb: var(--accent-mine-rgb); }
  .modal-band--chart    { --band-a-rgb: var(--accent-violet-rgb);    --band-b-rgb: var(--accent-copper-rgb); }
  .modal-band--journey  { --band-a-rgb: var(--accent-copper-rgb);    --band-b-rgb: var(--accent-mine-rgb); }
  .modal-band--concert  { --band-a-rgb: var(--accent-turquoise-rgb); --band-b-rgb: var(--accent-violet-rgb); }
  .modal-band--online   { --band-a-rgb: var(--accent-turquoise-rgb); --band-b-rgb: var(--accent-online-rgb); }
  .modal-band--landmark { --band-a-rgb: var(--accent-turquoise-rgb); --band-b-rgb: var(--accent-copper-rgb); }
  /* Community Hub (ADR-252) — both washes turquoise: the hub is a purely
     teal world (Petřino rozhodnutí 2026-06-04), distinct from concert's
     teal+violet pair. */
  .modal-band--community { --band-a-rgb: var(--accent-turquoise-rgb); --band-b-rgb: var(--accent-turquoise-rgb); }
  /* Profile family (Petřin pokyn 2026-06-04) — Profile / Security /
     Preferences / Your data share one mono-magenta band: these windows
     are the user's own things, and "mine" is the magenta world. */
  .modal-band--profile { --band-a-rgb: var(--accent-mine-rgb); --band-b-rgb: var(--accent-mine-rgb); }
  /* Auth entry (Petřin pokyn 2026-06-04 — "dejme i přihlašovacímu a
     registračnímu oknu naši hlavičku") — mono-copper: auth is the copper
     world (copper submit CTAs + the mobile sheet's copper wash). */
  .modal-band--auth { --band-a-rgb: var(--accent-copper-rgb); --band-b-rgb: var(--accent-copper-rgb); }

  /* Geometry — the pin-form header already carries the dialog padding
     itself; the other dialogs pad on the <dialog>, so their headers pull
     to the edges with negative margins (the rounded dialog clips the
     square band corners via its overflow box). */
  .event-create__header.modal-band {
    margin: -1.25rem -1.4rem 1rem;
    padding: 1.25rem 2.6rem 0.9rem 1.4rem;
  }
  /* Hub dialog pads 1.5rem top / 0 bottom (footer band reaches the edge) —
     pull its band to the edges + let the canonical card supply the gap. */
  #community-hub-dialog .event-create__header.modal-band {
    margin: -1.5rem -1.4rem 0;
    padding: 1.5rem 2.6rem 0.9rem 1.4rem;
  }
  /* Profile-family dialogs (Petřin pokyn 2026-06-04) pad 1.7rem/1.5rem and
     stack as flex+gap — pull the band to the edges; the dialog's own gap
     supplies the head→body rhythm (bottom margin 0). */
  dialog[id^="auth-"] .event-create__header.modal-band {
    margin: -1.7rem -1.5rem 0;
    padding: 1.7rem 2.6rem 0.9rem 1.5rem;
  }
  /* Login / Register carry the band INSIDE the form (legacy per-element
     margins, no flex gap) — the band itself supplies the head→body gap. */
  #auth-login-form > .event-create__header.modal-band,
  #auth-register-form > .event-create__header.modal-band,
  #auth-magic-form > .event-create__header.modal-band {
    margin-bottom: 1rem;
  }
  .songs-dialog__header {
    margin: -1.25rem -1.25rem 0.9rem;
    padding: 1.25rem 1.25rem 0.9rem;
  }
  /* The band's own padding supplies the head→body gap now. */
  .songs-dialog__header .songs-dialog__intro {
    margin-bottom: 0;
  }
  .journey-dialog .journey__hero.modal-band {
    /* -1.4rem dialog padding + -0.2rem .journey padding-top */
    margin: calc(-1.4rem - 0.2rem) -1.5rem 1rem;
    padding: 1.4rem 1.5rem 0.9rem;
  }
}

/* ── Group scaffold — quiet tracked head + hairline rule ──────────── */

.echo-group {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
/* The grouping field renders its own head (it folds away with the cards) —
   give it the same head↔content rhythm as a regular .echo-group. The lead
   paragraph that used to pad this gap was removed (Petřin pokyn 2026-06-03).
   :not([hidden]) so display:flex can't resurrect the hidden container. */
.echo-grouping-field:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.echo-group__head {
  display: flex;
  align-items: center;
  gap: 12px;
}
.echo-group__head h3 {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-3xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-light-muted);
  white-space: nowrap;
}
.echo-group__head::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--border-on-light), transparent);
}
/* A group whose entire content is [hidden] folds away — head included
   (registered users' WHO group, song group with no album, an all-hidden
   BEFORE YOU POST). Pure CSS so form.js needs no per-group bookkeeping. */
.pin-form__body .echo-group:not(:has(> :not(.echo-group__head):not([hidden]))) {
  display: none;
}

/* (Comment counter overlay REVERTED per Petřin pokyn 2026-06-03 večer —
   „číslování zasahuje do textarea". The canonical .field__counter placement
   below the box applies again; no override needed.) */

/* ── Song select — teal chevron suffix ────────────────────────────── */

.field__select-wrap {
  position: relative;
  display: block;
}
.field__select-wrap select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 2.2rem;
}
.field__select-chevron {
  position: absolute;
  right: 0.95rem;
  top: 50%;
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--accent-turquoise);
  border-bottom: 2px solid var(--accent-turquoise);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

/* ── Rating — canonical give-stars anatomy (Petřin pokyn 2026-06-03):
      one gold star + big gold number + the gold slider + square ± steppers,
      REUSING .rating-dialog__count / .rating-slider / .rating-step from the
      Your Ratings dialog so the two surfaces can never drift. ───────── */

.echo-rate {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  /* The rating reveals itself when a song is tagged — wrap the whole block in
     a soft gold frame (the stars' own --accent-copper) so it reads as ONE
     contained card, not a flood of loose light text in the composer (Petřin
     pokyn 2026-06-09). Mirrors the day-of gold pill's tint+border recipe. */
  padding: var(--space-4);
  border: 1px solid rgba(var(--accent-copper-rgb), 0.28);
  border-radius: var(--radius-md);
  background: rgba(var(--accent-copper-rgb), 0.05);
}
.echo-rate__state {
  margin: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.echo-rate__count {
  margin: 0; /* the .rating-dialog__count bottom margin is for the dialog */
}
/* Row 1: count left, square ± steppers right (Petřin pokyn — above slider). */
.echo-rate__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
/* Row 3 — the album-budget line. Two halves on one row (Petřin pokyn
   2026-06-09): LEFT = the concept anchor ("Shared across the whole album"),
   set against Row 1's "…for this track" so the contrast itself teaches the
   shared-budget model (tester confusion: stars read as a per-song score);
   RIGHT = the live "{n} of {total} still to give" counter. --fs-sm, not the
   old 11px --fs-2xs (11px muted read too dim — Petra, repeatedly). Wraps to
   two rows on a narrow composer. */
/* Quiet "you can change this anytime" reassurance under the count row. Sits
   where the give-stars mockup's drag hint lived. --fs-2xs to match the
   "{n} of {total} still to give" counter exactly (Petřin pokyn 2026-06-09 —
   the new labels must read at the SAME small size, not a flood of light text). */
.echo-rate__note {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.echo-rate__budget {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.15rem 0.6rem;
}
.echo-rate__budget-album {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.echo-rate__hint {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.echo-rate--empty .echo-rate__hint {
  color: var(--warning);
}
/* ── ADR-244 "Counted for <flag> Country" transparency line ──
   Shared by the composer's inline rating and the give-stars dialog; shown
   to everyone (amendment 2026-06-04): action tail without a declared
   nationality, "from your profile" info tail with one. */
.rating-country {
  margin: var(--space-2) 0 0;
  /* --fs-sm, not 2xs — 11px muted was borderline unreadable (Petra
     2026-06-04, twice). Render-measured: the pill variant still holds one
     row at 13px inside the 560px composer. */
  font-size: var(--fs-sm);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.3rem;
}
.rating-country b {
  font-weight: var(--fw-semibold);
  color: var(--text-dark);
}
.rating-country__flag {
  height: 12px;
  width: auto;
  border-radius: 2px;
  display: inline-block;
}
/* ── "Counted for" source tail — composer only (Petra 2026-06-09, restored
      after ADR-285 dropped it from both surfaces). ─────────────────────── */
/* The declared-nationality info tail ("from your profile") — bright enough
   to read on noir; it IS the feedback the fan came back for. */
.rating-country__from {
  color: var(--text-dark);
}
.rating-country__sep {
  opacity: 0.6;
}
/* The "set your country…" tail is a real button (opens Profile) styled as
   a quiet inline link — TEAL, it is an action (Petřin pokyn 2026-06-04). */
.rating-country__link {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--accent-turquoise);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.rating-country__link:hover {
  color: var(--accent-copper);
}
/* Composer surface: the generic `#pin-form button` chrome keeps its pill
   geometry on this link (excluded from the generic rule above per the
   documented :not() pattern, then re-stated 1:1 here) — ONLY the colours
   flip to teal. Petřin pokyn 2026-06-04: „jen změna barvy, žádné
   přeuspořádání". */
#pin-form .rating-country__link {
  background: transparent;
  border: 1px solid rgba(var(--accent-turquoise-rgb), 0.5);
  border-radius: 10px;
  padding: 0.65rem 1.3rem;
  font-weight: 600;
  transition: background 200ms ease, border-color 200ms ease, transform 200ms ease;
}
@media (hover: hover) and (pointer: fine) {
  #pin-form .rating-country__link:hover {
    background: rgba(var(--accent-turquoise-rgb), 0.08);
    border-color: var(--accent-turquoise);
    color: var(--accent-turquoise);
  }
}
/* The give-stars dialog never disables its ± — the inline composer does
   (value 0 / budget ceiling). One small state rule on the canonical class. */
.rating-step:disabled {
  opacity: 0.35;
  cursor: default;
}

/* ── Grouping cards — Variant A (tone accent + glyph well + radio) ── */

.echo-grouping {
  display: flex;
  flex-direction: column;
  gap: 9px;
}
#pin-form .echo-grouping__card {
  /* Beats the #pin-form generic button chrome. */
  position: relative;
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  text-align: left;
  padding: 12px 14px 12px 15px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-on-light);
  border-left: 3px solid var(--tone, var(--border-on-light));
  background: var(--cream-lifted);
  color: var(--text-dark);
  font: inherit;
  cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease,
              box-shadow 160ms ease, transform 120ms ease;
}
@media (hover: hover) and (pointer: fine) {
  #pin-form .echo-grouping__card:hover {
    transform: translateY(-1px);
    border-color: var(--border-strong-light);
    border-left-color: var(--tone, var(--border-strong-light));
    background: var(--cream-lifted);
  }
}
.echo-grouping__card[data-tone="mine"] {
  --tone: var(--accent-mine);
  --tone-rgb: var(--accent-mine-rgb);
}
.echo-grouping__card[data-tone="iris"] {
  --tone: var(--accent-violet-soft);
  --tone-rgb: var(--accent-violet-soft-rgb);
}
#pin-form .echo-grouping__card.is-selected {
  background:
    linear-gradient(180deg, rgba(var(--tone-rgb), 0.14), color-mix(in srgb, var(--text-dark) 3%, transparent)),
    var(--cream-lifted);
  border-color: rgba(var(--tone-rgb), 0.55);
  border-left-color: var(--tone);
  box-shadow:
    0 0 0 1px rgba(var(--tone-rgb), 0.30),
    0 8px 24px rgba(0, 0, 0, 0.35);
}
.echo-grouping__glyph {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  overflow: hidden;
  color: var(--text-dark);
  background: linear-gradient(
    180deg,
    rgba(var(--tone-rgb, 238, 240, 250), 0.26),
    rgba(var(--tone-rgb, 238, 240, 250), 0.08)
  );
  border: 1px solid rgba(var(--tone-rgb, 238, 240, 250), 0.40);
}
.echo-grouping__glyph svg {
  width: 19px;
  height: 19px;
}
.echo-grouping__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.echo-grouping__eyebrow {
  font-weight: 700;
  font-size: var(--fs-3xs);
  line-height: 1;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--tone, var(--label-color));
  margin-bottom: 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.echo-grouping__name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: var(--fs-lg);
  line-height: 1.15;
  color: var(--text-dark);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.echo-grouping__meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 4px;
  font-size: var(--fs-2xs);
  font-weight: 500;
  line-height: 1.2;
  color: var(--text-muted);
  letter-spacing: 0.01em;
}
.echo-grouping__dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--text-light-muted);
}
.echo-grouping__distance {
  font-weight: 700;
  color: var(--accent-turquoise);
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.echo-grouping__radio {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border-on-light);
  display: grid;
  place-items: center;
  transition: border-color 160ms ease;
}
.echo-grouping__card.is-selected .echo-grouping__radio {
  border-color: var(--tone);
}
.echo-grouping__radio::after {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--tone, var(--text-muted));
  transform: scale(0);
  transition: transform 160ms cubic-bezier(0.2, 0.9, 0.3, 1.5);
}
.echo-grouping__card.is-selected .echo-grouping__radio::after {
  transform: scale(1);
}

/* ── Map card — circular chevron (rest of the card lives with the
      .echo-location__precision block, consolidated in place) ────────── */

#pin-form .echo-location__precision-toggle {
  flex-shrink: 0;
  margin-top: 1px;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--cream-surface);
  border: 1px solid var(--border-on-light);
  color: var(--accent-turquoise);
  font-size: 0.7rem;
  line-height: 1;
  cursor: pointer;
}

/* ── Custom checkboxes — teal mark (gold in the test banner) ──────── */
/* Shared by the open-gathering opt-in AND the test-data row (all 4 create
   modals use .test-data-field → one consistent checkbox everywhere). */

.open-gathering__toggle input[type="checkbox"],
.test-data-field input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 19px;
  height: 19px;
  flex-shrink: 0;
  border-radius: 6px;
  background-color: var(--cream-sunken);
  border: 1px solid var(--border-strong-light);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 13px 13px;
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease;
}
.open-gathering__toggle input[type="checkbox"]:checked {
  background-color: var(--accent-turquoise);
  border-color: var(--accent-turquoise);
  /* Ink check on the bright fill (a data URI cannot read var(); %230a0d1e =
     --cream-base, the canonical ink behind --text-on-accent). */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230a0d1e' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
.test-data-field input[type="checkbox"]:checked {
  background-color: var(--warning);
  border-color: var(--warning);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230a0d1e' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
.open-gathering__toggle input[type="checkbox"]:focus-visible,
.test-data-field input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--accent-turquoise-rgb), 0.30);
}
.test-data-field input[type="checkbox"]:focus-visible {
  box-shadow: 0 0 0 3px rgba(var(--warning-rgb), 0.30);
}
