/* AUTO-GENERATED by `rails app_css:build` — do not edit. Source: app/assets/stylesheets/application.css */
/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/*
 * ── Kanonische Breakpoints (Single Source of Truth) ──────────────────────
 * Diese Stylesheets werden von Propshaft roh ausgeliefert (kein Build-Step),
 * daher native CSS-Range-Syntax statt @custom-media/var(). IMMER diese exakten
 * Grenzen verwenden — keine Off-by-ones (767/769/639px) und keine doppelten
 * Schreibweisen (768px vs. 48rem) mehr.
 *
 *   sm   40rem  (640px)    @media (width >= 40rem)  /  (width < 40rem)
 *   md   48rem  (768px)    @media (width >= 48rem)  /  (width < 48rem)
 *   lg   64rem  (1024px)   @media (width >= 64rem)  /  (width < 64rem)
 *   xl   80rem  (1280px)   @media (width >= 80rem)  /  (width < 80rem)
 *
 * Bewusste Sonder-Breakpoints (kein Teil der Skala, je nur an einer Stelle):
 *   560px / 860px / 900px / 1080px  → ebenfalls Range-Syntax: (width < 860px)
 *
 * Gilt auch für die separat gebauten Tailwind-Bundles (tailwind/application.css,
 * tailwind/public_gallery.css, tailwind/global_search.css).
 * ─────────────────────────────────────────────────────────────────────────
 */

/* Foundation (always required when using gui_components) */

/* ===== gui_components/foundation.css ===== */
/*
 * GuiComponents Foundation: Base styles required by all components.
 * Import this when using selective component CSS loading.
 */


/* ===== ../fonts.css ===== */
/* Inter font - Variable fonts with static fallbacks */

@font-face {
  font-family: InterVariable;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/InterVariable-a1d74d4f.woff2") format("woff2");
}

@font-face {
  font-family: InterVariable;
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/InterVariable-Italic-b72f1b36.woff2") format("woff2");
}

/* Static fonts fallbacks */
@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 100; 
  font-display: swap; 
  src: url("/assets/Inter-Thin-e7fc3bde.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 100; 
  font-display: swap; 
  src: url("/assets/Inter-ThinItalic-f056574f.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 200; 
  font-display: swap; 
  src: url("/assets/Inter-ExtraLight-895a31a4.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 200; 
  font-display: swap; 
  src: url("/assets/Inter-ExtraLightItalic-e4096887.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 300; 
  font-display: swap; 
  src: url("/assets/Inter-Light-ed7700a5.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 300; 
  font-display: swap; 
  src: url("/assets/Inter-LightItalic-4ca792e0.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 400; 
  font-display: swap; 
  src: url("/assets/Inter-Regular-14a9d9e2.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 400; 
  font-display: swap; 
  src: url("/assets/Inter-Italic-6a8ff56c.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 500; 
  font-display: swap; 
  src: url("/assets/Inter-Medium-88799d0f.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 500; 
  font-display: swap; 
  src: url("/assets/Inter-MediumItalic-a32bcf6b.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 600; 
  font-display: swap; 
  src: url("/assets/Inter-SemiBold-5f190ea5.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 600; 
  font-display: swap; 
  src: url("/assets/Inter-SemiBoldItalic-98f87f91.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 700; 
  font-display: swap; 
  src: url("/assets/Inter-Bold-b8e29d1a.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 700; 
  font-display: swap; 
  src: url("/assets/Inter-BoldItalic-b8bf352f.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 800; 
  font-display: swap; 
  src: url("/assets/Inter-ExtraBold-d2dd0dd6.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 800; 
  font-display: swap; 
  src: url("/assets/Inter-ExtraBoldItalic-b89d3bc6.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: normal; 
  font-weight: 900; 
  font-display: swap; 
  src: url("/assets/Inter-Black-a9bf4ad8.woff2") format("woff2"); 
}

@font-face { 
  font-family: "Inter"; 
  font-style: italic; 
  font-weight: 900; 
  font-display: swap; 
  src: url("/assets/Inter-BlackItalic-578768a0.woff2") format("woff2"); 
}

/* ===== ../colors.css ===== */
:root {
    --slate-50: oklch(0.984 0.003 247.858);
    --slate-100: oklch(0.968 0.007 247.896);
    --slate-200: oklch(0.929 0.013 255.508);
    --slate-300: oklch(0.869 0.022 252.894);
    --slate-400: oklch(0.704 0.04 256.788);
    --slate-500: oklch(0.554 0.046 257.417);
    --slate-600: oklch(0.446 0.043 257.281);
    --slate-700: oklch(0.372 0.044 257.287);
    --slate-800: oklch(0.279 0.041 260.031);
    --slate-900: oklch(0.208 0.042 265.755);
    --slate-950: oklch(0.129 0.042 264.695);

    --gray-50: oklch(0.985 0.002 247.839);
    --gray-100: oklch(0.967 0.003 264.542);
    --gray-200: oklch(0.928 0.006 264.531);
    --gray-300: oklch(0.872 0.01 258.338);
    --gray-400: oklch(0.707 0.022 261.325);
    --gray-500: oklch(0.551 0.027 264.364);
    --gray-600: oklch(0.446 0.03 256.802);
    --gray-700: oklch(0.373 0.034 259.733);
    --gray-800: oklch(0.278 0.033 256.848);
    --gray-900: oklch(0.21 0.034 264.665);
    --gray-950: oklch(0.13 0.028 261.692);

    --zinc-50: oklch(0.985 0 0);
    --zinc-100: oklch(0.967 0.001 286.375);
    --zinc-200: oklch(0.92 0.004 286.32);
    --zinc-300: oklch(0.871 0.006 286.286);
    --zinc-400: oklch(0.705 0.015 286.067);
    --zinc-500: oklch(0.552 0.016 285.938);
    --zinc-600: oklch(0.442 0.017 285.786);
    --zinc-700: oklch(0.37 0.013 285.805);
    --zinc-800: oklch(0.274 0.006 286.033);
    --zinc-900: oklch(0.21 0.006 285.885);
    --zinc-950: oklch(0.141 0.005 285.823);

    --neutral-50: oklch(0.985 0 0);
    --neutral-100: oklch(0.97 0 0);
    --neutral-200: oklch(0.922 0 0);
    --neutral-300: oklch(0.87 0 0);
    --neutral-400: oklch(0.708 0 0);
    --neutral-500: oklch(0.556 0 0);
    --neutral-600: oklch(0.439 0 0);
    --neutral-700: oklch(0.371 0 0);
    --neutral-800: oklch(0.269 0 0);
    --neutral-900: oklch(0.205 0 0);
    --neutral-950: oklch(0.145 0 0);

    --stone-50: oklch(0.985 0.001 106.423);
    --stone-100: oklch(0.97 0.001 106.424);
    --stone-200: oklch(0.923 0.003 48.717);
    --stone-300: oklch(0.869 0.005 56.366);
    --stone-400: oklch(0.709 0.01 56.259);
    --stone-500: oklch(0.553 0.013 58.071);
    --stone-600: oklch(0.444 0.011 73.639);
    --stone-700: oklch(0.374 0.01 67.558);
    --stone-800: oklch(0.268 0.007 34.298);
    --stone-900: oklch(0.216 0.006 56.043);
    --stone-950: oklch(0.147 0.004 49.25);

    --red-50: oklch(0.971 0.013 17.38);
    --red-100: oklch(0.936 0.032 17.717);
    --red-200: oklch(0.885 0.062 18.334);
    --red-300: oklch(0.808 0.114 19.571);
    --red-400: oklch(0.704 0.191 22.216);
    --red-500: oklch(0.637 0.237 25.331);
    --red-600: oklch(0.577 0.245 27.325);
    --red-700: oklch(0.505 0.213 27.518);
    --red-800: oklch(0.444 0.177 26.899);
    --red-900: oklch(0.396 0.141 25.723);
    --red-950: oklch(0.258 0.092 26.042);

    --orange-50: oklch(0.98 0.016 73.684);
    --orange-100: oklch(0.954 0.038 75.164);
    --orange-200: oklch(0.901 0.076 70.697);
    --orange-300: oklch(0.837 0.128 66.29);
    --orange-400: oklch(0.75 0.183 55.934);
    --orange-500: oklch(0.705 0.213 47.604);
    --orange-600: oklch(0.646 0.222 41.116);
    --orange-700: oklch(0.553 0.195 38.402);
    --orange-800: oklch(0.47 0.157 37.304);
    --orange-900: oklch(0.408 0.123 38.172);
    --orange-950: oklch(0.266 0.079 36.259);

    --amber-50: oklch(0.987 0.022 95.277);
    --amber-100: oklch(0.962 0.059 95.617);
    --amber-200: oklch(0.924 0.12 95.746);
    --amber-300: oklch(0.879 0.169 91.605);
    --amber-400: oklch(0.828 0.189 84.429);
    --amber-500: oklch(0.769 0.188 70.08);
    --amber-600: oklch(0.666 0.179 58.318);
    --amber-700: oklch(0.555 0.163 48.998);
    --amber-800: oklch(0.473 0.137 46.201);
    --amber-900: oklch(0.414 0.112 45.904);
    --amber-950: oklch(0.279 0.077 45.635);

    --yellow-50: oklch(0.987 0.026 102.212);
    --yellow-100: oklch(0.973 0.071 103.193);
    --yellow-200: oklch(0.945 0.129 101.54);
    --yellow-300: oklch(0.905 0.182 98.111);
    --yellow-400: oklch(0.852 0.199 91.936);
    --yellow-500: oklch(0.795 0.184 86.047);
    --yellow-600: oklch(0.681 0.162 75.834);
    --yellow-700: oklch(0.554 0.135 66.442);
    --yellow-800: oklch(0.476 0.114 61.907);
    --yellow-900: oklch(0.421 0.095 57.708);
    --yellow-950: oklch(0.286 0.066 53.813);

    --lime-50: oklch(0.986 0.031 120.757);
    --lime-100: oklch(0.967 0.067 122.328);
    --lime-200: oklch(0.938 0.127 124.321);
    --lime-300: oklch(0.897 0.196 126.665);
    --lime-400: oklch(0.841 0.238 128.85);
    --lime-500: oklch(0.768 0.233 130.85);
    --lime-600: oklch(0.648 0.2 131.684);
    --lime-700: oklch(0.532 0.157 131.589);
    --lime-800: oklch(0.453 0.124 130.933);
    --lime-900: oklch(0.405 0.101 131.063);
    --lime-950: oklch(0.274 0.072 132.109);

    --green-50: oklch(0.982 0.018 155.826);
    --green-100: oklch(0.962 0.044 156.743);
    --green-200: oklch(0.925 0.084 155.995);
    --green-300: oklch(0.871 0.15 154.449);
    --green-400: oklch(0.792 0.209 151.711);
    --green-500: oklch(0.723 0.219 149.579);
    --green-600: oklch(0.627 0.194 149.214);
    --green-700: oklch(0.527 0.154 150.069);
    --green-800: oklch(0.448 0.119 151.328);
    --green-900: oklch(0.393 0.095 152.535);
    --green-950: oklch(0.266 0.065 152.934);

    --emerald-50: oklch(0.979 0.021 166.113);
    --emerald-100: oklch(0.95 0.052 163.051);
    --emerald-200: oklch(0.905 0.093 164.15);
    --emerald-300: oklch(0.845 0.143 164.978);
    --emerald-400: oklch(0.765 0.177 163.223);
    --emerald-500: oklch(0.696 0.17 162.48);
    --emerald-600: oklch(0.596 0.145 163.225);
    --emerald-700: oklch(0.508 0.118 165.612);
    --emerald-800: oklch(0.432 0.095 166.913);
    --emerald-900: oklch(0.378 0.077 168.94);
    --emerald-950: oklch(0.262 0.051 172.552);

    --teal-50: oklch(0.984 0.014 180.72);
    --teal-100: oklch(0.953 0.051 180.801);
    --teal-200: oklch(0.91 0.096 180.426);
    --teal-300: oklch(0.855 0.138 181.071);
    --teal-400: oklch(0.777 0.152 181.912);
    --teal-500: oklch(0.704 0.14 182.503);
    --teal-600: oklch(0.6 0.118 184.704);
    --teal-700: oklch(0.511 0.096 186.391);
    --teal-800: oklch(0.437 0.078 188.216);
    --teal-900: oklch(0.386 0.063 188.416);
    --teal-950: oklch(0.277 0.046 192.524);

    --cyan-50: oklch(0.984 0.019 200.873);
    --cyan-100: oklch(0.956 0.045 203.388);
    --cyan-200: oklch(0.917 0.08 205.041);
    --cyan-300: oklch(0.865 0.127 207.078);
    --cyan-400: oklch(0.789 0.154 211.53);
    --cyan-500: oklch(0.715 0.143 215.221);
    --cyan-600: oklch(0.609 0.126 221.723);
    --cyan-700: oklch(0.52 0.105 223.128);
    --cyan-800: oklch(0.45 0.085 224.283);
    --cyan-900: oklch(0.398 0.07 227.392);
    --cyan-950: oklch(0.302 0.056 229.695);

    --sky-50: oklch(0.977 0.013 236.62);
    --sky-100: oklch(0.951 0.026 236.824);
    --sky-200: oklch(0.901 0.058 230.902);
    --sky-300: oklch(0.828 0.111 230.318);
    --sky-400: oklch(0.746 0.16 232.661);
    --sky-500: oklch(0.685 0.169 237.323);
    --sky-600: oklch(0.588 0.158 241.966);
    --sky-700: oklch(0.5 0.134 242.749);
    --sky-800: oklch(0.443 0.11 240.79);
    --sky-900: oklch(0.391 0.09 240.876);
    --sky-950: oklch(0.293 0.066 243.157);

    --blue-50: oklch(0.97 0.014 254.604);
    --blue-100: oklch(0.932 0.032 255.585);
    --blue-200: oklch(0.882 0.059 254.128);
    --blue-300: oklch(0.809 0.105 251.813);
    --blue-400: oklch(0.707 0.165 254.624);
    --blue-500: oklch(0.623 0.214 259.815);
    --blue-600: oklch(0.546 0.245 262.881);
    --blue-700: oklch(0.488 0.243 264.376);
    --blue-800: oklch(0.424 0.199 265.638);
    --blue-900: oklch(0.379 0.146 265.522);
    --blue-950: oklch(0.282 0.091 267.935);

    --indigo-50: oklch(0.962 0.018 272.314);
    --indigo-100: oklch(0.93 0.034 272.788);
    --indigo-200: oklch(0.87 0.065 274.039);
    --indigo-300: oklch(0.785 0.115 274.713);
    --indigo-400: oklch(0.673 0.182 276.935);
    --indigo-500: oklch(0.585 0.233 277.117);
    --indigo-600: oklch(0.511 0.262 276.966);
    --indigo-700: oklch(0.457 0.24 277.023);
    --indigo-800: oklch(0.398 0.195 277.366);
    --indigo-900: oklch(0.359 0.144 278.697);
    --indigo-950: oklch(0.257 0.09 281.288);

    --violet-50: oklch(0.969 0.016 293.756);
    --violet-100: oklch(0.943 0.029 294.588);
    --violet-200: oklch(0.894 0.057 293.283);
    --violet-300: oklch(0.811 0.111 293.571);
    --violet-400: oklch(0.702 0.183 293.541);
    --violet-500: oklch(0.606 0.25 292.717);
    --violet-600: oklch(0.541 0.281 293.009);
    --violet-700: oklch(0.491 0.27 292.581);
    --violet-800: oklch(0.432 0.232 292.759);
    --violet-900: oklch(0.38 0.189 293.745);
    --violet-950: oklch(0.283 0.141 291.089);

    --purple-50: oklch(0.977 0.014 308.299);
    --purple-100: oklch(0.946 0.033 307.174);
    --purple-200: oklch(0.902 0.063 306.703);
    --purple-300: oklch(0.827 0.119 306.383);
    --purple-400: oklch(0.714 0.203 305.504);
    --purple-500: oklch(0.627 0.265 303.9);
    --purple-600: oklch(0.558 0.288 302.321);
    --purple-700: oklch(0.496 0.265 301.924);
    --purple-800: oklch(0.438 0.218 303.724);
    --purple-900: oklch(0.381 0.176 304.987);
    --purple-950: oklch(0.291 0.149 302.717);

    --fuchsia-50: oklch(0.977 0.017 320.058);
    --fuchsia-100: oklch(0.952 0.037 318.852);
    --fuchsia-200: oklch(0.903 0.076 319.62);
    --fuchsia-300: oklch(0.833 0.145 321.434);
    --fuchsia-400: oklch(0.74 0.238 322.16);
    --fuchsia-500: oklch(0.667 0.295 322.15);
    --fuchsia-600: oklch(0.591 0.293 322.896);
    --fuchsia-700: oklch(0.518 0.253 323.949);
    --fuchsia-800: oklch(0.452 0.211 324.591);
    --fuchsia-900: oklch(0.401 0.17 325.612);
    --fuchsia-950: oklch(0.293 0.136 325.661);

    --pink-50: oklch(0.971 0.014 343.198);
    --pink-100: oklch(0.948 0.028 342.258);
    --pink-200: oklch(0.899 0.061 343.231);
    --pink-300: oklch(0.823 0.12 346.018);
    --pink-400: oklch(0.718 0.202 349.761);
    --pink-500: oklch(0.656 0.241 354.308);
    --pink-600: oklch(0.592 0.249 0.584);
    --pink-700: oklch(0.525 0.223 3.958);
    --pink-800: oklch(0.459 0.187 3.815);
    --pink-900: oklch(0.408 0.153 2.432);
    --pink-950: oklch(0.284 0.109 3.907);

    --rose-50: oklch(0.969 0.015 12.422);
    --rose-100: oklch(0.941 0.03 12.58);
    --rose-200: oklch(0.892 0.058 10.001);
    --rose-300: oklch(0.81 0.117 11.638);
    --rose-400: oklch(0.712 0.194 13.428);
    --rose-500: oklch(0.645 0.246 16.439);
    --rose-600: oklch(0.586 0.253 17.585);
    --rose-700: oklch(0.514 0.222 16.935);
    --rose-800: oklch(0.455 0.188 13.697);
    --rose-900: oklch(0.41 0.159 10.272);
    --rose-950: oklch(0.271 0.105 12.094);
}


/* ===== ../brand_colors.css ===== */
/*
 * Dynamic Brand Color System
 *
 * OKLCH color scales driven by hue + chroma anchor variables.
 * Each scale has 11 stops (50-950) with lightness and chroma computed via calc().
 *
 * To customize at build time, override the anchors in your host app's theme.css:
 *
 *   :root {
 *     --primary-hue: 340;
 *     --primary-chroma: 0.22;
 *   }
 *
 * To customize at runtime (e.g. theme picker):
 *
 *   document.documentElement.style.setProperty('--primary-hue', '340');
 *
 * OKLCH format: oklch(lightness chroma hue)
 *   - Lightness: 0–1 (0 = black, 1 = white)
 *   - Chroma: 0–0.4 (color intensity)
 *   - Hue: 0–360 (color angle)
 */

:root {
  /* === Configurable anchors — change these to re-theme the entire app === */
  --primary-hue: 262;
  --primary-chroma: 0.05;
  --secondary-hue: 84;
  --secondary-chroma: 0.05;
  --accent-hue: 84;
  --accent-chroma: 0.12;
  --neutral-hue: 262;
  --neutral-chroma: 0.012;

  /* === Primary === */
  --primary-50:  oklch(0.97 calc(var(--primary-chroma) * 0.07) var(--primary-hue));
  --primary-100: oklch(0.93 calc(var(--primary-chroma) * 0.13) var(--primary-hue));
  --primary-200: oklch(0.87 calc(var(--primary-chroma) * 0.25) var(--primary-hue));
  --primary-300: oklch(0.79 calc(var(--primary-chroma) * 0.44) var(--primary-hue));
  --primary-400: oklch(0.67 calc(var(--primary-chroma) * 0.70) var(--primary-hue));
  --primary-500: oklch(0.59 calc(var(--primary-chroma) * 0.90) var(--primary-hue));
  --primary-600: oklch(0.51 var(--primary-chroma) var(--primary-hue));
  --primary-700: oklch(0.46 calc(var(--primary-chroma) * 0.92) var(--primary-hue));
  --primary-800: oklch(0.40 calc(var(--primary-chroma) * 0.75) var(--primary-hue));
  --primary-900: oklch(0.36 calc(var(--primary-chroma) * 0.55) var(--primary-hue));
  --primary-950: oklch(0.26 calc(var(--primary-chroma) * 0.35) var(--primary-hue));

  /* === Secondary === */
  --secondary-50:  oklch(0.97 calc(var(--secondary-chroma) * 0.15) var(--secondary-hue));
  --secondary-100: oklch(0.95 calc(var(--secondary-chroma) * 0.37) var(--secondary-hue));
  --secondary-200: oklch(0.91 calc(var(--secondary-chroma) * 0.66) var(--secondary-hue));
  --secondary-300: oklch(0.85 calc(var(--secondary-chroma) * 0.93) var(--secondary-hue));
  --secondary-400: oklch(0.77 var(--secondary-chroma) var(--secondary-hue));
  --secondary-500: oklch(0.70 var(--secondary-chroma) var(--secondary-hue));
  --secondary-600: oklch(0.60 calc(var(--secondary-chroma) * 0.86) var(--secondary-hue));
  --secondary-700: oklch(0.51 calc(var(--secondary-chroma) * 0.71) var(--secondary-hue));
  --secondary-800: oklch(0.43 calc(var(--secondary-chroma) * 0.57) var(--secondary-hue));
  --secondary-900: oklch(0.38 calc(var(--secondary-chroma) * 0.46) var(--secondary-hue));
  --secondary-950: oklch(0.26 calc(var(--secondary-chroma) * 0.32) var(--secondary-hue));

  /* === Accent === */
  --accent-50:  oklch(0.99 calc(var(--accent-chroma) * 0.16) var(--accent-hue));
  --accent-100: oklch(0.97 calc(var(--accent-chroma) * 0.35) var(--accent-hue));
  --accent-200: oklch(0.94 calc(var(--accent-chroma) * 0.67) var(--accent-hue));
  --accent-300: oklch(0.90 calc(var(--accent-chroma) * 0.89) var(--accent-hue));
  --accent-400: oklch(0.84 var(--accent-chroma) var(--accent-hue));
  --accent-500: oklch(0.77 var(--accent-chroma) var(--accent-hue));
  --accent-600: oklch(0.65 calc(var(--accent-chroma) * 0.87) var(--accent-hue));
  --accent-700: oklch(0.53 calc(var(--accent-chroma) * 0.71) var(--accent-hue));
  --accent-800: oklch(0.45 calc(var(--accent-chroma) * 0.58) var(--accent-hue));
  --accent-900: oklch(0.41 calc(var(--accent-chroma) * 0.46) var(--accent-hue));
  --accent-950: oklch(0.27 calc(var(--accent-chroma) * 0.32) var(--accent-hue));

  /* === Brand neutral (tinted gray, inherits primary hue for cohesion) === */
  --brand-neutral-50:  oklch(0.985 calc(var(--neutral-chroma) * 0.19) var(--neutral-hue));
  --brand-neutral-100: oklch(0.967 calc(var(--neutral-chroma) * 0.31) var(--neutral-hue));
  --brand-neutral-200: oklch(0.920 calc(var(--neutral-chroma) * 0.38) var(--neutral-hue));
  --brand-neutral-300: oklch(0.871 calc(var(--neutral-chroma) * 0.50) var(--neutral-hue));
  --brand-neutral-400: oklch(0.705 calc(var(--neutral-chroma) * 0.94) var(--neutral-hue));
  --brand-neutral-500: oklch(0.552 var(--neutral-chroma) var(--neutral-hue));
  --brand-neutral-600: oklch(0.442 var(--neutral-chroma) var(--neutral-hue));
  --brand-neutral-700: oklch(0.370 calc(var(--neutral-chroma) * 0.81) var(--neutral-hue));
  --brand-neutral-800: oklch(0.274 calc(var(--neutral-chroma) * 0.38) var(--neutral-hue));
  --brand-neutral-900: oklch(0.210 calc(var(--neutral-chroma) * 0.38) var(--neutral-hue));
  --brand-neutral-950: oklch(0.141 calc(var(--neutral-chroma) * 0.31) var(--neutral-hue));
}


/* ===== ../base.css ===== */
:root {
  /* Font families */
  --default-font-family:
    "InterVariable", "Inter", system-ui, -apple-system, sans-serif;
  --default-font-feature-settings: "cv02", "cv03", "cv04", "cv11";
  --default-font-variation-settings: normal;

  /* Border radius */
  --rounded-sm: 0.125rem;
  --rounded: 0.25rem;
  --rounded-md: 0.375rem;
  --rounded-lg: 0.5rem;
  --rounded-xl: 0.75rem;

  /* Shadows */
  --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg:
    0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl:
    0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);

  /* Opacity */
  --opacity-50: 0.5;

  /* Scale */
  --scale-95: scale(0.95);
  --scale-100: scale(1);

  /* Timing */
  --time-150: 150ms;
  --time-200: 200ms;
  --time-300: 300ms;
  --time-400: 400ms;

  /* Abstractions */
  --color-bg: white;
  --color-text: var(--brand-neutral-950);
  --color-text-reversed: white;
  --color-text-subtle: var(--brand-neutral-500);
  --color-link: var(--primary-700);
  --color-border-light: var(--brand-neutral-100);
  --color-border: var(--brand-neutral-200);
  --color-border-dark: var(--brand-neutral-400);
  --border: 1px;
  --color-selected: var(--primary-100);
  --color-selected-dark: var(--primary-200);
  --color-highlight: var(--accent-200);
  --color-pill-hover: var(--accent-50);
  --color-pill-active: var(--accent-100);

  /* Brand colors */
  --color-primary: var(--primary-600);
  --color-secondary: var(--brand-neutral-100);

  /* Atmospheric surfaces (derived from primary hue) */
  --color-sidebar-bg: oklch(0.97 min(0.01, var(--primary-chroma)) var(--primary-hue));
  --color-main-bg: oklch(0.99 min(0.005, var(--primary-chroma)) var(--primary-hue));
  --color-navbar-bg: rgba(255, 255, 255, 0.7);
  --color-navbar-border: var(--primary-600);
  --color-negative: var(--red-600);
  --color-positive: var(--green-600);

  /* Status colors (fixed semantic, not brand-dependent) */
  --color-info-bg: var(--blue-50);
  --color-info-border: var(--blue-200);
  --color-info-text: var(--blue-800);
  --color-info-subtext: var(--blue-700);
  --color-info-icon: var(--blue-500);

  --color-success-bg: var(--green-50);
  --color-success-border: var(--green-200);
  --color-success-text: var(--green-800);
  --color-success-subtext: var(--green-700);
  --color-success-icon: var(--green-500);

  --color-warning-bg: var(--yellow-50);
  --color-warning-border: var(--yellow-200);
  --color-warning-text: var(--yellow-800);
  --color-warning-subtext: var(--yellow-700);
  --color-warning-icon: var(--yellow-500);

  --color-error-bg: var(--red-50);
  --color-error-border: var(--red-200);
  --color-error-text: var(--red-800);
  --color-error-subtext: var(--red-700);
  --color-error-icon: var(--red-500);

  /* Interactive states */
  --color-focus-ring: var(--primary-600);

  /* SVG color values */
  --color-filter-text: invert(0);
  --color-filter-text-reversed: invert(1);
  --color-filter-negative: invert(22%) sepia(85%) saturate(1790%)
    hue-rotate(339deg) brightness(105%) contrast(108%);
  --color-filter-positive: invert(44%) sepia(89%) saturate(409%)
    hue-rotate(89deg) brightness(94%) contrast(97%);

  /* Size variables (preserved from sizes.css) */
  --size-0_5: 0.125rem;
  --size-1: 0.25rem;
  --size-1_5: 0.375rem;
  --size-2: 0.5rem;
  --size-2_5: 0.625rem;
  --size-3: 0.75rem;
  --size-4: 1rem;
  --size-4_5: 1.125rem;
  --size-5: 1.25rem;
  --size-6: 1.5rem;
  --size-7: 1.75rem;
  --size-8: 2rem;
  --size-9: 2.25rem;
  --size-10: 2.5rem;
  --size-11: 2.75rem;
  --size-12: 3rem;
  --size-16: 4rem;
  --size-full: 100%;

  /* Transform variables */
  --rotate-90: rotate(90deg);

  /* Max inline sizes (preserved from sizes.css) */
  --max-i-3xs: 16rem;
  --max-i-sm: 24rem;
  --max-i-lg: 32rem;

  /* Typography variables (preserved from typography.css) */
  --text-fluid-xs: clamp(0.75rem, 0.64rem + 0.57vw, 1rem);
  --text-fluid-sm: clamp(0.875rem, 0.761rem + 0.568vw, 1.125rem);
  --text-fluid-base: clamp(1rem, 0.89rem + 0.57vw, 1.25rem);
  --text-fluid-lg: clamp(1.125rem, 0.955rem + 0.852vw, 1.5rem);
  --text-fluid-xl: clamp(1.25rem, 0.966rem + 1.42vw, 1.875rem);
  --text-fluid-2xl: clamp(1.5rem, 1.16rem + 1.7vw, 2.25rem);
  --text-fluid-3xl: clamp(1.875rem, 1.364rem + 2.557vw, 3rem);
  --text-fluid-4xl: clamp(2.25rem, 1.57rem + 3.41vw, 3.75rem);

  /* Font weights (preserved from typography.css) */
  --font-normal: 400;
  --font-medium: 500;
  --font-semibold: 600;
  --font-bold: 700;

  /* Line heights (preserved from typography.css) */
  --leading-none: 1;
  --leading-tight: 1.25;
  --leading-5: 1.25rem;
  --leading-6: 1.5rem;

  /* Letter spacing (preserved from typography.css) */
  --tracking-tighter: -0.05em;

  /* Colorize browser widgets */
  color-scheme: light;
}

[data-color-scheme="dark"] {
  /* Abstractions */
  --color-bg: var(--brand-neutral-950);
  --color-text: white;
  --color-text-reversed: var(--brand-neutral-950);
  --color-text-subtle: var(--brand-neutral-400);
  --color-link: var(--primary-400);
  --color-border-light: var(--brand-neutral-900);
  --color-border: var(--brand-neutral-800);
  --color-border-dark: var(--brand-neutral-600);
  --color-selected: var(--primary-950);
  --color-selected-dark: var(--primary-800);
  --color-highlight: var(--accent-900);
  --color-pill-hover: var(--accent-950);
  --color-pill-active: var(--accent-900);

  /* Brand colors */
  --color-primary: var(--primary-400);
  --color-secondary: var(--brand-neutral-800);
  --color-negative: var(--red-900);
  --color-positive: var(--green-900);

  /* Atmospheric surfaces (dark mode) */
  --color-sidebar-bg: oklch(0.15 min(0.01, var(--primary-chroma)) var(--primary-hue));
  --color-main-bg: oklch(0.13 min(0.005, var(--primary-chroma)) var(--primary-hue));
  --color-navbar-bg: rgba(0, 0, 0, 0.7);
  --color-navbar-border: var(--primary-400);

  /* Status colors (dark mode) */
  --color-info-bg: var(--blue-950);
  --color-info-border: var(--blue-800);
  --color-info-text: var(--blue-200);
  --color-info-subtext: var(--blue-300);
  --color-info-icon: var(--blue-400);

  --color-success-bg: var(--green-950);
  --color-success-border: var(--green-800);
  --color-success-text: var(--green-200);
  --color-success-subtext: var(--green-300);
  --color-success-icon: var(--green-400);

  --color-warning-bg: var(--yellow-950);
  --color-warning-border: var(--yellow-800);
  --color-warning-text: var(--yellow-200);
  --color-warning-subtext: var(--yellow-300);
  --color-warning-icon: var(--yellow-400);

  --color-error-bg: var(--red-950);
  --color-error-border: var(--red-800);
  --color-error-text: var(--red-200);
  --color-error-subtext: var(--red-300);
  --color-error-icon: var(--red-400);

  /* Interactive states */
  --color-focus-ring: var(--primary-400);

  /* SVG color values */
  --color-filter-text: invert(1);
  --color-filter-text-reversed: invert(0);
  --color-filter-negative: invert(15%) sepia(65%) saturate(2067%)
    hue-rotate(339deg) brightness(102%) contrast(97%);
  --color-filter-positive: invert(23%) sepia(62%) saturate(554%)
    hue-rotate(91deg) brightness(93%) contrast(91%);

  /* Colorize browser widgets */
  color-scheme: dark;
}

* {
  border-color: var(--color-border);
  scrollbar-color: var(--brand-neutral-300) transparent;
  scrollbar-width: thin;
}

html {
  scroll-behavior: smooth;
}

body {
  background-color: var(--color-bg);
  color: var(--color-text);
  font-synthesis-weight: none;
  overscroll-behavior: none;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  position: relative;
}

.turbo-progress-bar {
  background-color: var(--color-primary);
}

::selection {
  background-color: var(--color-selected);
}

.cursor-pointer {
  cursor: pointer;
}

/* Global utility classes */
.box {
  box-shadow: var(--shadow-sm);
  border: var(--border) solid var(--color-border);
  background-color: var(--color-bg);
  border-radius: var(--rounded-md);
}

.entry-width {
  width: 32rem;
}

@media (max-width: 768px) {
  .entry-width {
    width: 100%;
  }
}

/* Highlight styling for search results */
mark {
  background-color: rgb(254 240 138); /* yellow-200 */
  padding: 0.125rem;
  margin-inline: -0.125rem;
  border-radius: 0.25rem;
}

/* Turbo frame pseudo-box */
turbo-frame {
  display: contents;
}

/* Prevent page scroll when modal dialog is open */
html:has(dialog:modal[open]) {
  overflow: hidden;
}

/* Reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after,
  *::backdrop {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ===== ../links_custom.css ===== */
@layer components.base {
  :where(.link-text) {
    color: var(--link-text-color, var(--color-primary));
    text-decoration: none;
    cursor: pointer;
    transition: color 0.2s;
    display: block;

    /* Hover and focus states */
    &:hover,
    &:focus {
      color: var(--color-primary);

      /* Only underline text spans, exclude badge spans - handles both direct and nested spans */
      span:not(.badge) {
        text-decoration: underline;
      }
    }

    /* Size variants */
    &--small {
      font-size: var(--text-sm);
    }

    &--normal {
      font-size: var(--text-base);
    }

    &--large {
      font-size: var(--text-lg);
    }

    /* Color variants */
    &.primary {
      color: var(--color-primary);
    }

    &.positive {
      color: var(--color-positive);
    }

    &.negative {
      color: var(--color-negative);
    }

    &.secondary {
      color: var(--color-secondary);
    }

    &.text {
      color: var(--color-text);
    }

    &.text-reversed {
      color: var(--color-text-reversed);
    }

    &.link {
      color: var(--color-link);
    }

    /* Menu link variants */
    &.menu-link {
      border-left: 2px solid transparent;
      transition:
        background 0.2s,
        border-color 0.2s;

      &.active {
        background: var(--brand-neutral-100);
        border-left: 2px solid var(--color-primary);
      }
    }
  }
}



/* Brand typography (Space Grotesk wordmark @font-face; font-grotesk token is in tailwind/application.css) */

/* ===== brand_fonts.css ===== */
/* Brand typography — Space Grotesk for the picgate wordmark (self-hosted, latin
 * subset). Consumed via the `font-grotesk` utility, whose @theme token lives in
 * tailwind/application.css. @font-face is document-global, so it applies to the
 * utility regardless of which bundle defines each. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/SpaceGrotesk-SemiBold-ab3ffa9f.woff2") format("woff2");
}

/* DejaVu Sans/Mono Bold (latin subset, self-hosted) — the exact faces the
 * image converter renders text watermarks with for the "sans"/"mono"
 * aliases. Loaded so the watermark editor's optimistic canvas draws with
 * identical metrics and the server swap doesn't change the text size. */
@font-face {
  font-family: "DejaVu Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/DejaVuSans-Bold-bf4840ae.woff2") format("woff2");
}
@font-face {
  font-family: "DejaVu Sans Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/DejaVuSansMono-Bold-7cfec2ce.woff2") format("woff2");
}


/* Landing typography (Sora + Source Serif Pro @font-face; read via --landing-font-* in pages/landing.css) */

/* ===== landing_fonts.css ===== */
/* Landing typography — Lora (the editorial serif accent line). Inter carries
 * everything else and is loaded app-wide from the gem (fonts.css → the Tailwind
 * build), so only the serif is declared here. Self-hosted latin subsets; the
 * landing reads Lora via the .landing-h-serif rule in pages/landing.css. Bare
 * relative url()s resolve through Propshaft's logical asset paths, same as
 * brand_fonts.css. */

@font-face {
  font-family: "Lora";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/Lora-Medium-4389e0c1.woff2") format("woff2");
}

@font-face {
  font-family: "Lora";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/Lora-SemiBold-afecaf98.woff2") format("woff2");
}

@font-face {
  font-family: "Lora";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/Lora-Italic-44f5dd95.woff2") format("woff2");
}


/* Brand-logo color exception for the grayscale "monochrome" theme */

/* ===== brand_logo.css ===== */
/*
 * Brand-logo exception for the grayscale "monochrome" theme.
 *
 * The monochrome preset zeroes every chroma anchor, so the whole OKLCH
 * system — including the theme-driven logo fills — renders gray. The brand
 * mark is the one element that must keep its original colors, so these rules
 * pin the logo's colored parts to the DEFAULT theme's resolved stops
 * (primary-700/400 and accent-600/400 with hue 262 / 84 and the original
 * chroma anchors baked in as literals).
 *
 * The active theme name is exposed on <html> as data-theme-active-theme-value
 * (written by the head bootstrap script before first paint and kept in sync
 * by the theme Stimulus controller via Stimulus value reflection).
 */

html[data-theme-active-theme-value="monochrome"] .brand-logo__frame {
  fill: oklch(0.46 0.239 262);
}

html[data-theme-active-theme-value="monochrome"][data-color-scheme="dark"] .brand-logo__frame {
  fill: oklch(0.67 0.182 262);
}

html[data-theme-active-theme-value="monochrome"] .brand-logo__cell {
  fill: oklch(0.65 0.165 84);
}

html[data-theme-active-theme-value="monochrome"][data-color-scheme="dark"] .brand-logo__cell {
  fill: oklch(0.84 0.19 84);
}

html[data-theme-active-theme-value="monochrome"] .brand-logo__gate {
  color: oklch(0.65 0.165 84);
}

html[data-theme-active-theme-value="monochrome"][data-color-scheme="dark"] .brand-logo__gate {
  color: oklch(0.84 0.19 84);
}


/* Shared pattern libraries (reused across many components) */
/* Gem generic keyframes first (fade-in, fade-out, slide-out-up, scale-up, …) so
   the gui_components component CSS imported below — clipboard.css et al. — has
   its referenced keyframes defined in the served bundle. The app registry is
   imported immediately after, at this same (unlayered) cascade position, so the
   app-tuned keyframes that share a name with a gem one (shake-x) still win. */

/* ===== animations.css ===== */
/****************************************************************
* Animation
* Shared @keyframes for animating elements with CSS animations.
* Reference a keyframe directly, e.g.:
* animation: fade-in 200ms var(--ease-out-soft) forwards;
*****************************************************************/

/* Shared motion easing tokens live in gui_components/easing_tokens.css (single
 * source of truth). Importing them here keeps the gem self-contained for
 * standalone consumers that load only animations.css and for the classic
 * Propshaft bundle; gem component CSS (toast.css, view_transitions.css,
 * sheet.css, …) references them via var(--ease-*).
 *
 * NOTE: This NESTED @import is dropped by Lightning CSS in the docs build
 * (Tailwind v4), so gui_components/all.css imports easing_tokens.css DIRECTLY
 * as well. The double import is idempotent (byte-identical :root block). Do not
 * remove this line — the Propshaft path depends on it. */

/* ===== gui_components/easing_tokens.css ===== */
/* ---- Motion easing tokens ----
   Single source of truth for the six shared easing curves used across every
   bundle. Imported by the gem's animations.css (standalone all.css) and by the
   host app's animations.css (classic Propshaft bundle). The guest bundle
   (app/assets/tailwind/public_gallery.css) keeps a byte-identical copy on
   purpose (Tailwind `source(none)` build) — that copy is guarded against drift
   by test/architecture/easing_tokens_drift_test.rb.

   CSS cannot run a real spring simulation (that is what motion.js in
   gui_components is for), but the overshoot beziers read as springy for
   one-shot, non-interruptible transitions. Prefer these tokens over
   `ease`/`ease-out` and over hand-typed cubic-bezier() literals. */
:root {
  --ease-crisp: cubic-bezier(0.2, 0.6, 0.2, 1);       /* house UI curve */
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);      /* iOS sheets/drawer/view-transitions */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);   /* overshoot, settles back */
  --ease-out-soft: cubic-bezier(0.16, 1, 0.3, 1);     /* fast in, long soft landing */
  --ease-toast: cubic-bezier(0.18, 1.25, 0.4, 1);     /* gentle overshoot for fly-ins */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);      /* Material standard */
}


@keyframes fade-in {
  to { opacity: 1 }
}

@keyframes fade-in-bloom {
    0% { opacity: 0; filter: brightness(1) blur(20px) }
   10% { opacity: 1; filter: brightness(2) blur(10px) }
  100% { opacity: 1; filter: brightness(1) blur(0) }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes fade-out-bloom {
  100% { opacity: 0; filter: brightness(1) blur(20px) }
   10% { opacity: 1; filter: brightness(2) blur(10px) }
    0% { opacity: 1; filter: brightness(1) blur(0) }
}
@keyframes scale-up {
  to { transform: scale(1.25) }
}

@keyframes scale-down {
  to { transform: scale(.75) }
}

@keyframes slide-out-up {
  to { transform: translateY(-100%) }
}

@keyframes slide-out-down {
  to { transform: translateY(100%) }
}

@keyframes slide-out-right {
  to { transform: translateX(100%) }
}

@keyframes slide-out-left {
  to { transform: translateX(-100%) }
}

@keyframes slide-in-up {
  from { transform: translateY(100%) }
}

@keyframes slide-in-down {
  from { transform: translateY(-100%) }
}

@keyframes slide-in-right {
  from { transform: translateX(-100%) }
}

@keyframes slide-in-left {
  from { transform: translateX(100%) }
}

@keyframes shake-x {
  0%, 100% { transform: translateX(0%) }
  20% { transform: translateX(-5%) }
  40% { transform: translateX(5%) }
  60% { transform: translateX(-5%) }
  80% { transform: translateX(5%) }
}

@keyframes shake-y {
  0%, 100% { transform: translateY(0%) }
  20% { transform: translateY(-5%) }
  40% { transform: translateY(5%) }
  60% { transform: translateY(-5%) }
  80% { transform: translateY(5%) }
}

@keyframes shake-z {
  0%, 100% { transform: rotate(0deg) }
  20% { transform: rotate(-2deg) }
  40% { transform: rotate(2deg) }
  60% { transform: rotate(-2deg) }
  80% { transform: rotate(2deg) }
}

@keyframes spin {
  to { transform: rotate(1turn) }
}

@keyframes ping {
  90%, 100% {
    transform: scale(2);
    opacity: 0;
  }
}

@keyframes blink {
  0%, 100% {
    opacity: 1
  }
  50% {
    opacity: .5
  }
}

@keyframes float {
  50% { transform: translateY(-25%) }
}

@keyframes bounce {
  25% { transform: translateY(-20%) }
  40% { transform: translateY(-3%) }
  0%, 60%, 100% { transform: translateY(0) }
}

@keyframes pulse {
  50% { transform: scale(.9,.9) }
}

/* Bottom sheet: slide up from off-screen, slide back down on close.
 * Works with the popover API (transition-behavior: allow-discrete).
 * Apply this class to any popover/overlay element inside a
 * bottom-sheet context (mobile only). */
@media (width < 40rem) {
  .animate-sheet-up {
    opacity: 0;
    transform: translateY(100%);
    transition-property: display, overlay, transform, opacity;
    transition-duration: var(--time-300);
    transition-timing-function: cubic-bezier(0.22, 1.2, 0.36, 1);
    transition-behavior: allow-discrete;

    &:popover-open {
      opacity: 1;
      transform: translateY(0);
    }

  }
}

@media (prefers-color-scheme: dark) {
  @keyframes fade-in-bloom {
      0% { opacity: 0; filter: brightness(1) blur(20px) }
     10% { opacity: 1; filter: brightness(0.5) blur(10px) }
    100% { opacity: 1; filter: brightness(1) blur(0) }
  }
}

@media (prefers-color-scheme: dark) {
  @keyframes fade-out-bloom {
    100% { opacity: 0; filter: brightness(1) blur(20px) }
     10% { opacity: 1; filter: brightness(0.5) blur(10px) }
      0% { opacity: 1; filter: brightness(1) blur(0) }
  }
}


/* ===== app_animations.css ===== */
/* ============================================================================
   Shared keyframes registry
   ----------------------------------------------------------------------------
   Single home for every keyframe animation used across the app. Keyframes are
   global by name within the served bundle, so any rule (in any stylesheet here)
   that says `animation: <name> ...` resolves against the definitions below —
   there is no need to keep the definition next to the rule that uses it.

   Grouped by the feature that introduced them. Near-duplicate families (the two
   "breathe", "arrow" and "pulse" pairs) are kept as distinct, separately-tuned
   variants on purpose: their values differ per context (full-page empty state vs.
   small card), so they are NOT merged.
   ============================================================================ */

/* ---- Motion easing tokens ----
   Shared easing curves for micro-interactions and state changes. The six
   tokens live in the gem as gui_components/easing_tokens.css (single source of
   truth) and are pulled in here so every rule imported by application.css —
   including the gui_components component CSS pulled in by name (toast,
   view_transitions, sheet, …) — resolves `var(--ease-*)` against them. The
   import stays where the :root block used to sit so the (unlayered) cascade
   order is unchanged. The self-contained public_gallery.css guest bundle keeps
   a byte-identical copy, guarded by test/architecture/easing_tokens_drift_test.rb.

   Near-miss families kept as literals on purpose (visually distinct, NOT
   folded into a token — candidates for a later feel-alignment pass):
   (0.2, 0.7, 0.2, 1) / (0.2, 0.7, 0.3, 1) / (0.2, 0.9, 0.3, 1),
   (0.22, 1, 0.36, 1) / (0.22, 1.2, 0.36, 1) / (0.22, 0.61, 0.36, 1). */
/* deduped @import: gui_components/easing_tokens.css */

/* ---- Image-display: selection bar reveal ---- */
@keyframes selectionSlideUp {
  from {
    opacity: 0;
    transform: translateY(60px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---- Images empty-state: floating three-polaroid stack + drop ----
 * The shared breathe / arrow / pulse keyframes moved to
 * `empty_polaroid_base.css` (as parametrized `ep-*`); only the
 * component-unique stack floats, drop and catch remain here. */
@keyframes images-empty-catch {
  0%   { transform: translateY(0) scale(1); }
  60%  { transform: translateY(-10px) scale(1.06); }
  100% { transform: translateY(0) scale(1.02); }
}

@keyframes images-empty-float-1 {
  0%, 100% { transform: rotate(-12deg) translate(-4.6rem, 0.6rem); }
  50%      { transform: rotate(-10deg) translate(-4.7rem, 0.1rem); }
}

@keyframes images-empty-float-2 {
  0%, 100% { transform: rotate(3deg)  translate(0.1rem, -0.4rem); }
  50%      { transform: rotate(4deg)  translate(0.1rem, -0.9rem); }
}

@keyframes images-empty-float-3 {
  0%, 100% { transform: rotate(11deg) translate(4.7rem, 0.5rem); }
  50%      { transform: rotate(13deg) translate(4.8rem, 0.0rem); }
}

@keyframes images-empty-drop {
  0%   { transform: translateY(-1.5rem) rotate(-6deg); opacity: 0; }
  18%  { opacity: 1; }
  55%  { transform: translateY(0.6rem) rotate(2deg);  opacity: 1; }
  70%  { transform: translateY(-0.1rem) rotate(0deg); opacity: 1; }
  85%  { transform: translateY(0.3rem) rotate(0deg);  opacity: 0.9; }
  100% { transform: translateY(0.3rem) rotate(0deg);  opacity: 0; }
}

/* ---- Page drop overlay: bobbing prompt ---- */
@keyframes page-drop-overlay-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* ---- Photo favorites: heart pop ---- */
@keyframes fv-pop {
  0%   { transform: scale(0.4); }
  60%  { transform: scale(1.2); }
  100% { transform: scale(1); }
}

/* ---- Photo-shooting comments: typing/loading pulse ---- */
@keyframes ps-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* ---- Settings sheet: panel slide-in (forward / back navigation) ---- */
@keyframes settingsSheetPanelEnterForward {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

@keyframes settingsSheetPanelEnterBack {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

/* ---- Share page: staggered row fade-in ---- */
@keyframes share-stagger-fade {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- Form errors: horizontal shake (wrong OTP code, invalid input) ---- */
@keyframes shake-x {
  10%, 90%      { transform: translateX(-1px); }
  20%, 80%      { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60%      { transform: translateX(4px); }
}

/* ---- Micro-interactions: springy scale bump on value change ----
   One-shot pop for elements whose content just changed (OTP digit typed,
   badge/chip count updated). Remove the class on animationend to re-arm. */
@keyframes bump-pop {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* ---- Grid tiles: staggered entrance (rise + fade, delay set per tile) ---- */
@keyframes tile-enter {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- Grid tiles: exit (fade + slight scale-down, mirrors tile-enter) ----
   Toggled from JS by the animated_remove stream action right before the node
   is removed, so a deleted tile/row/session fades out instead of teleporting.
   Exits use an ease-out curve (see #898), never scale to 0. `forwards` holds
   the end state if removal is a frame late; reduced motion collapses the
   duration via the global reset (gui_components base.css), so animationend
   still fires — practically instantly. */
@keyframes tile-exit {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.97); }
}

.tile-exit {
  animation: tile-exit 0.18s var(--ease-out-soft) forwards;
  pointer-events: none;
}

/* ---- Grid tiles: long-press pulse (touch entry into selection mode) ----
   One-shot scale nudge acknowledging a long-press, added by
   gallery_select_controller and removed on animationend to re-arm. */
@keyframes long-press-pulse {
  0%   { transform: scale(1); }
  40%  { transform: scale(0.94); }
  100% { transform: scale(1); }
}

/* ---- Shared one-shot classes ----
   Cross-feature helper classes toggled from JS (added, removed again on
   animationend). Kept here because they pair 1:1 with registry keyframes
   and are used by several features. */
.count-bump {
  display: inline-block;
  animation: bump-pop 0.35s var(--ease-spring);
}

/* Toggled on a gallery tile the moment a long-press engages selection mode. */
.gallery-item--long-press {
  animation: long-press-pulse 0.28s var(--ease-spring);
}

/* Springy hover lift for linked cards (gallery/shooting overview). Replaces
   the flat Tailwind hover:-translate-y-0.5 — same trigger, more life. */
.card-lift {
  transition: transform 0.5s var(--ease-spring), box-shadow 0.5s var(--ease-spring);
}

.card-lift:hover {
  box-shadow: 0 18px 32px -14px rgba(0, 0, 0, 0.35);
}

/* Der Lift (Bewegung) nur auf echten Zeigergeräten — auf Touch bliebe er sonst
   nach dem Tap kleben. Der Schatten-Hover darf feuern. */
@media (hover: hover) and (pointer: fine) {
  .card-lift:hover {
    transform: translateY(-6px);
  }

  /* Cover-Zoom (ARD-Stil): das Bild einer VT-Hero-Cover-Karte skaliert sanft
     innerhalb des Covers (card-lift und data-vt-hero sitzen auf demselben,
     via overflow-hidden clippenden Element — CoverCardComponent). Nur auf
     Zeigergeräten — auf Touch bliebe der Zoom nach dem Tap kleben. */
  .card-lift[data-vt-hero] img {
    transition: transform 0.35s var(--ease-out-soft);
  }

  .card-lift[data-vt-hero]:hover img {
    transform: scale(1.045);
  }

  /* Auswahlmodus gewinnt: die selektierte Karte bleibt geschrumpft
     (selection_overlay.css hat geringere Spezifität als die Hover-Regel). */
  .gallery-card--selected .card-lift[data-vt-hero]:hover img {
    transform: scale(0.9);
  }
}

/* Users who ask for reduced motion get state changes without theatrics:
   one-shot entrance/feedback animations are skipped entirely. */
@media (prefers-reduced-motion: reduce) {
  .count-bump {
    animation: none;
  }

  .gallery-item--long-press {
    animation: none;
  }

  .card-lift,
  .card-lift:hover {
    transform: none;
  }

  .card-lift[data-vt-hero]:hover img {
    transform: none;
  }

  /* Der Auswahl-Shrink ist Zustand, keine Bewegung — er bleibt auch unter
     reduced motion beim Hover erhalten. */
  .gallery-card--selected .card-lift[data-vt-hero]:hover img {
    transform: scale(0.9);
  }
}

/* ----------------------------------------------------------------------- *
   Layout-Grenze (ApplicationLayout ⇄ FullWidthLayout) — View Transition.

   vt_layout_controller.js setzt `html[data-vt-layout]` (zusätzlich zum
   data-vt-nav-Gate des Gems), wenn ein Drive-Visit die Sidebar-Grenze
   kreuzt, z. B. /settings → /settings/watermarks. Das Gem animiert dann
   bereits die vt-content-Snapshots (Fade-Through), aber:
     * das Root-Chrome (Sidebar, Tool-Topbar) swappt hart, weil die
       Root-Old/New-Bilder per Default auf animation: none stehen,
     * die vt-content-GROUP springt auf die neue Geometrie (volle Breite
       statt neben der Sidebar), weil ihre Group-Dauer auf 0s gezeroed ist.
   Beides wird hier nur für diese eine Navigation aufgeweicht: das Chrome
   blendet über, die Content-Box morpht in ihre neue Lage.
 * ----------------------------------------------------------------------- */
html[data-vt-layout]::view-transition-group(root) {
  animation-duration: 0.25s;
}
html[data-vt-layout]::view-transition-old(root) {
  animation: 0.2s var(--ease-drawer) both vt-crossfade-out;
}
html[data-vt-layout]::view-transition-new(root) {
  animation: 0.25s var(--ease-drawer) both vt-crossfade-in;
}
html[data-vt-layout]::view-transition-group(vt-content) {
  animation-duration: 0.25s;
  animation-timing-function: var(--ease-drawer);
}

@media (prefers-reduced-motion: reduce) {
  html[data-vt-layout]::view-transition-group(root),
  html[data-vt-layout]::view-transition-group(vt-content) {
    animation-duration: 0.01ms;
  }

  html[data-vt-layout]::view-transition-old(root),
  html[data-vt-layout]::view-transition-new(root) {
    animation-duration: 0.01ms;
  }
}


/* Component CSS (import only what your app needs) */

/* ===== accordion.css ===== */
/* Accordion Component */

/* Ensure initially open accordion content is visible before JS sets maxHeight */
[data-accordion-target="content"][data-state="open"]:not([hidden]) {
  max-height: none;
  opacity: 1;
}

/* Fluid animation variant (opt-in via animation_variant: :fluid).
   Pure-CSS collapse via grid-template-rows 0fr↔1fr — no JS height measurement. */
[data-accordion-target="item"] {
  --gc-acc-duration: 250ms;
  --gc-acc-ease: cubic-bezier(0.22, 1, 0.36, 1);
}

.gc-acc-fluid-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--gc-acc-duration) var(--gc-acc-ease);
}

[data-accordion-target="item"][data-state="open"] .gc-acc-fluid-panel {
  grid-template-rows: 1fr;
}

.gc-acc-fluid-inner {
  min-height: 0;
  overflow: hidden;
  opacity: 0;
  filter: blur(2px);
  transition:
    opacity var(--gc-acc-duration) var(--gc-acc-ease),
    filter var(--gc-acc-duration) var(--gc-acc-ease);
}

[data-accordion-target="item"][data-state="open"] .gc-acc-fluid-inner {
  opacity: 1;
  filter: blur(0);
}

.gc-acc-chevron {
  display: inline-flex;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--gc-acc-duration) var(--gc-acc-ease);
}

.gc-acc-chevron path {
  vector-effect: non-scaling-stroke;
}

[data-accordion-target="item"][data-state="open"] .gc-acc-chevron {
  transform: scaleY(-1);
}

@media (prefers-reduced-motion: reduce) {
  .gc-acc-fluid-panel,
  .gc-acc-fluid-inner,
  .gc-acc-chevron {
    transition: none !important;
  }
}


/* ===== avatar.css ===== */
.avatar {
  --radius: var(--rounded-full);
  --size: var(--avatar-size, var(--size-10));

  block-size: var(--size);
  border-radius: var(--radius);
  display: flex;
  flex-shrink: 0;
  inline-size: var(--size);
  overflow: hidden;

  &:is(.btn) {
    --btn-background: transparent;
    --btn-border-color: transparent;
    --btn-box-shadow: none;
    --btn-icon-color: none;
    --btn-inline-size: var(--size);
    --btn-padding: 0;
    --btn-radius: var(--radius);
  }

  &:is(.btn):hover {
    filter: var(--brightness-90);
  }

  img {
    align-content: center;
    aspect-ratio: var(--aspect-square);
    block-size: var(--size-full);
    inline-size: var(--size-full);
    object-fit: cover;
  }

  span[role=img] {
    align-items: center;
    background-color: var(--color-border-light);
    block-size: var(--size-full);
    border-radius: var(--radius);
    display: flex;
    font-size: calc(var(--size) * .4);
    inline-size: var(--size-full);
    justify-content: center;
    user-select: none;
  }
}

.avatar-group {
  align-items: center;
  display: flex;

  > :not(:last-child) {
    margin-inline-end: calc(var(--size-2) * -1);
  }
}


/* ===== badge.css ===== */
@layer components.base {
  :where(.badge-wrapper) {
    display: inline-flex;
    position: relative;

    > span:last-child {
      position: absolute;
      top: 0;
      right: 0;
      transform: translate(50%, -50%);
    }
  }

  [dir="rtl"] {
    :where(.badge-wrapper) {
      > span:last-child {
        right: auto;
        left: 0;
        transform: translate(-50%, -50%);
      }
    }
  }

  .badge-anim {
    --badge-slide-dur: 260ms;
    --badge-pop-dur: 500ms;
    --badge-pop-close-dur: 180ms;
    --badge-fade-dur: 400ms;
    --badge-fade-close-dur: 180ms;
    --badge-blur: 2px;
    --badge-offset-x: -8.2px;
    --badge-offset-y: 12.4px;
    --badge-slide-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --badge-pop-ease: cubic-bezier(0.34, 1.36, 0.64, 1);
    --badge-close-ease: cubic-bezier(0.4, 0, 0.2, 1);

    position: absolute;
    top: var(--badge-anim-top, -6px);
    right: var(--badge-anim-right, -8px);
    pointer-events: none;
    will-change: transform;
  }

  .badge-wrapper > .badge-anim {
    top: var(--badge-anim-top, -6px);
    right: var(--badge-anim-right, -8px);
    transform: none;
  }

  .badge-anim[data-open="true"] {
    animation: badge-anim-slide-in var(--badge-slide-dur) var(--badge-slide-ease);
  }

  @keyframes badge-anim-slide-in {
    from {
      transform: translate(var(--badge-offset-x), var(--badge-offset-y));
    }
    to {
      transform: translate(0, 0);
    }
  }

  .badge-anim-dot {
    display: block;
    transform-origin: center;
    transform: scale(1);
    opacity: 1;
    filter: blur(0);
    transition:
      transform var(--badge-pop-dur) var(--badge-pop-ease),
      opacity var(--badge-fade-dur) var(--badge-pop-ease),
      filter var(--badge-pop-dur) var(--badge-pop-ease);
    will-change: transform, opacity, filter;
  }

  .badge-anim[data-open="false"] .badge-anim-dot {
    transform: scale(0);
    opacity: 0;
    filter: blur(var(--badge-blur));
    transition:
      transform var(--badge-pop-close-dur) var(--badge-close-ease),
      opacity var(--badge-fade-close-dur) var(--badge-close-ease),
      filter var(--badge-pop-close-dur) var(--badge-close-ease);
  }

  [dir="rtl"] .badge-anim {
    right: auto;
    left: var(--badge-anim-right, -8px);
    --badge-offset-x: 8.2px;
  }

  [dir="rtl"] .badge-wrapper > .badge-anim {
    right: auto;
    left: var(--badge-anim-right, -8px);
    transform: none;
  }

  .animate-badge-pulse {
    animation: badge-pulse 2s infinite;
  }

  @keyframes badge-pulse {
    0% {
      scale: 1;
    }
    50% {
      scale: 1.1;
    }
    100% {
      scale: 1;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .animate-badge-pulse {
      animation: none;
    }

    .badge-anim,
    .badge-anim-dot {
      animation: none !important;
      transition: none !important;
    }
  }
}


/* ===== breadcrumb.css ===== */
/* Breadcrumb truncation is now interactive: the middle levels collapse into an
   accessible ellipsis dropdown driven by the `max_items:` prop and the shared
   dropdown/menu Stimulus controllers (see BreadcrumbComponent). The former
   CSS-only `.breadcrumb--truncated` variant (display:none + ::after "…") hid the
   levels from keyboard and screen-reader users and was removed. */


/* ===== button.css ===== */
/* Button component - minimal CSS for features not available in Tailwind */

/* Base .btn class for legacy components (sidebar, menus) that use CSS custom properties */
.btn {
  align-items: var(--btn-align-items, center);
  background-color: var(--btn-background, transparent);
  block-size: var(--btn-block-size, auto);
  border-color: var(--btn-border-color, transparent);
  border-radius: var(--btn-radius, var(--radius-2));
  border-style: solid;
  border-width: var(--btn-border-width, var(--border));
  box-shadow: var(--btn-box-shadow, none);
  color: var(--btn-color, inherit);
  column-gap: var(--btn-gap, var(--size-2));
  cursor: pointer;
  display: inline-flex;
  font-size: var(--btn-font-size, var(--text-sm));
  font-weight: var(--btn-font-weight, var(--font-medium));
  inline-size: var(--btn-inline-size, auto);
  justify-content: var(--btn-justify-content, center);
  padding: var(--btn-padding, var(--size-2) var(--size-3));
  text-align: var(--btn-text-align, center);
  transition: background-color var(--time-100), box-shadow var(--time-100);
  user-select: none;
  white-space: nowrap;

  &:hover {
    background-color: var(--btn-hover-color, var(--btn-background));
  }

  &:focus-visible {
    outline: var(--btn-outline-size, 2px) solid var(--btn-outline-color, currentColor);
    outline-offset: 2px;
  }

  &:disabled {
    cursor: not-allowed;
    opacity: 0.5;
  }
}

/* Avatar button variant inherits from avatar component styles */


/* ===== clipboard.css ===== */
/* Clipboard functionality styles */

.clipboard {
  max-height: 350px;
  display: flex;
  overflow: auto;
  overflow-x: auto;
  border-radius: 0.375rem;
  border-width: 1px;
  background-color: var(--brand-neutral-950) !important;
}

.clipboard::after {
  content: none;
}

.clipboard pre {
  padding: 1rem;
}

.btn--copied > .icon {
  animation: fade-out 200ms var(--ease-out-soft) forwards, slide-out-up 200ms var(--ease-out-soft) forwards;
}

/* Clipboard button positioning within code blocks */
.codeblock__code {
  position: relative;
}

.codeblock__clipboard {
  position: absolute;
  top: 1rem;
  right: 1rem;
  background: rgba(0, 0, 0, 0.7) !important;
  border: none !important;
  padding: 0.5rem !important;
  border-radius: 0.25rem !important;
  opacity: 0;
  transition: opacity var(--time-200);
  color: white;
  z-index: 10;
  cursor: pointer;
}

.codeblock__code:hover .codeblock__clipboard {
  opacity: 0.8;
}

.codeblock__clipboard:hover {
  opacity: 1 !important;
  background: rgba(0, 0, 0, 0.9) !important;
}

.codeblock__clipboard .icon {
  color: white;
}

/* Clipboard success state styling */
.codeblock__clipboard-success {
  display: none;
  align-items: center;
  gap: 0.25rem;
}

.codeblock__clipboard-text {
  font-size: 0.75rem;
  font-weight: 500;
}

.btn--copied .codeblock__clipboard-default {
  display: none;
}

.btn--copied .codeblock__clipboard-success {
  display: flex;
}

.btn--copied {
  background: rgba(34, 197, 94, 0.9) !important;
  color: white;
}

.btn--copied:hover {
  background: rgba(34, 197, 94, 1) !important;
}

/* ================================================
   ClipboardComponent Tooltip Styles
   ================================================ */

@layer components.base {
  .clipboard-tooltip {
    position: absolute;
    z-index: 9999;
    padding: 0.375rem 0.625rem;
    border-radius: 0.375rem;
    font-size: 0.875rem;
    font-weight: 500;
    white-space: nowrap;
    pointer-events: none;
    background-color: var(--brand-neutral-800);
    color: white;
    border: 1px solid rgba(255, 255, 255, 0.1);
    box-shadow: var(--shadow-md);
    opacity: 0;
    transform: scale(0.95);
    transition: opacity 150ms ease-out, transform 150ms ease-out;
  }

  .clipboard-tooltip--visible {
    opacity: 1;
    transform: scale(1);
  }

  .clipboard-tooltip--success {
    background-color: var(--green-600);
    border-color: rgba(255, 255, 255, 0.15);
  }

  .clipboard-tooltip--error {
    background-color: var(--red-600);
    border-color: rgba(255, 255, 255, 0.15);
  }

  .clipboard-tooltip__arrow {
    position: absolute;
    width: 0.5rem;
    height: 0.5rem;
    background-color: inherit;
    transform: rotate(45deg);
  }

  .clipboard-tooltip--success .clipboard-tooltip__arrow {
    background-color: var(--green-600);
  }

  .clipboard-tooltip--error .clipboard-tooltip__arrow {
    background-color: var(--red-600);
  }
}


/* ===== calendar.css ===== */
.calendar {
    background-color: var(--color-bg);
    padding: var(--size-2);
    text-align: center;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-2);
}

.calendar__header {
    display: flex;
    align-items: center;
    color: var(--color-text);
}

.calendar__nav-button {
    margin: calc(var(--size-1_5) * -1);
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    padding: var(--size-1_5);
    color: var(--color-text-subtle);
}

.calendar__nav-button:hover {
    color: var(--color-text);
}

/* Frozen calendar: prev/next are genuinely disabled and read as such —
   greyed out, not-allowed cursor, and no hover brightening. */
.calendar__nav-button--frozen {
    opacity: 0.5;
    cursor: not-allowed;
    color: var(--color-text-subtle);
}

.calendar__nav-button--frozen:hover {
    color: var(--color-text-subtle);
}

/* Chevron ⇄ no_symbol icon swap on hover of a frozen nav button. Both icons
   are size-4 so swapping them never shifts the layout. */
.calendar__nav-icon--forbidden {
    display: none;
}

.calendar__nav-button--frozen:hover .calendar__nav-icon--default {
    display: none;
}

.calendar__nav-button--frozen:hover .calendar__nav-icon--forbidden {
    display: block;
}

.calendar__month-year {
    flex: auto;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
}

.calendar__month-year--clickable {
    cursor: pointer;
    padding: var(--size-2);
    border-radius: var(--radius-2);
    text-decoration: none;
    color: inherit;
    transition: background-color 0.2s ease;
}

.calendar__month-year--clickable:hover {
    background-color: var(--color-secondary);
}

.calendar__weekdays {
    margin-top: var(--size-6);
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    font-size: var(--text-xs);
    line-height: var(--leading-6);
    color: var(--color-text-subtle);
}

.calendar__weekdays--with-weeks {
    grid-template-columns: 2.5rem repeat(7, 1fr);
}

.calendar__week-header {
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: var(--font-semibold);
    color: var(--color-text-subtle);
}

.calendar__grid {
    isolation: isolate;
    margin-top: var(--size-2);
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    font-size: var(--text-sm);
}

.calendar__grid--with-weeks {
    grid-template-columns: 2.5rem repeat(7, 1fr);
}

.calendar__week-number {
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--color-secondary);
    color: var(--color-text-subtle);
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    padding-top: var(--size-1_5);
    padding-bottom: var(--size-1_5);
}

.calendar__day {
    display: block;
    width: 100%;
    padding-top: var(--size-1_5);
    padding-bottom: var(--size-1_5);
    border: none;
}

.calendar__day:hover {
    background-color: var(--color-secondary);
}

.calendar__day:focus {
    z-index: 10;
}

.calendar__day--current-month {
    background-color: var(--color-bg);
    color: var(--color-text);
}

.calendar__day--other-month {
    background-color: var(--color-bg);
    color: var(--color-text-subtle);
}

.calendar__day--selected {
    background-color: var(--color-selected);
    color: var(--color-primary);
    font-weight: var(--font-semibold);
}

.calendar__day-time {
    /* Default styling for day numbers */
}

.calendar__day-time--today {
    margin-left: auto;
    margin-right: auto;
    display: flex;
    height: var(--size-7);
    width: var(--size-7);
    align-items: center;
    justify-content: center;
    border-radius: var(--rounded-full);
    background-color: var(--blue-100);
    font-weight: var(--font-semibold);
    color: var(--color-text);
}

.calendar__footer {
    margin-left: auto;
    margin-right: auto;
}

.calendar__today-link {
    font-size: var(--text-sm);
    color: var(--color-primary);
}

.calendar__today-link:hover {
    color: var(--color-link);
}

/* Frozen calendar: the "Today" link is disabled and greyed out. */
.calendar__today-link--frozen {
    color: var(--color-text-subtle);
    opacity: 0.5;
    cursor: not-allowed;
}

.calendar__today-link--frozen:hover {
    color: var(--color-text-subtle);
}

/* Year view styles */
.calendar__months-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--size-2);
    margin-top: var(--size-4);
    margin-bottom: var(--size-4);
}

.calendar__month {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--size-2);
    text-decoration: none;
    border: none;
    border-radius: var(--radius-2);
    background-color: transparent;
    color: var(--color-text);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.calendar__month:hover {
    background-color: var(--color-secondary);
}

.calendar__month--current {
    background-color: var(--indigo-50);
    color: var(--indigo-700);
    font-weight: var(--font-semibold);
}

.calendar__month--selected {
    background-color: var(--color-primary);
    color: white;
    font-weight: var(--font-semibold);
}

.calendar__month--selected:hover {
    background-color: var(--color-primary);
}

/* Decade view styles */
.calendar__years-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--size-2);
    margin-top: var(--size-4);
    margin-bottom: var(--size-4);
}

.calendar__year {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--size-2);
    text-decoration: none;
    border: none;
    border-radius: var(--radius-2);
    background-color: transparent;
    color: var(--color-text);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.calendar__year:hover {
    background-color: var(--color-secondary);
}

.calendar__year--selected {
    background-color: var(--color-primary);
    color: white;
    font-weight: var(--font-semibold);
}

.calendar__year--selected:hover {
    background-color: var(--color-primary);
}

.calendar__year--out-of-range {
    color: var(--color-text-subtle);
    opacity: 0.6;
}

/* ===== datepicker.css ===== */
/* Datepicker Component */

/* Default position before floating-ui sets inline left/top.
   Without this, a brief flash at left:0/top:0 of the offset parent
   could appear, and any JS hiccup would leave the popup off-anchor. */
.datepicker-calendar {
  top: 100%;
  left: 0;
  margin-top: 4px;
}

/* Override calendar selected day color within datepicker context */
.datepicker-calendar .calendar__day--selected {
  background-color: var(--color-secondary);
}

/* Backdrop used in mobile sheet mode — injected by the controller */
.datepicker-backdrop {
  display: none;
}

/* Mobile: bottom sheet layout, analogous to the floating action bar */
@media (max-width: 639px) {
  .datepicker-calendar.datepicker-calendar--sheet {
    position: fixed !important;
    top: auto !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    max-height: 90vh;
    margin: 0 !important;
    z-index: 1001 !important;
    background-color: var(--color-bg);
    border-top-left-radius: var(--radius-3, 1rem);
    border-top-right-radius: var(--radius-3, 1rem);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    box-shadow:
      0 -8px 30px oklch(0 0 0 / 0.15),
      0 -2px 8px oklch(0 0 0 / 0.08);
    padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    will-change: transform, opacity;
  }

  /* Drag handle indicator */
  .datepicker-calendar.datepicker-calendar--sheet::before {
    content: "";
    display: block;
    width: 36px;
    height: 4px;
    margin: 0.625rem auto 0.25rem;
    border-radius: 9999px;
    background-color: var(--color-border);
  }

  .datepicker-calendar.datepicker-calendar--sheet .calendar {
    border: none;
    padding: var(--size-3, 0.75rem);
    border-radius: 0;
  }

  /* Bigger tap targets for day, month, year cells */
  .datepicker-calendar.datepicker-calendar--sheet .calendar__day {
    min-height: 44px;
    font-size: 1rem;
  }

  .datepicker-calendar.datepicker-calendar--sheet .calendar__month,
  .datepicker-calendar.datepicker-calendar--sheet .calendar__year {
    min-height: 44px;
    font-size: 1rem;
  }

  .datepicker-calendar.datepicker-calendar--sheet .calendar__nav-button {
    padding: var(--size-2, 0.5rem);
  }

  .datepicker-calendar.datepicker-calendar--sheet .calendar__today-link {
    min-height: 40px;
    padding-inline: 1rem;
  }

  /* Backdrop */
  .datepicker-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background-color: oklch(0 0 0 / 0.45);
    z-index: 1000;
    -webkit-tap-highlight-color: transparent;
    will-change: opacity;
  }
}


/* ===== timepicker.css ===== */
/* Timepicker Component */

/* Popover card. Default position before floating-ui sets inline left/top —
   without this a brief flash at left:0/top:0 of the offset parent could
   appear, and any JS hiccup would leave the popup off-anchor. */
.timepicker-popover {
  top: 100%;
  left: 0;
  margin-top: 4px;
  min-width: 11rem;
  padding: var(--size-2);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2, 0.5rem);
  box-shadow:
    0 10px 15px -3px oklch(0 0 0 / 0.1),
    0 4px 6px -4px oklch(0 0 0 / 0.1);
}

.timepicker__columns {
  display: flex;
  gap: var(--size-2);
  align-items: flex-start;
}

.timepicker__column-wrap {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.timepicker__column-label {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  text-align: center;
  padding-block: var(--size-1, 0.25rem);
}

.timepicker__column {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 12rem;
  overflow-y: auto;
  scroll-snap-type: y proximity;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

.timepicker__column li {
  scroll-snap-align: center;
}

.timepicker__option {
  display: block;
  width: 100%;
  box-sizing: border-box;
  border: 0;
  background: transparent;
  margin: 0;
  padding: var(--size-1_5, 0.375rem) var(--size-2, 0.5rem);
  border-radius: var(--radius-1, 0.375rem);
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--color-text);
  cursor: pointer;
  line-height: 1.5;
}

.timepicker__option:hover {
  background-color: var(--color-secondary);
}

.timepicker__option:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: -2px;
}

.timepicker__option[aria-selected="true"] {
  background-color: var(--color-selected);
  color: var(--color-primary);
  font-weight: var(--font-semibold, 600);
}

.timepicker__option[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

.timepicker__option[aria-disabled="true"]:hover {
  background-color: transparent;
}

.timepicker__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--size-2);
  margin-top: var(--size-2);
  padding-top: var(--size-2);
  border-top: 1px solid var(--color-border);
}

.timepicker__footer-actions {
  display: flex;
  align-items: center;
  gap: var(--size-3);
}

.timepicker__link {
  border: 0;
  background: transparent;
  padding: var(--size-1, 0.25rem) var(--size-1, 0.25rem);
  font-size: var(--text-sm);
  color: var(--color-primary);
  cursor: pointer;
}

.timepicker__link:hover {
  color: var(--color-link);
}

.timepicker__link--subtle {
  color: var(--color-text-subtle);
}

.timepicker__link--primary {
  font-weight: var(--font-semibold, 600);
}

/* Backdrop used in mobile sheet mode — injected by the controller */
.timepicker-backdrop {
  display: none;
}

/* Native <input type="time"> in mobile_native mode — blend the OS field into
   the :card look (transparent field, themed picker indicator, full width). */
input[data-timepicker-target="native"] {
  width: 100%;
}

input[data-timepicker-target="native"]::-webkit-datetime-edit {
  color: var(--color-text);
}

input[data-timepicker-target="native"]::-webkit-calendar-picker-indicator {
  color: var(--color-primary);
  cursor: pointer;
}

/* Mobile: bottom sheet layout, mirrors the datepicker calendar sheet */
@media (max-width: 639px) {
  .timepicker-popover.timepicker-popover--sheet {
    position: fixed !important;
    top: auto !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    max-height: 90vh;
    margin: 0 !important;
    z-index: 1001 !important;
    background-color: var(--color-bg);
    border: none;
    border-top-left-radius: var(--radius-3, 1rem);
    border-top-right-radius: var(--radius-3, 1rem);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    box-shadow:
      0 -8px 30px oklch(0 0 0 / 0.15),
      0 -2px 8px oklch(0 0 0 / 0.08);
    padding: 0 var(--size-4, 1rem) max(0.5rem, env(safe-area-inset-bottom));
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    will-change: transform, opacity;
  }

  /* Drag handle indicator */
  .timepicker-popover.timepicker-popover--sheet::before {
    content: "";
    display: block;
    width: 36px;
    height: 4px;
    margin: 0.625rem auto 0.5rem;
    border-radius: 9999px;
    background-color: var(--color-border);
  }

  /* Bigger columns + tap targets on the sheet */
  .timepicker-popover--sheet .timepicker__column {
    max-height: 40vh;
  }

  .timepicker-popover--sheet .timepicker__option {
    min-height: 44px;
    font-size: 1rem;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .timepicker-popover--sheet .timepicker__link {
    min-height: 40px;
    padding-inline: var(--size-3, 0.75rem);
  }

  /* Backdrop */
  .timepicker-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background-color: oklch(0 0 0 / 0.45);
    z-index: 1000;
    -webkit-tap-highlight-color: transparent;
    will-change: opacity;
  }
}


/* ===== font_picker.css ===== */
.font-picker__popover[hidden] {
  display: none;
}

.font-picker__empty[hidden] {
  display: none;
}

.font-picker__list {
  scrollbar-width: thin;
}

.font-picker__option--active {
  background-color: var(--color-shade);
}

.font-picker__option[aria-selected="true"] {
  font-weight: 500;
}


/* ===== details_accordion.css ===== */
/* Details Accordion - Zero Dependency Component */

/* Smooth content reveal animation */
details[open] > div:last-child {
  animation: details-show 0.2s ease-out;
}

@keyframes details-show {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  details[open] > div:last-child {
    animation: none;
  }

  .group-open\:rotate-180 {
    transition: none;
  }
}

/* =================================================================
   :card variant — Karten-Optik (weiße Karte, Radius, Brand-Border +
   Schatten bei [open], drehender Chevron, weiche Höhen-Animation).
   Portiert aus der Landing-FAQ (.bs-faq-list), auf gem-weite Tokens
   abgestützt. Exklusives Öffnen läuft nativ über <details name="…">.
   ================================================================= */
.gc-details-card details {
  background: var(--color-surface, var(--color-white));
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  margin-bottom: 12px;
  overflow: hidden;
  transition: border-color .25s, box-shadow .25s;
}
.gc-details-card details:last-child { margin-bottom: 0; }
.gc-details-card details[open] {
  border-color: var(--color-primary);
  box-shadow: var(--shadow-2xl);
}

.gc-details-card summary {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  cursor: pointer; list-style: none; padding: 20px 24px;
  font-weight: 700; color: var(--color-text);
}
.gc-details-card summary::-webkit-details-marker { display: none; }
.gc-details-card summary .chev {
  flex: none; color: var(--color-text-muted);
  transition: transform .35s cubic-bezier(.2,.7,.3,1), color .25s;
}
.gc-details-card details[open] summary .chev {
  transform: rotate(180deg); color: var(--color-primary);
}

/* Weiches Auf-/Zuklappen: Inhalt bleibt gerendert (::details-content),
   die Höhe steuert der Grid-Trick 0fr → 1fr. */
.gc-details-card details::details-content { content-visibility: visible; }
.gc-details-card details[open] > .a-wrap { animation: none; }
/* Getrennte Zu-/Aufklapp-Transition: gleiche Begruendung wie in landing_faq.css
   — bei gleicher Dauer wurde der Text waehrend der Hoehenaenderung mitten durch
   die Zeile beschnitten. */
.gc-details-card .a-wrap {
  display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows .4s cubic-bezier(.2,.7,.3,1), opacity .12s ease;
}
.gc-details-card details[open] .a-wrap {
  grid-template-rows: 1fr; opacity: 1;
  transition: grid-template-rows .4s cubic-bezier(.2,.7,.3,1), opacity .25s ease .12s;
}
.gc-details-card .a { min-height: 0; overflow: hidden; }
.gc-details-card .a p {
  padding: 0 24px 22px; font-size: 16px; color: var(--color-text-subtle); max-width: 62ch;
}

@media (max-width: 480px) {
  .gc-details-card summary { padding: 16px 18px; font-size: 16.5px; }
  .gc-details-card .a p { padding: 0 18px 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .gc-details-card .a-wrap,
  .gc-details-card summary .chev { transition: none; }
}


/* ===== dialog.css ===== */
/* Dialog component styles using Boring Rails approach for animations */
.dialog {
  background-color: var(--color-bg);
  border-radius: var(--rounded-lg);
  border-width: var(--border);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
  inline-size: var(--size-full);
  margin: auto;
  max-inline-size: var(--dialog-size, var(--max-i-lg));

  opacity: 1;
  scale: 1;

  transition:
    opacity 0.2s ease-out,
    scale 0.2s ease-out,
    overlay 0.2s ease-out allow-discrete,
    display 0.2s ease-out allow-discrete;

  @starting-style {
    opacity: 0;
    scale: 0.95;
  }

  @media (width < 40rem) {
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    border-start-start-radius: var(--rounded-xl, 1rem);
    border-start-end-radius: var(--rounded-xl, 1rem);
    margin-block-start: auto;
    margin-block-end: 0;
    max-inline-size: 100%;

    transition:
      opacity 0.2s ease-out,
      translate 0.2s ease-out,
      overlay 0.2s ease-out allow-discrete,
      display 0.2s ease-out allow-discrete;

    @starting-style {
      opacity: 0;
      translate: 0 100%;
      scale: 1;
    }
  }
}

.dialog:not([open]) {
  opacity: 0;
  scale: 0.95;
}

@media (width < 40rem) {
  .dialog:not([open]) {
    opacity: 0;
    translate: 0 100%;
    scale: 1;
  }
}

.dialog::backdrop {
  background-color: rgb(0 0 0 / 0.5);
  transition:
    background-color 0.2s ease-out,
    overlay 0.2s ease-out allow-discrete,
    display 0.2s ease-out allow-discrete;

  @starting-style {
    background-color: rgb(0 0 0 / 0);
  }
}

.dialog:not([open])::backdrop {
  background-color: rgb(0 0 0 / 0);
}

.dialog__content {
  padding: var(--size-6);
}

.dialog--scrollable {
  display: grid;
  grid-template-rows: auto 1fr auto;
  max-block-size: calc(100dvh - var(--size-8));
}

.dialog--scrollable .dialog__content {
  min-block-size: 0;
  overflow-y: auto;
  padding-block-start: var(--size-3);
}

.dialog__header {
  padding: var(--size-6) var(--size-6) 0;
}

.dialog__footer {
  display: flex;
  gap: var(--size-2);
  justify-content: flex-end;
  padding: var(--size-4) var(--size-6);
}

.dialog__footer--sticky {
  background-color: var(--color-bg);
  border-block-start: var(--border) solid var(--color-border, currentColor);
}

/* The close button sits inside a method="dialog" form (native no-JS close
   fallback); display:contents keeps the form out of the dialog's layout. */
.dialog__close-form {
  display: contents;
}

.dialog__close {
  inset-block-start: var(--size-3);
  inset-inline-end: var(--size-3);
  position: absolute;
  /* Own stacking context + compositing layer: without these, iOS Safari
     hit-tests taps on the button into an overlapping scroll container —
     the X is visible but not tappable on real devices. */
  z-index: 20;
  transform: translateZ(0);
}

/* showModal() autofocuses the close button, which would draw the global
   .btn:focus-visible outline on every open (not just keyboard nav). Replace
   the outline with a subtle background fill so keyboard focus stays visible. */
.dialog__close:focus-visible {
  outline: none;
  background: var(--color-border-light);
}

/* `cover-picker-dialog` is kept as a legacy alias of the neutral
   `dialog--fullscreen-mobile` so existing markup and any unlayered app CSS
   referencing the old class stay pixel-identical during the migration. */
@media (width < 40rem) {
  .dialog--fullscreen-mobile,
  .cover-picker-dialog {
    block-size: 100dvh;
    border-radius: 0;
    margin: 0;
    max-block-size: 100dvh;
  }
}



/* ===== docs_table.css ===== */
/* Documentation table styles */

/* Inline code text styling for props tables */
.code-inline-text {
  font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
  font-size: 0.875rem;
  background-color: rgb(243 244 246);
  padding: 0.25rem 0.5rem;
  border-radius: 0.25rem;
  color: rgb(55 65 81);
}

[data-color-scheme="dark"] .code-inline-text {
  background-color: rgb(39 39 42);
  color: rgb(212 212 216);
}

/* Small scrollbar for horizontal overflow */
.small-scrollbar {
  scrollbar-width: thin;
}

.small-scrollbar::-webkit-scrollbar {
  height: 6px;
}

.small-scrollbar::-webkit-scrollbar-track {
  background: transparent;
}

.small-scrollbar::-webkit-scrollbar-thumb {
  background-color: rgb(156 163 175);
  border-radius: 3px;
}

[data-color-scheme="dark"] .small-scrollbar::-webkit-scrollbar-thumb {
  background-color: rgb(82 82 91);
}


/* ===== drawer.css ===== */
/* Drawer styles */
dialog.drawer {
  margin: 0;
  max-height: 100vh;
  height: auto;
  margin-inline: auto;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 0;
  border-radius: 1rem 1rem 0 0;
  background: var(--color-bg);
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.5);
  overflow: hidden;
}

.dark dialog.drawer {
  background: rgb(38 38 38);
}

dialog.drawer::backdrop {
  background: none;
}

/* Prevent body scroll when drawer is open */
body:has(dialog.drawer[open]) {
  overflow: hidden;
}

/* Drawer animations */
dialog.drawer[open] {
  animation: fadeIn 200ms forwards;
}

dialog.drawer[closing] {
  pointer-events: none;
  animation: fadeOut 200ms forwards;
}

/* Hide dialogs by default to prevent flash of open state */
dialog:not([open]) {
  display: none !important;
}

/* Scrollbar compensation for fixed elements */
:root {
  --scrollbar-compensation: 0px;
}

body.drawer-open {
  padding-right: var(--scrollbar-compensation);
}

body.drawer-open .fixed {
  padding-right: var(--scrollbar-compensation);
}

body.drawer-open [data-drawer-target="dialog"] {
  padding-right: 0 !important;
}

/* Keyframes */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}


/* ===== event_sidebar.css ===== */
/* Event Sidebar Styles */

.event-details-sheet {
  width: 400px;
  max-width: 90vw;
}

.event-sidebar {
  padding: var(--size-6);
  background: var(--color-bg);
  height: 100%;
  overflow-y: auto;
}

.event-sidebar__header {
  margin-bottom: var(--size-6);
  padding-bottom: var(--size-4);
  border-bottom: 1px solid var(--color-border);
}

.event-sidebar__title-section {
  display: flex;
  align-items: center;
  gap: var(--size-3);
}

.event-sidebar__color-indicator {
  width: 4px;
  height: 40px;
  border-radius: var(--radius-1);
  flex-shrink: 0;
}

.event-sidebar__title {
  font-size: var(--text-xl);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  margin: 0;
  line-height: 1.2;
  word-wrap: break-word;
}

.event-sidebar__content {
  display: flex;
  flex-direction: column;
  gap: var(--size-6);
}

.event-sidebar__section {
  display: flex;
  flex-direction: column;
  gap: var(--size-4);
}

.event-sidebar__field {
  display: flex;
  align-items: flex-start;
  gap: var(--size-3);
  padding: var(--size-3);
  border-radius: var(--radius-2);
  background: var(--color-secondary);
  transition: background-color 0.15s ease;
}

.event-sidebar__field:hover {
  background: var(--color-hover);
}

.event-sidebar__field-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 2px; /* Slight alignment adjustment */
}

.event-sidebar__field-content {
  flex: 1;
  min-width: 0; /* Allows text to wrap properly */
}

.event-sidebar__field-label {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  color: var(--color-text-subtle);
  margin-bottom: var(--size-1);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.event-sidebar__field-value {
  font-size: var(--text-base);
  font-weight: var(--font-medium);
  color: var(--color-text);
  line-height: 1.4;
  word-wrap: break-word;
}

.event-sidebar__field-meta {
  font-size: var(--text-sm);
  color: var(--color-text-subtle);
  margin-top: var(--size-1);
  font-style: italic;
}

/* Color preview section */
.event-sidebar__color-preview {
  display: flex;
  align-items: center;
  gap: var(--size-2);
}

.event-sidebar__color-swatch {
  width: 20px;
  height: 20px;
  border-radius: var(--radius-1);
  border: 1px solid rgba(0, 0, 0, 0.1);
  flex-shrink: 0;
}

.event-sidebar__color-name {
  font-size: var(--text-base);
  font-weight: var(--font-medium);
  color: var(--color-text);
}

/* Responsive adjustments */
@media (max-width: 768px) {
  .event-sidebar {
    padding: var(--size-4);
  }
  
  .event-sidebar__title {
    font-size: var(--text-lg);
  }
  
  .event-sidebar__field {
    padding: var(--size-2);
  }
  
  .event-sidebar__field-value {
    font-size: var(--text-sm);
  }
}

/* Animation for sidebar opening */
.event-sidebar {
  animation: slideIn 0.3s ease-out;
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateX(20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* ===== fab.css ===== */
/* FabComponent — Speed-Dial menu.
   The blockless FAB is pure Tailwind (no styles here). These rules cover only
   the speed-dial variant: a vertical stack of mini-FABs + label pills that
   fans out above the trigger. Motion tokens come from easing_tokens.css.

   Stagger: each item carries --i (open, bottom → top) and --j (close, top →
   bottom) so opening springs up from the FAB and closing collapses briskly.
   The asymmetry (spring open / crisp close) mirrors the dropdown and sheet. */

.fab-menu {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.75rem;
}

/* Real HTML backdrop (not ::backdrop) so it captures touch on iOS Safari.
   Sits above page content (the wrapper carries z-40) but below the items and
   trigger, which are lifted with position/z-index below. Inert when closed. */
.fab-menu__backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  opacity: 0;
  pointer-events: none;
  background-color: oklch(0 0 0 / 0.28);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  transition: opacity 250ms var(--ease-out-soft);
}

.fab-menu--open .fab-menu__backdrop {
  opacity: 1;
  pointer-events: auto;
}

.fab-menu__items {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.75rem;
  pointer-events: none;
}

.fab-menu--open .fab-menu__items {
  pointer-events: auto;
}

.fab-menu__item {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  /* Grow the fan out of the FAB: origin on the mini-FAB's centre (right side). */
  transform-origin: right center;
  opacity: 0;
  transform: translateY(12px) scale(0.55);
  /* Closed state carries the crisp close transition + top → bottom stagger. */
  transition:
    opacity 180ms var(--ease-crisp),
    transform 180ms var(--ease-crisp);
  transition-delay: calc(var(--j, 0) * 30ms);
}

.fab-menu--open .fab-menu__item {
  opacity: 1;
  transform: translateY(0) scale(1);
  /* Open state springs, staggered bottom → top. */
  transition:
    opacity 320ms var(--ease-spring),
    transform 320ms var(--ease-spring);
  transition-delay: calc(var(--i, 0) * 40ms);
}

/* Label pill — always visible, card surface, matches .floating-action-bar. */
.fab-menu__label {
  padding: 0.375rem 0.625rem;
  border-radius: var(--rounded-md, 0.375rem);
  background-color: var(--color-bg);
  color: var(--color-text);
  box-shadow: var(--shadow-md);
  font-size: 0.8125rem;
  font-weight: 500;
  white-space: nowrap;
}

/* Mini-FAB — 48px round, :surface look (card bg, border, md shadow). */
.fab-menu__mini {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 9999px;
  background-color: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-md);
  cursor: pointer;
  transition: box-shadow 150ms var(--ease-crisp), transform 150ms var(--ease-crisp);
}

.fab-menu__mini:hover {
  box-shadow: var(--shadow-lg);
}

.fab-menu__mini:active {
  transform: scale(0.94);
}

/* button_to wraps the mini-FAB in a form — keep it out of the flex flow so the
   label pill still sits directly beside the button. */
.fab-menu__item form {
  display: inline-flex;
  margin: 0;
}

.fab-menu__trigger {
  position: relative;
  z-index: 1;
}

/* Plus → × : the trigger icon rotates when the menu opens. */
.fab-menu__icon {
  transition: transform 300ms var(--ease-spring);
}

.fab-menu--open .fab-menu__trigger .fab-menu__icon {
  transform: rotate(135deg);
}

/* Center variant — the inline BottomNav FAB (:center_inline). The trigger sits
   in a fixed-height nav slot, so the items stack must fan out ABOVE the bar
   (absolute, out of flow) instead of growing the slot. Items are centred over
   the trigger; the label pill floats to the left of each centred mini-FAB so
   the mini-FABs form a straight column regardless of label width. */
.fab-menu--center {
  position: relative;
  align-items: center;
}

.fab-menu--center .fab-menu__items {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 0.75rem;
  align-items: center;
}

.fab-menu--center .fab-menu__item {
  position: relative;
  justify-content: center;
  /* Spring straight up out of the trigger below. */
  transform-origin: center bottom;
}

.fab-menu--center .fab-menu__label {
  position: absolute;
  /* Right edge of the pill sits one gap left of the 48px mini-FAB's left edge. */
  right: calc(50% + 24px + 0.75rem);
}

/* Reduced motion: fade only, no transform, no stagger. */
@media (prefers-reduced-motion: reduce) {
  .fab-menu__item,
  .fab-menu--open .fab-menu__item {
    transform: none;
    transition: opacity 120ms linear;
    transition-delay: 0ms;
  }

  .fab-menu__icon,
  .fab-menu--open .fab-menu__trigger .fab-menu__icon {
    transition: none;
  }

  .fab-menu__backdrop {
    transition: opacity 120ms linear;
  }
}


/* ===== flash.css ===== */
.flash {
  align-items: center;
  animation: appear-then-fade 4s 300ms both;
  backdrop-filter: var(--blur-sm) var(--contrast-75);
  background-color: var(--flash-background, rgb(from var(--color-text) r g b / .65));
  border-radius: var(--rounded-full);
  color: var(--flash-color, var(--color-text-reversed));
  column-gap: var(--size-2);
  display: flex;
  font-size: var(--text-fluid-base);
  justify-content: center;
  line-height: var(--leading-none);
  margin-block-start: var(--flash-position, var(--size-4));
  margin-inline: auto;
  min-block-size: var(--size-11);
  padding: var(--size-1) var(--size-4);
  text-align: center;

  [data-turbo-preview] & {
    display: none;
  }
}

.flash--positive {
  --flash-background: var(--color-positive);
  --flash-color: white;
}

.flash--negative {
  --flash-background: var(--color-negative);
  --flash-color: white;
}

.flash--extended {
  animation-name: appear-then-fade-extended;
  animation-duration: 12s;
}

@keyframes appear-then-fade {
  0%, 100% { opacity: 0; }
  5%, 60%  { opacity: 1; }
}

@keyframes appear-then-fade-extended {
  0%, 100% { opacity: 0; }
  2%, 90% { opacity: 1; }
}


/* ===== status_dot.css ===== */
/* Ui::StatusDotComponent
 *
 * Pulse keyframe rings expand outward from the dot, fading from full opacity
 * to transparent in 2.2s. One keyframe per tone keeps the dot colour and the
 * ring colour consistent.
 */

@keyframes ui-status-dot-pulse-live {
  0%, 100% { box-shadow: 0 0 0 0 rgba(132, 204, 22, 0.55); }
  50%      { box-shadow: 0 0 0 7px rgba(132, 204, 22, 0); }
}

@keyframes ui-status-dot-pulse-warning {
  0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.55); }
  50%      { box-shadow: 0 0 0 7px rgba(245, 158, 11, 0); }
}

@keyframes ui-status-dot-pulse-info {
  0%, 100% { box-shadow: 0 0 0 0 rgba(56, 189, 248, 0.55); }
  50%      { box-shadow: 0 0 0 7px rgba(56, 189, 248, 0); }
}

.ui-status-dot--pulse-live    { animation: ui-status-dot-pulse-live    2.2s ease-in-out infinite; }
.ui-status-dot--pulse-warning { animation: ui-status-dot-pulse-warning 2.2s ease-in-out infinite; }
.ui-status-dot--pulse-info    { animation: ui-status-dot-pulse-info    2.2s ease-in-out infinite; }


/* ===== floating_action_bar.css ===== */
.floating-action-bar {
  display: none;
  align-items: center;
  gap: 1rem;
  position: fixed;
  top: 20px;
  z-index: 50;
  padding: 0.5rem 0.75rem;
  border-radius: var(--rounded-xl, 0.75rem);
  font-size: var(--text-sm, 0.875rem);

  left: 0;
  right: 0;
  width: fit-content;
  margin-inline: auto;

  background-color: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  box-shadow: 0 8px 30px oklch(0 0 0 / 0.12), 0 2px 8px oklch(0 0 0 / 0.08);
}

.floating-action-bar__count {
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  padding-inline: 0.25rem;
}

.floating-action-bar__divider {
  width: 1px;
  height: 1.25rem;
  background-color: var(--color-border);
}

.floating-action-bar__actions {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

/* A tone modifier colours BOTH the icon and the label together — the icon
   inherits the button's colour via currentColor. The default (no modifier)
   is neutral grey; add --danger / --success / --neutral for semantic colour. */
.floating-action-bar__action {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.3rem 0.625rem;
  border-radius: var(--rounded-md, 0.375rem);
  font-size: inherit;
  font-weight: 500;
  background: transparent;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  color: var(--color-text-subtle);
  transition: color 150ms, background-color 150ms;

  &:hover {
    color: var(--color-text);
    background-color: var(--color-secondary);
  }
}

/* Tone modifiers colour icon + label together (icon inherits via currentColor).
   Each keeps its tone colour on hover; only the hover background changes. */
.floating-action-bar__action--danger {
  color: var(--color-negative);

  &:hover {
    color: var(--color-negative);
    background-color: var(--color-error-bg);
  }
}

.floating-action-bar__action--success {
  color: var(--color-positive);

  &:hover {
    color: var(--color-positive);
    background-color: var(--color-success-bg);
  }
}

.floating-action-bar__action--neutral {
  color: var(--color-text-subtle);
}

.floating-action-bar__dismiss {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem;
  border-radius: var(--rounded-md, 0.375rem);
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--color-text-subtle);
  transition: color 150ms, background-color 150ms;

  &:hover {
    color: var(--color-text);
    background-color: var(--color-secondary);
  }
}

/* Mobile: bottom sheet layout — styled to match the dropdown menu's mobile
   bottom sheet (drag handle, backdrop, swipe-to-dismiss; see popover.css /
   menu.css) so both sheets read identically. */
@media (max-width: 639px) {
  .floating-action-bar {
    top: unset;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    margin-inline: 0;
    /* Sit above the backdrop (z-index 998), like the dropdown sheet (1000). */
    z-index: 1000;
    border-radius: var(--rounded-xl, 0.75rem) var(--rounded-xl, 0.75rem) 0 0;
    /* Only a hairline top border + soft shadow, identical to the dropdown
       sheet — no side/bottom borders and no heavy upward shadow. */
    border-inline: none;
    border-block-end: none;
    box-shadow: var(--shadow-xl);
    flex-wrap: wrap;
    gap: 0;
    padding: 0.875rem 1rem;
    padding-bottom: max(0.875rem, env(safe-area-inset-bottom));
    align-items: center;
  }

  /* Row 1: count only — the X is hidden, swipe-to-dismiss replaces it.
     Styled as a sheet title to match the 3-dots menu's 16px typography. */
  .floating-action-bar__count {
    order: 1;
    flex: 1;
    font-size: var(--text-base, 1rem);
    font-weight: 600;
  }

  /* X sits top-right next to the title; pressing it resets the selection
     (same dismiss path as swipe / backdrop tap). */
  .floating-action-bar__dismiss {
    order: 3;
    align-self: center;
  }

  /* Dividers hidden on mobile */
  .floating-action-bar__divider {
    order: 4;
    display: none;
  }

  /* Row 2: actions — always expanded, styled like the dropdown menu's
     mobile bottom sheet (see menu.css) so both sheets read identically. */
  .floating-action-bar__actions {
    order: 5;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    gap: 1px;
    border-top: 1px solid var(--color-border);
    padding-top: 0.5rem;
    margin-top: 0.75rem;
  }

  .floating-action-bar__action {
    width: 100%;
    padding: 0.75rem 1rem;
    font-size: var(--text-base, 1rem);
    gap: 0.75rem;
  }

  /* Compact variant: a slim, non-modal icon toolbar (one row) — used for
     gallery multi-select, where the count lives in the page's selection
     header and the grid behind the bar must stay tappable. Overrides the
     tall-sheet rules above. */
  .floating-action-bar--compact {
    flex-wrap: nowrap;
    gap: 0;
    padding: 0.5rem;
    padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
  }

  /* Drop the redundant count, dividers and X — the selection header carries
     the count + exit, and swipe-down / deselect-all still dismiss the bar. */
  .floating-action-bar--compact .floating-action-bar__count,
  .floating-action-bar--compact .floating-action-bar__divider,
  .floating-action-bar--compact .floating-action-bar__dismiss {
    display: none;
  }

  /* Actions become a single horizontal row of evenly-spread icon buttons. */
  .floating-action-bar--compact .floating-action-bar__actions {
    flex-direction: row;
    width: 100%;
    justify-content: space-around;
    gap: 0;
    border-top: none;
    padding-top: 0;
    margin-top: 0;
  }

  /* Icon-only buttons: font-size:0 collapses the (still screen-reader-visible)
     text label while the rem-sized icon stays. */
  .floating-action-bar--compact .floating-action-bar__action {
    flex: 1;
    width: auto;
    justify-content: center;
    padding: 0.5rem 0;
    gap: 0;
    font-size: 0;
  }

  /* IconComponent emits an inline width/height of var(--size-4); override it so
     the toolbar icons read at a comfortable tap size. */
  .floating-action-bar--compact .floating-action-bar__action svg {
    width: 1.5rem !important;
    height: 1.5rem !important;
  }
}

/* Real HTML backdrop for the mobile selection sheet, mirroring the dropdown
   menu's backdrop (popover.css). A real element (not ::backdrop) is required
   so it can capture touch events on iOS Safari. Created/destroyed by the
   floating-action-bar controller on mobile only. */
.floating-action-bar__backdrop {
  position: fixed;
  inset: 0;
  z-index: 998;
  background-color: oklch(0 0 0 / 0);
  transition: background-color var(--time-300, 0.3s) ease-out;
}

.floating-action-bar__backdrop--visible {
  background-color: oklch(0 0 0 / 0.5);
}


/* ===== gui_components/cropper_base.css ===== */
/*!
 * Cropper.js v1.6.2
 * https://fengyuanchen.github.io/cropperjs
 *
 * Copyright 2015-present Chen Fengyuan
 * Released under the MIT license
 *
 * Date: 2024-04-21T07:43:02.731Z
 */.cropper-container{-webkit-touch-callout:none;direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
/* gem ImageCropComponent base layout/round/zoom rules (no longer shadowed since
   the app's gallery dialog CSS moved to image_crop_dialog.css). */

/* ===== image_crop.css ===== */
/* ==========================================================================
   ImageCropComponent
   Circular image cropper built on Cropper.js.
   ========================================================================== */

/* Cropper.js base CSS. NESTED @import — kept for the standalone/Propshaft path,
 * but Lightning CSS drops it in the docs build (Tailwind v4), so
 * gui_components/all.css imports cropper_base.css DIRECTLY (before this file) as
 * well. Double import is idempotent. Do not remove — the Propshaft path needs it. */
/* deduped @import: gui_components/cropper_base.css */

/* --- ImageCrop component layout ----------------------------------------- */

.image-crop__container {
  display: none;
  max-width: 400px;
  max-height: 400px;
  margin: 0 auto;
  border-radius: var(--radius-xl, 0.75rem);
  overflow: hidden;
  border: 1px solid var(--color-border, #e5e7eb);
}

.image-crop__container .cropper-container {
  max-height: 400px;
}

.image-crop__container .cropper-container img {
  max-height: 400px;
}

.image-crop__image {
  max-width: 100%;
  height: auto;
}

/* --- Round crop overlay ------------------------------------------------- */

.cropper-crop-box.round {
  border-radius: 50% !important;
  overflow: hidden !important;
}

.cropper-view-box.round {
  border-radius: 50% !important;
  outline: 2px solid rgba(255, 255, 255, 0.8) !important;
  outline-offset: 0 !important;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.3) !important;
}

.cropper-face.round {
  border-radius: 50% !important;
}

.cropper-container.round .cropper-line,
.cropper-container.round .cropper-point {
  display: none !important;
}

/* Cropper.js dims the modal via `background: #000; opacity: .5`, which leaves
   the area outside the circle barely darker than the image on bright photos.
   Full opacity with a controlled alpha makes the excluded area unmistakable. */
.cropper-container.round .cropper-modal {
  background-color: rgba(0, 0, 0, 0.55) !important;
  opacity: 1 !important;
}

/* --- Zoom slider -------------------------------------------------------- */

.image-crop__zoom {
  display: none;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
  max-width: 400px;
}

.image-crop__zoom-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  shrink: 0;
  padding: 0.25rem;
  border-radius: 50%;
  cursor: pointer;
  background: none;
  border: none;
  transition: background-color 0.15s;
}

.image-crop__zoom-btn:hover {
  background-color: var(--color-gray-100, rgba(0, 0, 0, 0.06));
}

/*
 * The zoom slider now uses the shared `.slider` styling and controller
 * (pointer-capture track-tap/drag). The wrapper grows to fill the row
 * between the +/- buttons; the input's own sizing comes from `.slider`.
 */
.image-crop__zoom-slider-wrap {
  flex: 1;
  display: flex;
  min-width: 0;
}

.image-crop__zoom-slider {
  flex: 1;
}

/* --- Action buttons ----------------------------------------------------- */

.image-crop__actions {
  display: none;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1rem;
}

/* --- Error message ------------------------------------------------------ */

.image-crop__error {
  display: none;
  margin-top: 0.5rem;
  font-size: 0.875rem;
  color: var(--color-red-600, #dc2626);
}

/* app gallery crop dialog (ImageCropModalComponent), renamed from image_crop.css
   so it no longer shadows the gem file above. */

/* ===== image_crop_dialog.css ===== */
/*
 * Image crop dialog (ImageCropModalComponent + image-crop-dialog controller).
 * The modal chrome (box, backdrop, close button, footer, animations) comes from
 * the shared gui_components `.dialog` styles; only the cropper stage and the
 * flex column that keeps the footer pinned while the stage flexes live here.
 * gui_components/cropper_base.css supplies the crop-box chrome itself.
 */

/* display:flex only while open so the closed dialog keeps the UA display:none
   (an unconditional display would override it and leave the dialog visible). */
.dialog.image-crop[open] {
  display: flex;
  flex-direction: column;
}

.dialog.image-crop {
  max-block-size: calc(100dvh - 2rem);
}

.image-crop__content {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-block-size: 0;
  /* Scroll the body so the footer stays pinned and reachable on short or
     landscape viewports (same guard the gem's .dialog--scrollable uses). */
  overflow-y: auto;
}

.image-crop__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  min-block-size: 8rem;
  overflow: hidden;
  border-radius: var(--rounded-md, 0.5rem);
  background: #1b1e24;
}

/* Cropper replaces this <img>; it must be block and bounded to the stage. */
.image-crop__image {
  display: block;
  max-inline-size: 100%;
  max-block-size: 100%;
}

.image-crop__aspects {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-block-start: 1rem;
}


/* ===== image_crop_settings.css ===== */
/* ==========================================================================
   Settings profile-picture crop MODAL (desktop)

   Desktop crop dialog built on the shared gui_components ModalComponent
   (.dialog chrome) and the gem ImageCropComponent (Cropper.js). The gem's own
   base layout/round rules now load from image_crop.css (the gallery dialog CSS
   was renamed to image_crop_dialog.css, so the gem file is no longer shadowed);
   the rules below are the modal-specific overrides layered on that base, scoped
   to .image-crop-modal so they never collide with the gallery .image-crop.

   Layout mirrors the "Profilbild zuschneiden" design: a header (title +
   subtitle), a rectangular dark crop stage with a circular mask + zoom row, and
   a bordered footer (re-pick link left, cancel + save right). The footer is the
   image-crop controller's actions block, so its buttons stay in controller
   scope. dialog__content padding is dropped so each region pads itself; the crop
   and zoom rows are inset 28px while the footer spans the full dialog width.
   ========================================================================== */

.dialog.image-crop-modal {
  --dialog-size: 45rem;
  overflow: hidden; /* clip the footer's fill to the dialog's rounded corners */
}

.image-crop-modal .dialog__content {
  padding: 0;
}

/* --- Header --------------------------------------------------------------- */

.image-crop-modal__head {
  padding: var(--size-6) var(--size-12) 0 var(--size-7);
}

.image-crop-modal__title {
  margin: 0;
  font-size: 1.1875rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--color-text);
}

.image-crop-modal__subtitle {
  margin: var(--size-1) 0 0;
  font-size: 0.84375rem;
  color: var(--color-text-subtle);
}

/* --- Crop stage ----------------------------------------------------------- */

.image-crop-modal__crop {
  display: block;
}

.image-crop-modal .image-crop__container {
  display: none;
  inline-size: auto;
  max-block-size: min(340px, 60vh);
  margin: var(--size-5) var(--size-7) 0;
  border-radius: var(--rounded-lg);
  background: #1e293b;
  overflow: hidden;
}

.image-crop-modal .image-crop__container .cropper-container {
  max-block-size: min(340px, 60vh);
}

/* Solid dark backdrop instead of Cropper.js' checkerboard — the cover-zoom
   minimum keeps the circle filled; this hides any transparent margin. */
.image-crop-modal .cropper-bg {
  background-image: none;
  background-color: #1e293b;
}

.image-crop-modal .image-crop__image {
  max-inline-size: 100%;
  block-size: auto;
}

/* --- Zoom row ------------------------------------------------------------- */

.image-crop-modal .image-crop__zoom {
  display: none;
  align-items: center;
  gap: var(--size-3);
  /* The gem base rule caps .image-crop__zoom at max-width: 400px, pinning the
     row narrow and left-aligned inside the wide dialog; release it so the row
     spans the full crop-image width between the +/- buttons. */
  max-inline-size: none;
  margin: var(--size-4) var(--size-7) 0;
}

.image-crop-modal .image-crop__zoom-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  inline-size: 2.125rem;
  block-size: 2.125rem;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--rounded-md);
  background: var(--color-bg);
  color: var(--color-text-subtle);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.image-crop-modal .image-crop__zoom-btn:hover {
  background: var(--brand-neutral-50);
  border-color: var(--color-border-dark);
}

/* The wrapper's "grow to fill the row" (flex:1; display:flex; min-width:0) now
   comes from the gem's own .image-crop__zoom-slider-wrap rule in image_crop.css,
   which loads again since it is no longer shadowed — so it is not re-supplied
   here. The slider below fills that wrapper. */
.image-crop-modal .image-crop__zoom-slider {
  flex: 1;
  /* WebKit keeps native ranges at their ~129px intrinsic width and refuses to
     grow them under flex; strip the native appearance and force full width so
     the slider fills the row between the +/- buttons. */
  -webkit-appearance: none;
  appearance: none;
  inline-size: 100%;
  min-inline-size: 0;
  block-size: 1.25rem;
  background: transparent;
  cursor: pointer;
}

/* appearance: none removes the native track/thumb AND accent-color, so the
   slider must be re-styled explicitly (theme primary, no hardcoded hex).
   Separate WebKit and Firefox selectors — a shared list would drop both rules
   in each engine. Track height 6px, thumb centred over it. */
.image-crop-modal .image-crop__zoom-slider::-webkit-slider-runnable-track {
  block-size: 6px;
  border-radius: 999px;
  background: var(--color-border);
}

.image-crop-modal .image-crop__zoom-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  inline-size: 18px;
  block-size: 18px;
  margin-block-start: -6px;
  border-radius: 50%;
  background: var(--color-primary);
  border: 2px solid var(--color-bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.image-crop-modal .image-crop__zoom-slider::-moz-range-track {
  block-size: 6px;
  border-radius: 999px;
  background: var(--color-border);
}

.image-crop-modal .image-crop__zoom-slider::-moz-range-thumb {
  inline-size: 18px;
  block-size: 18px;
  border-radius: 50%;
  background: var(--color-primary);
  border: 2px solid var(--color-bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

/* --- Footer (the image-crop controller's actions block) ------------------- */

.image-crop-modal .image-crop__actions {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--size-3);
  margin-block-start: var(--size-6);
  padding: var(--size-5) var(--size-7);
  border-block-start: 1px solid var(--color-border-light);
  background: var(--brand-neutral-50);
}

.image-crop-modal__choose {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-link);
  cursor: pointer;
}

.image-crop-modal__choose:hover {
  text-decoration: underline;
}

.image-crop-modal__buttons {
  display: flex;
  align-items: center;
  gap: var(--size-2);
}

.image-crop-modal .image-crop__error {
  display: none;
  margin: var(--size-2) var(--size-7) 0;
  font-size: 0.8125rem;
  color: var(--color-negative, #dc2626);
}

/* --- Round crop overlay (controller adds .round to the cropper internals) -- */

.image-crop-modal .cropper-crop-box.round,
.image-crop-modal .cropper-face.round {
  border-radius: 50% !important;
  overflow: hidden !important;
}

.image-crop-modal .cropper-view-box.round {
  border-radius: 50% !important;
  outline: 2px solid rgba(255, 255, 255, 0.92) !important;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.3) !important;
}

.image-crop-modal .cropper-container.round .cropper-line,
.image-crop-modal .cropper-container.round .cropper-point {
  display: none !important;
}

.image-crop-modal .cropper-container.round .cropper-modal {
  background-color: rgba(15, 23, 42, 0.62) !important;
}


/* ===== menu.css ===== */
.menu {
    /* Panel radius and padding drive the concentric inner radius of the items
       (below). Defaults match the previous hard-coded values so the panel is
       visually unchanged. */
    --menu-radius: var(--rounded-lg);
    --menu-padding: var(--size-1);

    display: flex;
    flex-direction: column;
    padding: var(--menu-padding);
    row-gap: var(--size-1);
    border-radius: inherit;
    overflow: hidden;
    margin: 0;

    /* Ensure the menu respects the parent's rounded corners */
    clip-path: inset(0 round var(--menu-radius));
}

.menu__header {
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    padding: var(--size-1_5) var(--size-2);
}

.menu__group {
    display: flex;
    flex-direction: column;
    row-gap: 1px;
}

.menu__separator {
    margin-inline: -0.25rem;
}

.menu__item {
    --btn-border-color: transparent;
    --btn-box-shadow: none;
    --btn-font-weight: var(--font-normal);
    --btn-hover-color: var(--color-secondary);
    --btn-justify-content: start;
    --btn-outline-size: 0;
    --btn-padding: var(--size-1_5) var(--size-2);
    --btn-text-align: start;
    /* Concentric inner radius: item radius = panel radius − panel padding, so
       the item highlight sits flush inside the panel's rounded corners. */
    --btn-radius: calc(var(--menu-radius) - var(--menu-padding));
    width: 100%;

    &:focus-visible {
        --btn-background: var(--color-secondary);
    }
}

/* Content alignment of menu items. Logical (start/end) so the modifier is
   RTL-safe. The default (no modifier) keeps items left-aligned. */
.menu--align-center .menu__item {
    --btn-justify-content: center;
    --btn-text-align: center;
}

.menu--align-end .menu__item {
    --btn-justify-content: end;
    --btn-text-align: end;
}

.menu__item--danger {
    --btn-color: var(--color-red-600);
}

.menu__item-key {
    color: var(--color-text-subtle);
    font-size: var(--text-xs);
    margin-inline-start: auto;
}

@media (width < 40rem) {
    .menu__item {
        --btn-padding: var(--size-3) var(--size-4);
        --btn-font-size: var(--text-base);
        --btn-gap: var(--size-3);
    }

    .menu__item svg {
        width: 1.25rem;
        height: 1.25rem;
    }
}


/* ===== month_calendar.css ===== */
/* Month Calendar Component Styles */

.month-calendar {
  --month-cal-shade: var(--brand-neutral-50);

  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2);
  width: 100%;
  overflow: hidden;
}

[data-color-scheme="dark"] .month-calendar {
  --month-cal-shade: var(--brand-neutral-900);
}

.month-calendar__header {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: space-between;
  gap: var(--size-4);
  padding: var(--size-4) var(--size-6);
  border-bottom: 1px solid var(--color-border);
  background-color: var(--color-bg);
}

.month-calendar__title {
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  margin: 0;
}

.month-calendar__toolbar {
  display: flex;
  align-items: center;
  gap: var(--size-3);
}

.month-calendar__nav-group {
  display: inline-flex;
  align-items: stretch;
  gap: 0.5rem;
  background-color: transparent;
}

.month-calendar__nav-form,
.month-calendar__today-form {
  display: contents;
}

.month-calendar__nav-group .btn {
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  color: var(--color-text);
  font-weight: var(--font-semibold);
  box-shadow: none;
}

.month-calendar__nav-group .btn:hover {
  background-color: var(--month-cal-shade);
}

/* Toolbar buttons — neutral colors + spacing.
   Double-class selectors give us specificity 0,2,0 so single-class Tailwind
   utilities (p-2.5, px-3.5, bg-secondary-100, ...) lose without !important. */
.month-calendar__toolbar .month-calendar__nav-button {
  padding: 0.5rem 1rem;
  min-width: 44px;
}

.month-calendar__toolbar .month-calendar__today-button {
  padding: 0.5rem 1.25rem;
}

.month-calendar__toolbar .month-calendar__month-view-button,
.month-calendar__toolbar .month-calendar__week-view-button,
.month-calendar__toolbar .month-calendar__day-view-button {
  padding: 0.5rem 1rem;
}

.month-calendar__toolbar .month-calendar__nav-button,
.month-calendar__toolbar .month-calendar__today-button,
.month-calendar__toolbar .month-calendar__month-view-button,
.month-calendar__toolbar .month-calendar__week-view-button,
.month-calendar__toolbar .month-calendar__day-view-button {
  background-color: var(--color-bg);
  border-color: var(--color-border);
  color: var(--color-text);
  box-shadow: none;
}

.month-calendar__toolbar .month-calendar__nav-button:hover,
.month-calendar__toolbar .month-calendar__today-button:hover,
.month-calendar__toolbar .month-calendar__month-view-button:hover,
.month-calendar__toolbar .month-calendar__week-view-button:hover,
.month-calendar__toolbar .month-calendar__day-view-button:hover {
  background-color: var(--month-cal-shade);
  border-color: var(--color-border);
  color: var(--color-text);
}

/* Active view-switch button — neutral fill (no theme color), so the
   currently shown view stands out without competing with the primary
   action button (Add event). */
.month-calendar__toolbar .month-calendar__view-button--active,
.month-calendar__toolbar .month-calendar__view-button--active:hover {
  background-color: var(--color-secondary);
  border-color: var(--color-border-dark);
  color: var(--color-text);
  font-weight: 600;
}

/* Icons inside toolbar buttons render softer than the button text
   label — keeps labels crisp while the chevrons / view-icons feel
   less heavy. */
.month-calendar__toolbar .month-calendar__nav-button svg,
.month-calendar__toolbar .month-calendar__today-button svg,
.month-calendar__toolbar .month-calendar__month-view-button svg,
.month-calendar__toolbar .month-calendar__week-view-button svg,
.month-calendar__toolbar .month-calendar__day-view-button svg {
  color: var(--color-text-subtle);
}

/* The Woche|Monat view-switch now renders as Components::Ui::SegmentedControl
   (link mode); its chrome lives in segmented_control.css. The former
   .month-calendar__seg* pill styles were removed with #979. */

.month-calendar__weekdays {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0;
  border-bottom: 1px solid var(--color-border);
  background-color: var(--month-cal-shade);
}

.month-calendar__weekday {
  text-align: center;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-text-subtle);
  padding: var(--size-2) 0;
  letter-spacing: 0.02em;
}

.month-calendar__days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: repeat(6, 1fr);
  min-height: 600px;
}

.month-calendar__day {
  background-color: var(--color-bg);
  border-right: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  padding: var(--size-2) var(--size-3);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background-color 0.15s ease;
  min-height: 110px;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.month-calendar__day:nth-child(7n) {
  border-right: none;
}

.month-calendar__day:nth-last-child(-n + 7) {
  border-bottom: none;
}

.month-calendar__day:hover {
  background-color: var(--month-cal-shade);
}

.month-calendar__day--current-month {
  color: var(--color-text);
  font-weight: var(--font-medium);
}

.month-calendar__day--other-month {
  color: var(--color-text-subtle);
  background-color: var(--month-cal-shade);
}

.month-calendar__day--other-month .month-calendar__day-number {
  opacity: 0.75;
}

.month-calendar__day--other-month:hover {
  background-color: var(--color-secondary);
}

.month-calendar__day-number {
  font-size: var(--text-sm);
  font-weight: var(--font-medium);
  margin-bottom: var(--size-1);
  align-self: flex-start;
}

.month-calendar__day--today .month-calendar__day-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 9999px;
  background-color: var(--color-primary);
  color: var(--color-text-reversed);
  font-weight: var(--font-semibold);
}

.month-calendar__events {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--size-1);
  flex: 1;
  width: 100%;
  min-width: 0;
}

.month-calendar__event {
  display: flex;
  align-items: baseline;
  gap: var(--size-2);
  padding: 2px 0;
  font-size: var(--text-xs);
  color: var(--color-text);
  cursor: pointer;
  transition: opacity 0.15s ease;
  background: transparent;
  border: none;
  user-select: none;
  min-width: 0;
}

.month-calendar__event:hover {
  opacity: 0.75;
}

.month-calendar__event-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 9999px;
  flex: none;
  align-self: center;
}

.month-calendar__event-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: var(--font-medium);
}

@keyframes month-calendar-marquee {
  0%   { text-indent: 0; }
  15%  { text-indent: 0; }
  85%  { text-indent: -100%; }
  100% { text-indent: -100%; }
}

.month-calendar__event:hover .month-calendar__event-name {
  text-overflow: clip;
  animation: month-calendar-marquee 6s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .month-calendar__event:hover .month-calendar__event-name {
    animation: none;
  }
}

.month-calendar__event-time {
  flex: none;
  font-size: var(--text-xs);
  font-weight: var(--font-normal);
  color: var(--color-text-subtle);
}

.month-calendar__more-events {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  font-weight: var(--font-medium);
  padding: 2px 0;
}

/* Responsive styles */
@media (max-width: 768px) {
  .month-calendar__header {
    padding: var(--size-3) var(--size-4);
    flex-wrap: wrap;
  }

  .month-calendar__days {
    min-height: 400px;
  }

  .month-calendar__day {
    min-height: 80px;
    padding: var(--size-1);
  }

  .month-calendar__event-time {
    display: none;
  }
}

@media (max-width: 480px) {
  .month-calendar__weekday {
    font-size: var(--text-xs);
    padding: var(--size-2) 0;
  }

  .month-calendar__day {
    min-height: 60px;
    padding: var(--size-1);
  }

  .month-calendar__day-number {
    font-size: var(--text-xs);
  }

  .month-calendar__day--today .month-calendar__day-number {
    width: 18px;
    height: 18px;
  }
}

/* ---------------------------------------------------------------------------
 * Compact mobile mode (.month-calendar--compact-mobile)
 *
 * Below 768px the calendar drops its card chrome and renders touch-first: a
 * two-row header (date-label picker trigger + ‹ / Heute / › as 44px targets,
 * then a full-width Woche|Monat segmented row), a month grid of 44px day
 * cells with event dots, and day cells as tap targets for the month-agenda
 * controller. Desktop rendering is unchanged.
 * ------------------------------------------------------------------------- */

.month-calendar__header--compact {
  display: none;
}

.month-calendar__dots {
  display: none;
}

@media (max-width: 767px) {
  /* Chromeless container: the compact calendar sits directly on the page
     background (and lets the month/year picker overflow the grid). */
  .month-calendar--compact-mobile {
    border: none;
    border-radius: 0;
    background-color: transparent;
    overflow: visible;
  }

  .month-calendar--compact-mobile .month-calendar__header {
    display: none;
  }

  .month-calendar--compact-mobile .month-calendar__header--compact {
    display: flex;
    flex-direction: column;
    gap: var(--size-3);
    padding: 0 0 var(--size-3);
  }

  /* ---- Control bar: [date label ▾] ‹ Heute › ---- */

  .month-calendar__compact-bar {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .month-calendar__compact-bar .month-calendar__compact-title-button {
    flex: 1;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 44px;
    padding: 0 2px;
    border: none;
    background: transparent;
    cursor: pointer;
  }

  .month-calendar__compact-title {
    font-size: 17px;
    font-weight: var(--font-semibold);
    line-height: 1.2;
    color: var(--color-text);
    white-space: nowrap;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    margin: 0;
  }

  .month-calendar__compact-title-chevron {
    color: var(--color-primary);
    flex: none;
  }

  .month-calendar__compact-bar .month-calendar__compact-nav-button {
    width: 44px;
    height: 44px;
    min-width: 44px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    background-color: var(--color-bg);
    color: var(--color-text);
    box-shadow: none;
    flex-shrink: 0;
  }

  .month-calendar__compact-bar .month-calendar__compact-today-button {
    height: 44px;
    padding: 0 14px;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    background-color: var(--color-bg);
    box-shadow: none;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    color: var(--color-primary);
    white-space: nowrap;
    flex-shrink: 0;
  }

  /* ---- Month/year picker dropdown ---- */

  .month-calendar__picker {
    position: absolute;
    top: 50px;
    left: 0;
    right: 0;
    z-index: 40;
    background-color: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-3);
    box-shadow: var(--shadow-lg);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }

  .month-calendar__picker[hidden] {
    display: none;
  }

  .month-calendar__picker-year-row {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .month-calendar__picker-nav-button {
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    background-color: var(--color-bg);
    color: var(--color-text);
    cursor: pointer;
  }

  .month-calendar__picker-year {
    flex: 1;
    text-align: center;
    font-family: var(--font-grotesk, inherit);
    font-size: 17px;
    font-weight: var(--font-bold);
    color: var(--color-text);
  }

  .month-calendar__picker-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
  }

  .month-calendar__picker .month-calendar__picker-month {
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 11px;
    background-color: transparent;
    box-shadow: none;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    color: var(--color-text);
    cursor: pointer;
  }

  .month-calendar__picker .month-calendar__picker-month:hover {
    background-color: var(--month-cal-shade);
  }

  .month-calendar__picker .month-calendar__picker-month--active,
  .month-calendar__picker .month-calendar__picker-month--active:hover {
    background-color: var(--color-primary);
    color: var(--color-text-reversed);
  }

  .month-calendar__picker-close {
    height: 44px;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    background-color: var(--color-bg);
    color: var(--color-text-subtle);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    cursor: pointer;
  }

  /* ---- Full-width Woche|Monat segmented row ----
     The compact header's Woche|Monat row is now a stretched SegmentedControl
     (segmented_view_switch → SegmentedControlComponent stretch:); its
     full-width chrome lives in segmented_control.css. The former
     .month-calendar__seg--wide overrides were removed with #979. */

  /* Non-segmented fallback (docs): the old view-switch pills in a row. */
  .month-calendar__compact-switch-row {
    display: flex;
    align-items: center;
    gap: var(--size-1);
  }

  .month-calendar__header--compact .month-calendar__compact-view-button {
    display: inline-flex;
    align-items: center;
    gap: var(--size-1);
    padding: var(--size-1) var(--size-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-2);
    background-color: var(--color-bg);
    color: var(--color-text);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    box-shadow: none;
    white-space: nowrap;
    flex-shrink: 0;
    min-height: 44px;
  }

  .month-calendar__header--compact .month-calendar__compact-view-button svg {
    color: var(--color-text-subtle);
  }

  .month-calendar__header--compact .month-calendar__compact-view-button--active {
    background-color: var(--color-secondary);
    border-color: var(--color-border-dark);
  }

  /* ---- Weekday row + month grid ---- */

  /* Hairline month grid (reference: Tailwind Plus mobile month view): 1px
     lines drawn by the gap over a border-colored track, white day cells,
     muted adjacent-month cells, the day number top-right, event dots bottom
     left. */
  .month-calendar--compact-mobile .month-calendar__weekdays {
    background-color: transparent;
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: 4px;
  }

  .month-calendar--compact-mobile .month-calendar__weekday {
    font-size: 11px;
    font-weight: var(--font-medium);
    padding: 0;
  }

  .month-calendar--compact-mobile .month-calendar__days {
    min-height: 0;
    grid-template-rows: repeat(6, 56px);
    gap: 1px;
    padding-bottom: 0;
    background-color: var(--color-border);
    border-block: 1px solid var(--color-border);
  }

  .month-calendar--compact-mobile .month-calendar__day {
    min-height: 56px;
    border: none;
    border-radius: 0;
    background-color: var(--color-bg, #fff);
    padding: 5px 7px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: flex-start;
    gap: 0;
  }

  .month-calendar--compact-mobile .month-calendar__day:hover {
    background-color: var(--color-hover);
  }

  .month-calendar--compact-mobile .month-calendar__day-number {
    margin: 0 0 0 auto;
    align-self: flex-end;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 9999px;
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    line-height: 1;
  }

  .month-calendar--compact-mobile .month-calendar__day--other-month {
    background-color: var(--color-shade);
  }

  .month-calendar--compact-mobile .month-calendar__day--other-month .month-calendar__day-number {
    opacity: 1;
    color: var(--color-border-dark);
  }

  /* Today (not selected): primary number, no cell tint — the hairline grid
     stays calm; selection is the only filled treatment. */
  .month-calendar--compact-mobile .month-calendar__day--today {
    background-color: var(--color-bg, #fff);
  }

  .month-calendar--compact-mobile .month-calendar__day--today .month-calendar__day-number {
    background-color: transparent;
    color: var(--color-primary);
    font-weight: var(--font-bold);
  }

  /* Selected: the cell stays white; the number gets a filled dark circle
     (primary circle when the selected day is today). Wins over today. */
  .month-calendar--compact-mobile .month-calendar__day--selected,
  .month-calendar--compact-mobile .month-calendar__day--selected:hover {
    background-color: var(--color-bg, #fff);
  }

  .month-calendar--compact-mobile .month-calendar__day--selected .month-calendar__day-number {
    background-color: var(--color-text);
    color: var(--color-bg, #fff);
    font-weight: var(--font-bold);
  }

  .month-calendar--compact-mobile .month-calendar__day--selected.month-calendar__day--today .month-calendar__day-number {
    background-color: var(--color-primary);
    color: var(--color-text-reversed);
  }

  .month-calendar--compact-mobile .month-calendar__events {
    display: none;
  }

  .month-calendar--compact-mobile .month-calendar__dots {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    align-self: flex-start;
    margin-top: auto;
    gap: 3px;
    height: 5px;
  }

  /* The month grid shows up to two presence dots per day (the week strip
     keeps up to three). */
  .month-calendar--compact-mobile .month-calendar__dot {
    width: 5px;
    height: 5px;
    border-radius: 9999px;
    flex: none;
  }

  .month-calendar--compact-mobile .month-calendar__dots .month-calendar__dot:nth-child(n + 3) {
    display: none;
  }
}

/* ---- Half-day cell tint (issue #1259) --------------------------------------
   A single-day event with a half-day period tints the cell background as a
   faint diagonal triangle (:am = top-left, :pm = bottom-right) behind the
   unchanged event chips. A local stacking context keeps the tint behind the
   day content. */
.month-calendar__day--half-am,
.month-calendar__day--half-pm {
  isolation: isolate;
}

.month-calendar__day--half-am::before,
.month-calendar__day--half-pm::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.16;
}

.month-calendar__day--half-am::before {
  background-color: var(--calendar-event-am-bg);
  clip-path: polygon(0 0, 100% 0, 0 100%);
}

.month-calendar__day--half-pm::after {
  background-color: var(--calendar-event-pm-bg);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
}


/* ===== calendar_strip.css ===== */
/* Calendar Strip — a calm Mon–Sun overview row (not a 24h time grid).
   Reuses the month calendar's header/toolbar/segmented styles; only the body
   below the header is bespoke. */

.calendar-strip {
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2);
  width: 100%;
  overflow: hidden;
}

.calendar-strip__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.calendar-strip__day {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--size-2);
  padding: var(--size-3) var(--size-2) var(--size-4);
  border-right: 1px solid var(--color-border);
  min-width: 0;
  text-align: center;
}

.calendar-strip__day:last-child {
  border-right: none;
}

.calendar-strip__day--today {
  background-color: var(--primary-50);
}

[data-color-scheme="dark"] .calendar-strip__day--today {
  background-color: color-mix(in oklch, var(--color-primary) 14%, transparent);
}

.calendar-strip__weekday {
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-text-subtle);
}

.calendar-strip__day--today .calendar-strip__weekday {
  color: var(--color-primary);
  font-weight: var(--font-semibold);
}

.calendar-strip__day-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 9999px;
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  line-height: 1;
  background-color: transparent;
}

.calendar-strip__day--muted .calendar-strip__weekday,
.calendar-strip__day--muted .calendar-strip__day-number {
  color: var(--color-text-subtle);
}

.calendar-strip__day--today .calendar-strip__day-number {
  background-color: var(--color-primary);
  color: var(--color-text-reversed);
}

.calendar-strip__events {
  display: flex;
  flex-direction: column;
  gap: 3px;
  width: 100%;
  min-width: 0;
}

.calendar-strip__event {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  gap: 4px;
  padding: 4px 5px;
  border-radius: 6px;
  font-size: 10.5px;
  font-weight: var(--font-semibold);
  line-height: 1.2;
  min-width: 0;
  background-color: color-mix(in oklch, var(--calendar-strip-event-color) 14%, transparent);
  color: var(--calendar-strip-event-color);
  overflow: hidden;
}

.calendar-strip__day--today .calendar-strip__event {
  background-color: var(--color-primary);
  color: var(--color-text-reversed);
}

/* Chips carrying host click data (event_click_data) act as buttons. */
.calendar-strip__event--clickable {
  cursor: pointer;
  transition: background-color var(--time-150);
}

.calendar-strip__event--clickable:hover {
  background-color: color-mix(in oklch, var(--calendar-strip-event-color) 24%, transparent);
}

.calendar-strip__day--today .calendar-strip__event--clickable:hover {
  background-color: color-mix(in oklch, var(--color-primary) 88%, var(--color-text-reversed));
}

.calendar-strip__event-time {
  flex: none;
  font-weight: var(--font-bold);
  white-space: nowrap;
}

.calendar-strip__event-name {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Dots (compact mobile) — hidden by default, shown below 768px in place of
   chips, mirroring the month calendar's compact dots. */
.calendar-strip__dots {
  display: none;
}

@media (max-width: 767px) {
  /* Chromeless container matching the compact month calendar. */
  .calendar-strip.month-calendar--compact-mobile {
    border: none;
    border-radius: 0;
    background-color: transparent;
    overflow: visible;
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__grid {
    gap: 4px;
  }

  /* Tall tappable day pills: weekday over day number over event dots. */
  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day {
    min-height: 70px;
    padding: 11px 0 10px;
    border-right: none;
    border-radius: 14px;
    gap: 5px;
    justify-content: flex-start;
    background-color: transparent;
    cursor: pointer;
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__weekday {
    font-size: 11px;
    font-weight: var(--font-medium);
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    height: auto;
    font-size: 17px;
    font-weight: var(--font-bold);
    background-color: transparent;
    color: var(--color-text);
  }

  /* Today (not selected): tinted pill with a primary ring. */
  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--today {
    background-color: var(--color-selected);
    box-shadow: inset 0 0 0 1.5px var(--color-primary);
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--today .calendar-strip__weekday,
  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--today .calendar-strip__day-number {
    color: var(--color-primary);
  }

  /* Selected: filled pill; everything reverses. Wins over today. */
  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--selected {
    background-color: var(--color-primary);
    box-shadow: none;
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--selected .calendar-strip__weekday,
  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--selected .calendar-strip__day-number {
    color: var(--color-text-reversed);
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__events {
    display: none;
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__dots {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 3px;
    height: 5px;
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__dot {
    width: 5px;
    height: 5px;
    border-radius: 9999px;
    flex: none;
  }

  .calendar-strip.month-calendar--compact-mobile .calendar-strip__day--selected .calendar-strip__dot {
    background-color: color-mix(in oklch, var(--color-text-reversed) 90%, transparent) !important;
  }
}

/* ---- Today timeline view --------------------------------------------------
   A horizontal hour axis with lane-stacked event cards. The scroller is the
   overflow-x surface (native swipe + scroll-snap on mobile); .calendar-strip__track
   is the fixed-width layer everything positions against. */

.calendar-strip__timeline-wrap {
  position: relative;
}

/* Positioning context for the ‹/› buttons + scroller. Begins BELOW the
   (non-scrolling) all-day bar so the buttons never sit on the header divider. */
.calendar-strip__timeline-scroll-area {
  position: relative;
}

.calendar-strip__timeline {
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  padding: var(--size-3) 0 var(--size-4);
  scrollbar-width: thin;
}

.calendar-strip__track {
  position: relative;
  min-width: 100%;
}

/* Hour axis: a label over each hour column, columns divided by a left line. */
.calendar-strip__hours {
  display: flex;
  align-items: flex-end;
}

.calendar-strip__hour {
  flex: none;
  box-sizing: border-box;
  border-left: 1px solid var(--color-border);
  padding: 0 0 var(--size-2) var(--size-2);
  min-height: 1.75rem;
}

.calendar-strip__hour:first-child {
  border-left: none;
}

.calendar-strip__hour-label {
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-text-subtle);
  white-space: nowrap;
}

/* Event lane band: cards are absolutely positioned by left/width/top. */
.calendar-strip__lanes {
  position: relative;
  border-top: 1px solid var(--color-border);
  padding-top: var(--size-2);
  min-height: var(--calendar-strip-card-height, 3.875rem);
}

.calendar-strip__card {
  position: absolute;
  box-sizing: border-box;
  height: calc(var(--calendar-strip-card-height, 3.875rem) - var(--size-2));
  padding: var(--size-2);
  border-radius: var(--radius-2);
  border-left: 3px solid var(--calendar-strip-event-color, var(--color-primary));
  background-color: color-mix(in srgb, var(--calendar-strip-event-color, var(--color-primary)) 12%, transparent);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 2px;
  scroll-snap-align: start;
}

.calendar-strip__card--clickable {
  cursor: pointer;
}

.calendar-strip__card--clickable:hover {
  background-color: color-mix(in srgb, var(--calendar-strip-event-color, var(--color-primary)) 20%, transparent);
}

.calendar-strip__card-title {
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.calendar-strip__card-time {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  white-space: nowrap;
}

/* All-day events: a compact full-width row above the axis. Rendered outside the
   horizontal scroller so it stays visible while the hour axis scrolls; its left
   padding matches the first hour column so chips line up with the axis. */
.calendar-strip__all-day-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-2);
  padding: var(--size-3) var(--size-2) var(--size-2);
}

.calendar-strip__all-day-chip {
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-text);
  padding: 2px var(--size-2);
  border-radius: var(--radius-1);
  border-left: 3px solid var(--calendar-strip-event-color, var(--color-primary));
  background-color: color-mix(in srgb, var(--calendar-strip-event-color, var(--color-primary)) 12%, transparent);
}

/* Now indicator: a vertical line with a dot, only shown for today in range. */
.calendar-strip__now {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 0;
  border-left: 2px solid var(--color-danger, #ef4444);
  pointer-events: none;
  z-index: 2;
}

.calendar-strip__now-dot {
  position: absolute;
  top: 0;
  left: -4px;
  width: 8px;
  height: 8px;
  border-radius: 9999px;
  background-color: var(--color-danger, #ef4444);
}

/* Desktop scroll buttons: hidden on touch, floating over the axis edges. */
.calendar-strip__timeline-scroll {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .calendar-strip__timeline-scroll {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: var(--size-3);
    z-index: 3;
    width: 2rem;
    height: 1.75rem;
    border: 1px solid var(--color-border);
    border-radius: 9999px;
    background-color: var(--color-bg);
    color: var(--color-text-subtle);
    cursor: pointer;
  }

  .calendar-strip__timeline-scroll:disabled {
    opacity: 0.4;
    cursor: default;
  }

  .calendar-strip__timeline-scroll--prev {
    left: var(--size-1);
  }

  .calendar-strip__timeline-scroll--next {
    right: var(--size-1);
  }
}


/* ===== day_agenda.css ===== */
/* Day Agenda Component Styles
 *
 * Agenda list rendered below a compact mobile month calendar; rows are
 * filtered per selected day by the month-agenda Stimulus controller. */

.day-agenda {
  display: flex;
  flex-direction: column;
}

.day-agenda__header {
  display: flex;
  align-items: baseline;
  gap: var(--size-2);
  margin: var(--size-4) 0 var(--size-2);
}

.day-agenda__day-label {
  font-size: var(--text-base);
  font-weight: var(--font-bold);
  color: var(--color-text);
}

.day-agenda__date-label {
  font-size: var(--text-sm);
  color: var(--color-text-subtle);
}

.day-agenda__list {
  display: flex;
  flex-direction: column;
  gap: var(--size-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.day-agenda__row {
  display: flex;
  width: 100%;
  align-items: center;
  gap: var(--size-3);
  background-color: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-3);
  padding: var(--size-3) var(--size-4);
  color: inherit;
  text-decoration: none;
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.day-agenda__color-bar {
  width: 4px;
  height: 32px;
  border-radius: 2px;
  flex: none;
}

.day-agenda__body {
  flex: 1;
  min-width: 0;
}

.day-agenda__title {
  font-size: var(--text-sm);
  font-weight: var(--font-bold);
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.day-agenda__meta {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.day-agenda__chevron {
  color: var(--color-text-subtle);
  flex: none;
}

.day-agenda__empty {
  border: 1.5px dashed var(--color-border);
  border-radius: var(--radius-3);
  padding: 18px;
  text-align: center;
  font-size: 13px;
  color: var(--color-text-subtle);
}

.day-agenda__empty[hidden],
.day-agenda__item[hidden] {
  display: none;
}


/* ===== onboarding_checklist.css ===== */
/* Onboarding Checklist Component
 * A floating, collapsible onboarding widget anchored to a viewport corner. Slides in from the
 * edge, collapses into a launcher pill with a progress ring, and styles three item states.
 * Built with semantic CSS variables so it adapts to the active theme and dark mode. */

.onboarding-checklist {
  position: fixed;
  inset-block-end: var(--size-4);
  inset-inline-end: var(--size-4);
  z-index: 50;
  pointer-events: none;
  font-family: var(--default-font-family, inherit);
}

.onboarding-checklist--bottom-left {
  inset-inline-start: var(--size-4);
  inset-inline-end: auto;
}

/* Card and launcher overlap at the anchored corner; one is shown at a time. */
.onboarding-checklist__card,
.onboarding-checklist__launcher {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  pointer-events: auto;
}

.onboarding-checklist--bottom-left .onboarding-checklist__card,
.onboarding-checklist--bottom-left .onboarding-checklist__launcher {
  inset-inline-end: auto;
  inset-inline-start: 0;
}

/* Card -------------------------------------------------------------------- */

.onboarding-checklist__card {
  display: flex;
  flex-direction: column;
  inline-size: min(22rem, calc(100vw - 2rem));
  max-block-size: calc(100dvh - var(--size-8));
  background-color: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--rounded-xl);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  transform: translateX(0);
  opacity: 1;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1), opacity 240ms ease;
}

/* Entrance slide-in for floating widgets (not for the in-flow static variant). */
.onboarding-checklist--bottom-right:not(.is-collapsed) .onboarding-checklist__card {
  animation: onboardingSlideInRight 360ms cubic-bezier(0.22, 1, 0.36, 1);
}

.onboarding-checklist--bottom-left:not(.is-collapsed) .onboarding-checklist__card {
  animation: onboardingSlideInLeft 360ms cubic-bezier(0.22, 1, 0.36, 1);
}

.onboarding-checklist.is-collapsed .onboarding-checklist__card {
  transform: translateX(calc(100% + var(--size-6)));
  opacity: 0;
  pointer-events: none;
}

.onboarding-checklist--bottom-left.is-collapsed .onboarding-checklist__card {
  transform: translateX(calc(-100% - var(--size-6)));
}

@keyframes onboardingSlideInRight {
  from { transform: translateX(calc(100% + var(--size-6))); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes onboardingSlideInLeft {
  from { transform: translateX(calc(-100% - var(--size-6))); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

/* Header ------------------------------------------------------------------ */

.onboarding-checklist__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--size-3);
  padding: var(--size-5) var(--size-5) var(--size-2);
}

.onboarding-checklist__title {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: var(--font-bold);
  color: var(--color-text);
  line-height: 1.2;
}

.onboarding-checklist__collapse-btn {
  flex-shrink: 0;
  display: inline-flex;
  padding: var(--size-1);
  background: transparent;
  border: 0;
  border-radius: var(--rounded);
  color: var(--color-text-subtle);
  cursor: pointer;
  line-height: 0;
  transition: background-color 150ms ease, color 150ms ease;
}

.onboarding-checklist__collapse-btn:hover {
  background-color: var(--color-secondary);
  color: var(--color-text);
}

/* Body & sections --------------------------------------------------------- */

.onboarding-checklist__body {
  padding: 0 var(--size-5);
  overflow-y: auto;
}

.onboarding-checklist__section + .onboarding-checklist__section {
  margin-block-start: var(--size-5);
}

.onboarding-checklist__section-title {
  margin: var(--size-3) 0;
  font-size: var(--text-base);
  font-weight: var(--font-bold);
  color: var(--color-text);
  line-height: 1.3;
}

.onboarding-checklist__items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--size-4);
}

/* Items ------------------------------------------------------------------- */

.onboarding-checklist__item {
  display: flex;
  align-items: flex-start;
  gap: var(--size-3);
}

.onboarding-checklist__indicator {
  flex-shrink: 0;
  inline-size: 1.5rem;
  block-size: 1.5rem;
  margin-block-start: 1px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 2px solid var(--color-border);
  border-radius: 9999px;
}

button.onboarding-checklist__indicator {
  cursor: pointer;
  transition: border-color 150ms ease, background-color 150ms ease;
}

.onboarding-checklist__item--completed .onboarding-checklist__indicator {
  background-color: var(--amber-100);
  border: 1px solid var(--amber-200);
}

.onboarding-checklist__item--current .onboarding-checklist__indicator {
  border-color: var(--amber-500);
}

.onboarding-checklist__item--locked .onboarding-checklist__indicator {
  border-color: var(--color-border);
}

.onboarding-checklist__item-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-inline-size: 0;
}

.onboarding-checklist__item-title {
  font-weight: var(--font-medium);
  color: var(--color-text);
  line-height: 1.35;
}

.onboarding-checklist__item-link {
  color: var(--color-text);
  text-decoration: none;
}

.onboarding-checklist__item-link:hover {
  text-decoration: underline;
}

.onboarding-checklist__item-desc {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-subtle);
  line-height: 1.4;
}

.onboarding-checklist__item--locked {
  opacity: 0.65;
}

.onboarding-checklist__item--locked .onboarding-checklist__item-title,
.onboarding-checklist__item--locked .onboarding-checklist__item-desc {
  color: var(--color-text-subtle);
}

/* Footer & progress ------------------------------------------------------- */

.onboarding-checklist__footer {
  display: flex;
  align-items: center;
  gap: var(--size-3);
  padding: var(--size-4) var(--size-5);
  margin-block-start: var(--size-2);
}

.onboarding-checklist__progress {
  flex: 1;
  min-inline-size: 0;
}

.onboarding-checklist__progress-track {
  block-size: 6px;
  border-radius: 9999px;
  background-color: var(--color-border-light);
  overflow: hidden;
}

.onboarding-checklist__progress-fill {
  block-size: 100%;
  inline-size: 100%;
  border-radius: 9999px;
  background-color: var(--amber-500);
  transform-origin: left;
  transform: translateX(calc(var(--onboarding-progress, 0) * 1% - 100%));
  transition: transform 400ms ease;
}

.onboarding-checklist__progress-label {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}

.onboarding-checklist__close-text {
  flex-shrink: 0;
  padding: var(--size-1_5) var(--size-3);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--rounded-md);
  font-size: var(--text-sm);
  color: var(--color-text);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 150ms ease;
}

.onboarding-checklist__close-text:hover {
  background-color: var(--color-secondary);
}

/* Launcher (collapsed pill) ----------------------------------------------- */

.onboarding-checklist__launcher {
  display: inline-flex;
  align-items: center;
  gap: var(--size-2);
  padding: var(--size-2) var(--size-4) var(--size-2) var(--size-2);
  background-color: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 9999px;
  box-shadow: var(--shadow-lg);
  font: inherit;
  cursor: pointer;
  opacity: 0;
  transform: scale(0.85) translateY(8px);
  pointer-events: none;
  transition: opacity 200ms ease, transform 200ms ease;
}

.onboarding-checklist.is-collapsed .onboarding-checklist__launcher {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.onboarding-checklist__launcher-ring {
  --ring-size: 2.25rem;
  position: relative;
  inline-size: var(--ring-size);
  block-size: var(--ring-size);
  flex-shrink: 0;
  border-radius: 9999px;
  display: grid;
  place-items: center;
  background: conic-gradient(
    var(--amber-500) calc(var(--onboarding-progress, 0) * 1%),
    var(--color-border-light) 0
  );
}

.onboarding-checklist__launcher-pct {
  inline-size: calc(var(--ring-size) - 6px);
  block-size: calc(var(--ring-size) - 6px);
  background-color: var(--color-bg);
  border-radius: 9999px;
  display: grid;
  place-items: center;
  font-size: var(--text-2xs);
  font-weight: var(--font-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

.onboarding-checklist__launcher-title {
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  white-space: nowrap;
}

/* Completion celebration -------------------------------------------------- */

.onboarding-checklist.is-complete .onboarding-checklist__card {
  animation: onboardingCelebrate 700ms ease;
}

@keyframes onboardingCelebrate {
  0% { box-shadow: var(--shadow-xl); }
  45% { box-shadow: 0 0 0 4px var(--amber-200), var(--shadow-xl); }
  100% { box-shadow: var(--shadow-xl); }
}

.onboarding-checklist.is-leaving .onboarding-checklist__card,
.onboarding-checklist.is-leaving .onboarding-checklist__launcher {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 220ms ease, transform 220ms ease;
}

/* Static (in-flow) variant ------------------------------------------------ */

.onboarding-checklist--static {
  position: relative;
  inset: auto;
  pointer-events: auto;
  inline-size: min(22rem, 100%);
}

.onboarding-checklist--static .onboarding-checklist__card,
.onboarding-checklist--static .onboarding-checklist__launcher {
  position: relative;
  inset: auto;
}

.onboarding-checklist--static .onboarding-checklist__card {
  inline-size: 100%;
  max-block-size: none;
  transform: none;
  animation: none;
}

.onboarding-checklist--static.is-collapsed .onboarding-checklist__card {
  display: none;
}

.onboarding-checklist--static:not(.is-collapsed) .onboarding-checklist__launcher {
  display: none;
}

.onboarding-checklist--static .onboarding-checklist__launcher {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/* Dark mode --------------------------------------------------------------- */

[data-color-scheme="dark"] .onboarding-checklist__item--completed .onboarding-checklist__indicator {
  background-color: color-mix(in oklab, var(--amber-500) 22%, transparent);
  border-color: var(--amber-700);
}

[data-color-scheme="dark"] .onboarding-checklist__progress-track {
  background-color: var(--color-border);
}

/* Reduced motion ---------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .onboarding-checklist__card,
  .onboarding-checklist__launcher,
  .onboarding-checklist__progress-fill {
    transition: none;
    animation: none;
  }
}


/* ===== guide.css ===== */
/* ============================================================================
 * guide.css — GuideComponent (feature-explanation assistant).
 *
 * Self-contained on purpose: the customer-gallery bundle imports this file but
 * does NOT ship the gem's design tokens or Tailwind utilities. Every value here
 * is a literal or a token with a literal fallback (e.g. var(--color-primary,
 * #44403c)), and icon sizes are pinned in px because the slim bundle has no
 * IconComponent --size-* tokens. The trigger pill is server-rendered; the layer
 * (overlay, coach card, launcher, hotspots) is built at runtime by guide_core.js
 * and lives in a fixed full-viewport .guide-layer appended to <body>.
 * ========================================================================== */

.guide,
.guide-layer {
  --guide-bg: #ffffff;
  --guide-fg: #18181b;
  --guide-muted: #71717a;
  --guide-line: rgba(24, 24, 27, 0.10);
  --guide-surface: color-mix(in srgb, var(--color-primary, #a1a1aa) 6%, #f4f4f5);
  --guide-ink: #27272a;
  --guide-ink-hover: #18181b;
  --guide-accent: var(--color-primary, #16a34a);
  --guide-on-accent: #ffffff;
  --guide-shadow-sm: 0 1px 3px rgba(20, 18, 16, 0.18);
  --guide-shadow-lg: 0 18px 44px rgba(20, 18, 16, 0.22);
  --guide-radius: 16px;
  --guide-font: var(--font-sans, "Inter", "Helvetica Neue", system-ui, sans-serif);
}

/* ---------- Trigger pill (server-rendered) ---------- */

.guide {
  display: inline-flex;
}

.guide__trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--guide-font);
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: 0.01em;
  padding: 6px 16px 6px 6px;
  border-radius: 999px;
  border: 1px solid var(--guide-line);
  background: var(--guide-bg);
  color: var(--guide-fg);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: var(--guide-shadow-sm);
  transition: background 140ms ease;
}

.guide__trigger:hover {
  background: var(--guide-surface);
}

.guide__trigger-face {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--guide-surface);
  color: var(--guide-ink);
  overflow: hidden;
  flex: 0 0 auto;
}

.guide__face-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
  user-select: none;
}

.guide__face-icon,
.guide__trigger-face svg {
  width: 18px !important;
  height: 18px !important;
}

.guide__trigger-dot {
  --guide-dot-size: 11px;
  position: absolute;
  /* Tuned for the default pill trigger, where this lands on the top-right rim
   * of the 30px avatar. Round triggers re-anchor it below. */
  top: 0;
  left: 24px;
  width: var(--guide-dot-size);
  height: var(--guide-dot-size);
  border-radius: 50%;
  background: var(--guide-accent);
  border: 2px solid var(--guide-bg);
  display: none;
}

.guide--has-new .guide__trigger-dot {
  display: block;
  animation: guide-pulse 1.8s ease-in-out infinite;
}

/* Compact round help trigger (no avatar/label) — e.g. tucked into a toolbar
 * beside a bell icon. Flat and borderless so the Lucide circle-question-mark's
 * own ring is the only circle (no double ring); a hover surface still gives the
 * round affordance. */
.guide__trigger--icon {
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  border-radius: 50%;
  justify-content: center;
  background: transparent;
  color: var(--guide-ink);
  box-shadow: none;
}

/* Re-anchor the "new" badge for the round icon trigger. Pinning it to the
 * button's top-right corner does not work here: the button is a 44px box with
 * nothing but a centred 20px glyph, so a corner-pinned dot floats in empty
 * padding ~17px away from the glyph's ring and reads as a stray speck.
 * Instead orbit it out from the button's centre along the top-right diagonal:
 * --guide-dot-orbit is the glyph's radius x cos(45 deg) (10px x 0.707 for the
 * 20px glyph), and half the dot's own box centres it on that point. Positioned
 * via top/left, not transform — the has-new pulse animates transform: scale()
 * and would override it. Consumers that resize the glyph (e.g. a 34px trigger
 * with a 17px icon) set their own --guide-dot-orbit. */
.guide__trigger--icon .guide__trigger-dot {
  --guide-dot-orbit: 7px;
  top: calc(50% - var(--guide-dot-orbit) - var(--guide-dot-size) / 2);
  left: calc(50% + var(--guide-dot-orbit) - var(--guide-dot-size) / 2);
}

.guide__trigger--icon .guide__trigger-q {
  display: block;
  /* Pinned in px: the slim customer bundle has no IconComponent --size-* tokens,
   * so IconComponent(size: 5)'s inline `width/height: var(--size-5)` is invalid
   * at computed-value time and the SVG falls back to its intrinsic size. !important
   * is required to beat that inline declaration; on token-loaded pages (backend
   * topbar) the inline var already resolves to 20px, so this matches them exactly
   * — no visual change there. */
  width: 20px !important;
  height: 20px !important;
}

@keyframes guide-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.35); }
}

/* ---------- Runtime layer ---------- */

.guide-layer {
  position: fixed;
  inset: 0;
  pointer-events: none;
  font-family: var(--guide-font);
}

.guide-layer [hidden] { display: none !important; }

/* ---------- Spotlight overlay ---------- */

.guide-overlay {
  position: fixed;
  inset: 0;
  pointer-events: auto;
}

.guide-overlay__svg {
  position: absolute;
  inset: 0;
  display: block;
}

/* Companion mode dims less. */
.guide-overlay--soft .guide-overlay__svg rect[mask] {
  fill: rgba(14, 12, 10, 0.22);
}

/* No valid target this step: drop the cut-out and ring entirely. */
.guide-overlay--blank [data-part="cut"],
.guide-overlay--blank [data-part="ring"] {
  display: none;
}

.guide-overlay [data-part="cut"],
.guide-overlay [data-part="ring"] {
  transition: x 0.36s var(--ease-standard), y 0.36s var(--ease-standard),
    width 0.36s var(--ease-standard), height 0.36s var(--ease-standard);
}

/* ---------- Companion connector + avatar ---------- */

.guide-connector {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

.guide-connector path {
  stroke: rgba(255, 255, 255, 0.9);
  stroke-width: 2.5;
  stroke-dasharray: 5 6;
  stroke-linecap: round;
  animation: guide-dash 14s linear infinite;
}

@keyframes guide-dash {
  to { stroke-dashoffset: -220; }
}

.guide-companion {
  position: fixed;
  right: 26px;
  bottom: 24px;
  width: 64px;
  height: 64px;
  pointer-events: auto;
}

.guide--bottom-right .guide-companion { bottom: 84px; }

.guide-companion__bob {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--guide-bg);
  box-shadow: var(--guide-shadow-lg);
  animation: guide-bob 3.4s ease-in-out infinite;
  transform-origin: center bottom;
}

.guide-companion__bob svg { width: 30px !important; height: 30px !important; color: var(--guide-ink); }
.guide-companion__bob .guide__face-img { width: 64px; height: 64px; }

@keyframes guide-bob {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-6px) rotate(2deg); }
}

/* ---------- Coach card ---------- */

.guide-coach {
  position: fixed;
  z-index: 2;
  left: 0;
  top: 0;
  width: min(340px, calc(100vw - 28px));
  background: var(--guide-bg);
  color: var(--guide-fg);
  border-radius: 18px;
  box-shadow: var(--guide-shadow-lg);
  border: 1px solid var(--guide-line);
  pointer-events: auto;
  overflow: hidden;
  transition: translate 0.36s var(--ease-standard);
  animation: guide-card-in 0.24s cubic-bezier(0.2, 0.7, 0.3, 1);
}

@keyframes guide-card-in {
  from { transform: translateY(8px) scale(0.99); opacity: 0; }
}

.guide-coach__head {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 15px 15px 9px;
}

.guide-coach__face {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--guide-surface);
  color: var(--guide-ink);
  overflow: hidden;
  flex: 0 0 auto;
}

.guide-coach__face svg { width: 22px !important; height: 22px !important; }
.guide-coach__face .guide__face-img { width: 40px; height: 40px; }

.guide-coach__titles { flex: 1; min-width: 0; }

.guide-coach__step {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--guide-muted);
}

.guide-coach__title {
  margin: 2px 0 0;
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--guide-fg);
}

.guide-coach__x {
  appearance: none;
  border: 0;
  background: var(--guide-surface);
  width: 26px;
  height: 26px;
  border-radius: 999px;
  cursor: pointer;
  color: var(--guide-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 0;
}

.guide-coach__x:hover { color: var(--guide-fg); }
.guide-coach__x svg { width: 15px !important; height: 15px !important; }

.guide-coach__body {
  padding: 0 16px 14px;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--guide-muted);
}

.guide-coach__media { padding: 0 16px 14px; }

.guide-coach__film {
  position: relative;
  border-radius: 13px;
  overflow: hidden;
  border: 1px solid var(--guide-line);
  background: #1c1a17;
  aspect-ratio: 16 / 9;
}

.guide-coach__film video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.guide-coach__filmtag {
  position: absolute;
  left: 10px;
  bottom: 9px;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: #fff;
  background: rgba(255, 255, 255, 0.18);
  padding: 3px 8px;
  border-radius: 999px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

.guide-coach__foot {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 13px;
  border-top: 1px solid var(--guide-line);
  background: var(--guide-surface);
}

.guide-coach__dots { display: flex; gap: 6px; flex: 1; }

.guide-coach__dot {
  appearance: none;
  border: 0;
  padding: 0;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--guide-ink) 28%, transparent);
  cursor: pointer;
  transition: width 0.2s ease, background-color 0.2s ease;
}

.guide-coach__dot.is-active {
  background: var(--guide-ink);
  width: 18px;
}

.guide-coach__btn {
  appearance: none;
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  font-family: var(--guide-font);
  font-weight: 600;
  font-size: 0.8125rem;
  padding: 9px 14px;
}

.guide-coach__btn--ghost { background: transparent; color: var(--guide-muted); }
.guide-coach__btn--ghost:hover { background: var(--guide-bg); }
.guide-coach__btn--prim { background: var(--guide-ink); color: var(--guide-on-accent); box-shadow: var(--guide-shadow-sm); }
.guide-coach__btn--prim:hover { background: var(--guide-ink-hover); }

/* ---------- Launcher ---------- */

.guide-launcher {
  position: fixed;
  z-index: 3;
  width: min(380px, calc(100vw - 24px));
  background: var(--guide-bg);
  color: var(--guide-fg);
  border-radius: 18px;
  box-shadow: var(--guide-shadow-lg);
  border: 1px solid var(--guide-line);
  padding: 6px;
  pointer-events: auto;
  animation: guide-card-in 0.18s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.guide-launcher__hero {
  padding: 18px 16px 14px;
  border-radius: 14px;
  background: var(--guide-surface);
  position: relative;
}

/* Hero head: compass icon and title sit on one centered row. */
.guide-launcher__head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}

.guide-launcher__face {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--guide-bg);
  color: var(--guide-ink);
  overflow: hidden;
}

.guide-launcher__face svg { width: 22px !important; height: 22px !important; }
.guide-launcher__face .guide__face-img { width: 44px; height: 44px; }

.guide-launcher__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.guide-launcher__intro {
  margin: 3px 0 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--guide-muted);
}

.guide-launcher__start {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  margin-top: 13px;
  border: 0;
  border-radius: 11px;
  cursor: pointer;
  padding: 12px;
  font-family: var(--guide-font);
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--guide-on-accent);
  background: var(--guide-ink);
  box-shadow: var(--guide-shadow-sm);
}

.guide-launcher__start:hover { background: var(--guide-ink-hover); }
.guide-launcher__start svg { width: 16px !important; height: 16px !important; }

.guide-launcher__settings { padding: 6px 6px 4px; }

.guide-launcher__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px;
  border-radius: 11px;
}

.guide-launcher__row:hover { background: var(--guide-surface); }

.guide-launcher__label {
  font-size: 0.84375rem;
  font-weight: 600;
  color: var(--guide-fg);
}

.guide-launcher__label small {
  display: block;
  font-weight: 500;
  color: var(--guide-muted);
  font-size: 0.71875rem;
  margin-top: 1px;
}

/* segmented style toggle */
.guide-seg {
  display: flex;
  gap: 3px;
  padding: 3px;
  border-radius: 9px;
  background: var(--guide-surface);
  flex: 0 0 auto;
}

.guide-seg button {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--guide-font);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--guide-muted);
  padding: 6px 10px;
  border-radius: 7px;
}

.guide-seg button.is-active {
  background: var(--guide-bg);
  color: var(--guide-fg);
  box-shadow: var(--guide-shadow-sm);
}

/* hotspots toggle switch */
.guide-switch {
  appearance: none;
  position: relative;
  width: 42px;
  height: 25px;
  flex: 0 0 auto;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}

.guide-switch__track {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--guide-ink) 28%, transparent);
  border-radius: 999px;
  transition: background 180ms ease;
}

.guide-switch__knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 19px;
  height: 19px;
  background: #fff;
  border-radius: 50%;
  box-shadow: var(--guide-shadow-sm);
  transition: transform 180ms ease;
}

.guide-switch.is-on .guide-switch__track { background: var(--guide-accent); }
.guide-switch.is-on .guide-switch__knob { transform: translateX(17px); }

/* ---------- Video help (mode: videos) ---------- *
 * The launcher becomes a topic menu; picking a row opens the coach card as a
 * plain film viewer (no overlay/spotlight). The coach reuses .guide-coach +
 * .guide-coach__film, with the foot trimmed to a single back-to-topics button. */

.guide-launcher--menu { padding-bottom: 8px; }

.guide-menu {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 4px 6px 2px;
}

.guide-menu__item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  appearance: none;
  border: 0;
  background: var(--guide-surface);
  color: var(--guide-fg);
  text-align: left;
  cursor: pointer;
  padding: 11px 12px;
  border-radius: 12px;
  font-family: var(--guide-font);
  transition: background 140ms ease;
}

.guide-menu__item:hover { background: color-mix(in srgb, var(--guide-ink) 9%, var(--guide-surface)); }

.guide-menu__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--guide-bg);
  color: var(--guide-ink);
}

.guide-menu__icon svg { width: 14px !important; height: 14px !important; }

.guide-menu__label {
  flex: 1;
  min-width: 0;
  font-size: 0.9375rem;
  font-weight: 600;
}

.guide-menu__chev {
  display: flex;
  flex: 0 0 auto;
  color: var(--guide-muted);
}

.guide-menu__chev svg { width: 17px !important; height: 17px !important; }

/* Coach foot in videos mode: only the back-to-topics button remains. */
.guide-coach--videos .guide-coach__step,
.guide-coach--videos .guide-coach__dots,
.guide-coach--videos .guide-coach__btn--prim { display: none; }

.guide-coach--videos .guide-coach__foot { justify-content: flex-start; }

.guide-coach--videos .guide-coach__btn--ghost {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  visibility: visible !important;
}

.guide-coach--videos .guide-coach__btn--ghost svg { width: 15px !important; height: 15px !important; }

/* Placeholder film frame (no clip produced yet). */
.guide-coach__film--placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: rgba(255, 255, 255, 0.82);
  background: radial-gradient(circle at 50% 42%, #2c2823 0%, #1c1a17 70%);
}

.guide-coach__playmark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

.guide-coach__playmark svg { width: 20px !important; height: 20px !important; margin-left: 2px; }

.guide-coach__film--placeholder .guide-coach__filmtag {
  position: static;
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  padding: 0;
}

/* ---------- Hotspots + mini popover ---------- */

.guide-hotspot {
  position: fixed;
  z-index: 1;
  width: 27px;
  height: 27px;
  border-radius: 999px;
  border: 0;
  cursor: pointer;
  background: var(--guide-ink);
  color: #fff;
  font-family: var(--guide-font);
  font-weight: 800;
  font-size: 0.9375rem;
  line-height: 1;
  display: none;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  box-shadow: var(--guide-shadow-sm), 0 0 0 3px rgba(255, 255, 255, 0.9);
  transition: transform 0.15s ease, background 0.15s ease;
  animation: guide-hotspot-pulse 2.1s ease-in-out infinite;
}

.guide-hotspot:hover { background: var(--guide-ink-hover); transform: scale(1.12); }

@keyframes guide-hotspot-pulse {
  0%, 100% { box-shadow: var(--guide-shadow-sm), 0 0 0 3px rgba(255, 255, 255, 0.9), 0 0 0 0 color-mix(in srgb, var(--guide-ink) 40%, transparent); }
  50% { box-shadow: var(--guide-shadow-sm), 0 0 0 3px rgba(255, 255, 255, 0.9), 0 0 0 9px transparent; }
}

.guide-hpop {
  position: fixed;
  z-index: 4;
  width: min(268px, calc(100vw - 24px));
  background: var(--guide-bg);
  color: var(--guide-fg);
  border-radius: 14px;
  box-shadow: var(--guide-shadow-lg);
  border: 1px solid var(--guide-line);
  padding: 14px 15px;
  pointer-events: auto;
  animation: guide-card-in 0.16s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.guide-hpop h5 {
  margin: 0;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--guide-fg);
}

.guide-hpop p {
  margin: 6px 0 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--guide-muted);
}

.guide-hpop__more {
  margin-top: 11px;
  appearance: none;
  border: 0;
  background: var(--guide-surface);
  color: var(--guide-ink);
  font-family: var(--guide-font);
  font-weight: 600;
  font-size: 0.78125rem;
  padding: 8px 12px;
  border-radius: 9px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
}

.guide-hpop__more:hover { background: color-mix(in srgb, var(--guide-ink) 12%, var(--guide-surface)); }
.guide-hpop__more svg { width: 13px !important; height: 13px !important; }

/* ---------- Screen-reader live region ---------- */

.guide-live {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Mobile (phone-width) ---------- *
 * On narrow viewports the coach card stops chasing the target and docks to the
 * bottom as a sheet (the engine clears its inline left/top via _sheetCoach so
 * these rules win). It spans the width and tops out below the safe-area inset,
 * so it never covers the control the spotlight is highlighting above it. */

@media (width < 40rem) {
  .guide-coach,
  .guide-launcher--menu {
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: auto;
    max-width: none;
    max-height: 80dvh;
    overflow-y: auto;
    border-radius: 18px 18px 0 0;
    /* No left/top transition to chase: the sheet is docked, not positioned. */
    transition: none;
    padding-bottom: max(0px, env(safe-area-inset-bottom));
    animation: guide-sheet-in 0.26s cubic-bezier(0.2, 0.7, 0.3, 1);
  }

  /* Grab-handle affordance, matching the app's bottom sheets. */
  .guide-coach::before,
  .guide-launcher--menu::before {
    content: "";
    display: block;
    width: 2.25rem;
    height: 4px;
    margin: 8px auto 0;
    border-radius: 999px;
    background: var(--guide-line);
  }
}

@keyframes guide-sheet-in {
  from { transform: translateY(100%); }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .guide__trigger-dot,
  .guide-companion__bob,
  .guide-connector path,
  .guide-hotspot { animation: none !important; }
  .guide-coach,
  .guide-launcher,
  .guide-hpop { animation: none !important; }
  .guide-coach,
  .guide-overlay [data-part="cut"],
  .guide-overlay [data-part="ring"] { transition: none !important; }
}

.guide-layer--reduced .guide-coach,
.guide-layer--reduced .guide-overlay [data-part="cut"],
.guide-layer--reduced .guide-overlay [data-part="ring"],
.guide-layer--reduced .guide-companion__bob,
.guide-layer--reduced .guide-connector path,
.guide-layer--reduced .guide-hotspot { animation: none !important; transition: none !important; }


/* ===== password_strength.css ===== */
/* Password Strength — JS-applied state classes (toggled dynamically by Stimulus controller) */

/* Strength bar color variants */
.password-strength-bar--empty  { background-color: var(--brand-neutral-300); }
.password-strength-bar--weak   { background-color: var(--red-500); }
.password-strength-bar--fair   { background-color: var(--yellow-500); }
.password-strength-bar--good   { background-color: var(--lime-500); }
.password-strength-bar--strong { background-color: var(--green-500); }

@media (prefers-color-scheme: dark) {
  .password-strength-bar--empty  { background-color: var(--brand-neutral-500); }
  .password-strength-bar--weak   { background-color: var(--red-400); }
  .password-strength-bar--fair   { background-color: var(--yellow-400); }
  .password-strength-bar--good   { background-color: var(--lime-400); }
  .password-strength-bar--strong { background-color: var(--green-400); }
}

/* Strength text color variants */
.password-strength-text--weak   { color: var(--red-600); }
.password-strength-text--fair   { color: var(--yellow-600); }
.password-strength-text--good   { color: var(--lime-600); }
.password-strength-text--strong { color: var(--green-600); }

@media (prefers-color-scheme: dark) {
  .password-strength-text--weak   { color: var(--red-400); }
  .password-strength-text--fair   { color: var(--yellow-400); }
  .password-strength-text--good   { color: var(--lime-400); }
  .password-strength-text--strong { color: var(--green-400); }
}

/* Requirement states */
.password-requirement--unmet { color: var(--brand-neutral-500); }
.password-requirement--met   { color: var(--green-600); }

@media (prefers-color-scheme: dark) {
  .password-requirement--unmet { color: var(--brand-neutral-400); }
  .password-requirement--met   { color: var(--green-500); }
}

.password-requirement-text--met { text-decoration: line-through; }

/* Match indicator states */
.password-match--success { color: var(--green-600); }
.password-match--error   { color: var(--red-600); }

@media (prefers-color-scheme: dark) {
  .password-match--success { color: var(--green-400); }
  .password-match--error   { color: var(--red-400); }
}

/* display:flex base for match indicator (toggled via .hidden by JS) */
.password-match-indicator { display: flex; }

/* Icon size for JS-created SVG elements */
.icon {
  width: var(--size-4);
  height: var(--size-4);
  flex-shrink: 0;
}

/* Input success state (applied by JS on password match) */
.input--success {
  box-shadow: 0 0 0 3px rgb(34 197 94 / 0.1);
}


/* ===== input_error_shake.css ===== */
/* Validation-error shake for form fields. The field carrying the visible error
 * border shakes once horizontally on each failed submit. Server re-renders the
 * form with fresh DOM nodes (Turbo 422 frame/body replace), so the CSS
 * animation replays every time without any JS restart protocol.
 *
 * The keyframe name is deliberately distinct from `shake-x` (which the host app
 * shadows on purpose, see app_animations.css) so the two never collide.
 *
 * Tween: 4 segments 0 -> 6px -> -6px -> 4px -> 0 with segment durations
 * 80/60/80/60 ms (280 ms total). Cumulative stops at 0 / 28.57 / 57.14 /
 * 78.57 / 100 %. Each stop carries its own cubic-bezier easing; the base
 * animation timing stays linear so the per-stop functions drive the motion. */
@keyframes gui-input-error-shake {
  0% {
    transform: translateX(0);
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
  28.57% {
    transform: translateX(var(--shake-distance, 6px));
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
  57.14% {
    transform: translateX(calc(var(--shake-distance, 6px) * -1));
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
  78.57% {
    transform: translateX(var(--shake-overshoot, 4px));
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
  100% {
    transform: translateX(0);
  }
}

.gui-input--error-shake {
  animation: gui-input-error-shake 280ms linear both;
}

/* Reduced motion: keep the error border and message, drop the shake. */
@media (prefers-reduced-motion: reduce) {
  .gui-input--error-shake {
    animation: none;
  }
}


/* ===== slider.css ===== */
/*
 * GuiComponents: range slider styling.
 * Used by Components::Inputs::SliderComponent. Applies to <input
 * type="range" class="slider"> with vendor-specific track + thumb
 * pseudo-elements that pure Tailwind utilities can't reach.
 *
 * The input is a tall (44px) transparent hit area so a tap anywhere on
 * the track registers, while the visible track is drawn 4px high via the
 * runnable-track pseudo-element and vertically centred. `touch-action:
 * none` stops a surrounding scrollable sheet from stealing a drag that
 * drifts vertically; the slider_controller drives the value via pointer
 * capture instead.
 */

.slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 44px;
  min-height: 44px;
  background: transparent;
  border-radius: 999px;
  outline: none;
  cursor: pointer;
  touch-action: none;
}

.slider:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.slider::-webkit-slider-runnable-track {
  height: 4px;
  background: var(--color-border);
  border-radius: 999px;
}

.slider::-moz-range-track {
  height: 4px;
  background: var(--color-border);
  border-radius: 999px;
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--color-primary, var(--primary-600));
  border: 2px solid var(--color-bg, #fff);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-primary, var(--primary-600)) 16%, transparent);
  cursor: pointer;
  transition: transform 0.1s ease;
}

.slider::-webkit-slider-thumb:active {
  transform: scale(1.1);
}

.slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-primary, var(--primary-600));
  border: 2px solid var(--color-bg, #fff);
  cursor: pointer;
}

.slider:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-primary, var(--primary-600)) 32%, transparent);
}

.slider:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-primary, var(--primary-600)) 32%, transparent);
}

/*
 * Coarse pointers (touch): enlarge the thumb so it stays easy to see and
 * grab. Track height is unchanged, so the thumb margin re-centres it.
 */
@media (pointer: coarse) {
  .slider::-webkit-slider-thumb {
    width: 26px;
    height: 26px;
    margin-top: -11px;
  }

  .slider::-moz-range-thumb {
    width: 26px;
    height: 26px;
  }
}


/* ===== color_picker.css ===== */
/* Color Picker Component */

[data-controller="color-picker"] [aria-pressed="true"] {
  --tw-ring-color: var(--color-blue-500);
  --tw-ring-opacity: 1;
}

[data-color-scheme="dark"] sl-color-picker {
  --sl-color-neutral-0: var(--zinc-900, #18181b);
  --sl-color-neutral-50: var(--zinc-800, #27272a);
  --sl-color-neutral-100: var(--zinc-700, #3f3f46);
  --sl-color-neutral-200: var(--zinc-600, #52525b);
  --sl-color-neutral-300: var(--zinc-500, #71717a);
  --sl-color-neutral-1000: var(--zinc-50, #fafafa);
}


/* ===== loading-indicator.css ===== */
@layer components.base {
  @keyframes bar-pulse {
    0%, 100% { transform: scaleY(0.4); }
    50% { transform: scaleY(1); }
  }

  @media (prefers-reduced-motion: reduce) {
    [data-loading-bars] > * {
      animation-duration: 0.01ms !important;
    }
  }
}


/* ===== popover.css ===== */
/* Popover Component Styles */

/* Ensure popover containers don't clip content */
[data-controller="popover"] {
  position: relative;
  display: inline-block;
}

/* Dropdown specific styling */
[data-controller="dropdown"] {
  position: relative;
  display: inline-block;
}

/* Enhanced styling for dropdown popovers */
[data-controller="dropdown"] .popover {
  /* Enhanced shadow for dropdown menus */
  box-shadow: var(--shadow-xl);

  /* Enhanced border */
  border: 1px solid var(--color-border);

  /* Ensure proper background */
  background-color: var(--color-bg);

  /* Minimum width for better usability */
  min-width: var(--popover-size, 8rem);

  /* Ensure proper z-index */
  z-index: 1000;
}

/* Mobile: bottom sheet */
@media (width < 40rem) {
  [data-controller="dropdown"] .popover {
    inset-block-start: auto;
    inset-block-end: 0;
    inset-inline: 0;
    inline-size: 100%;
    min-width: unset;
    max-block-size: 80vh;
    overflow-y: auto;
    border-radius: var(--rounded-xl) var(--rounded-xl) 0 0;
    border-inline: none;
    border-block-end: none;
    padding-block-end: max(0.5rem, env(safe-area-inset-bottom));
  }

  /* Drag handle indicator */
  [data-controller="dropdown"] .popover::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 0.25rem;
    background-color: var(--color-border);
    border-radius: 9999px;
    margin: 0.5rem auto;
    flex-shrink: 0;
  }
}

/* Real HTML backdrop for mobile dropdown bottom sheet.
 * Replaces ::backdrop pseudo-element which cannot capture touch events on iOS Safari. */
.dropdown__backdrop {
  position: fixed;
  inset: 0;
  z-index: 998;
  background-color: oklch(0 0 0 / 0);
  transition: background-color var(--time-300) ease-out;
}

.dropdown__backdrop--visible {
  background-color: oklch(0 0 0 / 0.5);
}

/* Style for icon triggers */
[data-controller="popover"] > svg {
  cursor: help;
  transition: opacity 150ms;
}

[data-controller="popover"] > svg:hover {
  opacity: 0.8;
}

/* Main popover content container */
.popover-content {
  /* Shadow */
  box-shadow: var(--shadow-lg);
  
  /* Positioning */
  position: absolute;
  z-index: 9999;
  
  /* Typography */
  font-size: var(--text-sm);
  
  /* Background */
  background-color: var(--color-bg);
  
  /* Border */
  border: var(--border) solid var(--color-border);
  
  /* Border radius */
  border-radius: var(--rounded-lg);
  
  /* Initial state for animation */
  opacity: 0;
}

/* Visible state */
.popover-visible {
  opacity: 1;
}

/* Popover transitions */
.popover-transition {
  transition-property: opacity, transform;
  transition-duration: 250ms;
  transition-timing-function: ease-out;
}

/* Scale animation */
.popover-scale {
  transform: scale(0.95);
}

.popover-scale-100 {
  transform: scale(1);
}

/* Transform origin classes for directional animation */
.popover-origin-top {
  transform-origin: bottom center;
}

.popover-origin-bottom {
  transform-origin: top center;
}

.popover-origin-left {
  transform-origin: right center;
}

.popover-origin-right {
  transform-origin: left center;
}

/* Popover header */
.popover-header {
  /* Border bottom */
  border-bottom: var(--border) solid var(--color-border-light);
  
  /* Background - slightly lighter than main bg */
  background-color: var(--color-secondary);
  
  /* Padding */
  padding: 0.625rem 0.75rem;
  
  /* Border radius top only */
  border-radius: var(--rounded-md) var(--rounded-md) 0 0;
  
  /* Typography */
  font-weight: var(--font-semibold);
  color: var(--color-text-subtle);
}

/* Popover body */
.popover-body {
  /* Padding */
  padding: 0.75rem;
  
  /* Typography */
  color: var(--color-text-subtle);
  font-weight: normal;
}


/* Arrow container */
.popover-arrow-container {
  position: absolute;
  z-index: 999;
}

/* Horizontal padding for top/bottom placement */
.popover-arrow-h {
  padding-left: 0.125rem;
  padding-right: 0.125rem;
}

/* Vertical padding for left/right placement */
.popover-arrow-v {
  padding-top: 0.125rem;
  padding-bottom: 0.125rem;
}

/* Arrow element.
 * The tip half is filled via a diagonal gradient with the popover surface color
 * (theme token, never hardcoded) so it stays seamless on any background/theme. */
.popover-arrow {
  /* Fill color of the protruding tip — matches the popover surface */
  --popover-arrow-fill: var(--color-bg);

  /* Size */
  width: 0.75rem;
  height: 0.75rem;

  /* Transform */
  transform: rotate(45deg);

  /* Border style */
  border-style: solid;
  border-width: 1px;
}

/* Arrow directional styles */
.popover-arrow-bottom {
  border-bottom-color: var(--color-border);
  border-right-color: var(--color-border);
  border-top-color: transparent;
  border-left-color: transparent;
  background-image: linear-gradient(to top left, var(--popover-arrow-fill) 50%, transparent 50.1%);
}

.popover-arrow-top {
  border-top-color: var(--color-border);
  border-left-color: var(--color-border);
  border-bottom-color: transparent;
  border-right-color: transparent;
  background-image: linear-gradient(to bottom right, var(--popover-arrow-fill) 50%, transparent 50.1%);
}

.popover-arrow-left {
  border-bottom-color: var(--color-border);
  border-left-color: var(--color-border);
  border-top-color: transparent;
  border-right-color: transparent;
  background-image: linear-gradient(to top right, var(--popover-arrow-fill) 50%, transparent 50.1%);
}

.popover-arrow-right {
  border-top-color: var(--color-border);
  border-right-color: var(--color-border);
  border-bottom-color: transparent;
  border-left-color: transparent;
  background-image: linear-gradient(to bottom left, var(--popover-arrow-fill) 50%, transparent 50.1%);
}

/* An arrow on the top edge sits on the header (when present), not on the body —
 * fill it with the header surface color so there is no visible seam. */
.popover-content:has(.popover-header) .popover-arrow-top {
  --popover-arrow-fill: var(--color-secondary);
}

/* Dark mode adjustments */
[data-color-scheme="dark"] .popover-header {
  background-color: rgba(38, 38, 38, 0.25);
  border-bottom-color: rgba(115, 115, 115, 0.75);
  color: var(--brand-neutral-200);
}

[data-color-scheme="dark"] .popover-body {
  color: var(--brand-neutral-300);
}

/* Dark header is translucent over the popover bg — the arrow needs the
 * pre-composited equivalent since it renders over arbitrary page content. */
[data-color-scheme="dark"] .popover-content:has(.popover-header) .popover-arrow-top {
  --popover-arrow-fill: color-mix(in srgb, rgb(38, 38, 38) 25%, var(--color-bg));
}

/* Utility classes for popover content */
.popover-content p {
  margin: 0;
}

.popover-content p + p {
  margin-top: 0.75rem;
}

/* Popover body with flex layout */
.popover-body p + p {
  margin-top: 0.75rem;
}

/* Remove form-group margin in popovers for compact layouts */
.popover-body .form-group {
  margin-bottom: 0.5rem;
}

.popover-body .form-group:last-child {
  margin-bottom: 0;
}

/* Close button styles */
[data-popover-close-button] {
  cursor: pointer;
  background: transparent;
  border: none;
  padding: 0.25rem;
  color: var(--color-text-subtle);
  transition: color 150ms;
}

[data-popover-close-button]:hover {
  color: var(--color-text);
}




/* ===== popover-native.css ===== */
.popover {
  background-color: var(--color-bg);
  border-radius: var(--rounded-md);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-md);
  color: var(--color-text);
  inline-size: var(--popover-size, max-content);

  /* Final state of exit animation and setup */
  opacity: 0;
  transform: var(--scale-95);
  transition-behavior: allow-discrete;
  transition-duration: var(--time-150);
  transition-property: display, overlay, opacity, transform;

  /* Final state of entry animation */
  &:popover-open {
    opacity: 1; transform: var(--scale-100);
  }

  /* Initial state of entry animation */
  @starting-style {
    &:popover-open {
      opacity: 0; transform: var(--scale-95);
    }
  }
}

/* Enhanced styling for dropdown menus */
[data-controller="dropdown"] .popover {
  /* Border radius as per issue requirements */
  border-radius: var(--rounded-md);
  
  /* Border width as per issue requirements */
  border-width: var(--border);
  
  /* Box shadow as per issue requirements */
  box-shadow: var(--shadow-md);
  
  /* Ensure proper z-index above other elements */
  z-index: 1000;
  
  /* Minimum width for better usability */
  min-width: var(--popover-size, 8rem);
  
  /* Remove any default padding that might interfere */
  padding: 0;
  
  /* Ensure overflow is hidden to respect border radius */
  overflow: hidden;
}


/* ===== sheet.css ===== */
.sheet {
  background-color: var(--color-bg);
  border-inline-width: var(--sheet-border);
  box-shadow: var(--shadow-lg);
  block-size: var(--size-full);
  color: var(--color-text);
  inline-size: var(--sheet-size, var(--size-3-4));
  margin-inline: var(--sheet-margin);
  max-block-size: unset;
  overflow: hidden;

  &::backdrop {
    background-color: rgba(0, 0, 0, .8);
  }

  /* Entry animation. Deliberately no `forwards` fill: a persisting transform
     would make the dialog the containing block for position:fixed children
     (e.g. the datepicker's mobile sheet) and break their viewport anchoring. */
  &[open] {
    animation: sheetSlideIn 300ms ease-out;
  }

  &[open]::backdrop {
    animation: sheetFadeIn 200ms ease-out forwards;
  }

  /* Exit animation via [closing] attribute set by JS */
  &[closing] {
    pointer-events: none;
    animation: sheetSlideOut 300ms var(--ease-drawer) forwards;
  }

  &[closing]::backdrop {
    animation: sheetFadeOut 200ms ease-out forwards;
  }

  /* Swipe-down dismissal: the dialog itself follows the finger via inline
     transform (see dialog_controller.js), only the backdrop fade lives here. */
  &[swipe-closing] {
    pointer-events: none;
  }

  &[swipe-closing]::backdrop {
    animation: sheetFadeOut 200ms ease-out forwards;
  }

  @media (width >= 40rem) {
    max-inline-size: var(--sheet-size, var(--max-i-sm));
  }
}

@keyframes sheetSlideIn {
  from { transform: var(--sheet-transform); }
  to { transform: translateX(0); }
}

@keyframes sheetSlideOut {
  from { transform: translateX(0); }
  to { transform: var(--sheet-transform); }
}

@keyframes sheetFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes sheetFadeOut {
  from { opacity: 1; }
  to { opacity: 0; }
}

.sheet--left {
  --sheet-border: 0 1px;
  --sheet-margin: 0 auto;
  --sheet-transform: translateX(-100%);
}

.sheet--right {
  --sheet-border: 1px 0;
  --sheet-margin: auto 0;
  --sheet-transform: translateX(100%);
}

/* Below the mobile breakpoint, side sheets present as bottom sheets:
   full width, sliding up from the bottom edge with rounded top corners
   and a drag-handle indicator matching the popover bottom-sheet pattern.
   Add `sheet--mobile-side` to keep the side-sheet behavior on mobile
   (e.g. navigation drawers that should keep sliding in from the edge). */
@media (width < 40rem) {
  .sheet--left:not(.sheet--mobile-side),
  .sheet--right:not(.sheet--mobile-side) {
    --sheet-transform: translateY(100%);
    position: fixed;
    inset-block-end: 0;
    inset-block-start: auto;
    inset-inline: 0;
    margin: 0;
    inline-size: 100%;
    max-inline-size: 100%;
    block-size: auto;
    max-block-size: calc(100dvh - var(--size-6));
    border-radius: var(--rounded-xl) var(--rounded-xl) 0 0;
    border-block-start: 1px solid var(--color-border);
    border-inline: 0;
    border-block-end: 0;
  }

  .sheet--left:not(.sheet--mobile-side)::before,
  .sheet--right:not(.sheet--mobile-side)::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 0.25rem;
    background-color: var(--color-border);
    border-radius: 9999px;
    margin: 0.5rem auto 0;
  }

  .sheet--left:not(.sheet--mobile-side) .sheet__content,
  .sheet--right:not(.sheet--mobile-side) .sheet__content {
    padding-block-end: max(var(--size-6), env(safe-area-inset-bottom));
  }

  .sheet--left:not(.sheet--mobile-side) .sheet__close,
  .sheet--right:not(.sheet--mobile-side) .sheet__close {
    background: var(--color-border-light);
    border-radius: 9999px;
    inline-size: 2rem;
    block-size: 2rem;
    padding: 0;
    inset-block-start: var(--size-4);
  }
}

.sheet--bottom {
  --sheet-transform: translateY(100%);
  position: fixed;
  inset-block-end: 0;
  inset-block-start: auto;
  inset-inline: 0;
  margin: 0;
  inline-size: 100%;
  max-inline-size: 100%;
  block-size: calc(100dvh - var(--size-6));
  max-block-size: calc(100dvh - var(--size-6));
  border-radius: var(--rounded-lg) var(--rounded-lg) 0 0;
  border-block-start: 1px solid var(--color-border);
  border-inline: 0;
  border-block-end: 0;
}

@media (width >= 40rem) {
  .sheet--bottom {
    margin-inline: auto;
    max-inline-size: 32rem;
    max-block-size: 90dvh;
  }
}

/* Detented, keyboard-aware bottom sheet (see sheet_detent_controller.js). The
   controller sets the dialog's inline `height` per detent, so the dialog is a
   flex column: grabber on top, the scrollable content fills the rest. */
.sheet--detents {
  display: flex;
  flex-direction: column;
}

.sheet--detents > .sheet__content {
  flex: 1 1 auto;
  min-block-size: 0;
  block-size: auto;
}

/* Drag handle for the detent gesture. A generous (invisible) touch target with
   the visible pill drawn via ::before; touch-action:none lets the controller
   own the vertical drag instead of the page scrolling. */
.sheet__grabber {
  flex: 0 0 auto;
  height: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none;
  cursor: grab;
}

.sheet__grabber::before {
  content: "";
  width: 2.5rem;
  height: 0.3125rem;
  border-radius: 9999px;
  background-color: var(--color-border);
}

.issue-form-sheet__header {
  display: flex;
  align-items: center;
  gap: var(--size-3);
  padding: var(--size-3) var(--size-4);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg);
  position: sticky;
  inset-block-start: 0;
  z-index: 10;
}

.issue-form-sheet__title-block {
  flex: 1;
  min-inline-size: 0;
  text-align: start;
  line-height: 1.2;
}

.issue-form-sheet__title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
  color: var(--color-text);
}

.issue-form-sheet__subtitle {
  font-size: 0.75rem;
  color: var(--color-text-subtle, var(--color-text));
  opacity: 0.7;
  margin: 0;
}

.issue-form-sheet__close {
  flex-shrink: 0;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  border-radius: 9999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-border-light, transparent);
  border: 0;
  cursor: pointer;
}

.issue-form-sheet__close:hover {
  background: var(--color-border);
}

.issue-form-sheet__footer {
  display: flex;
  gap: var(--size-2);
  padding: var(--size-3) var(--size-4);
  border-top: 1px solid var(--color-border);
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}

.issue-form-sheet__footer::-webkit-scrollbar {
  display: none;
}

.sheet.issue-form-sheet > .sheet__content {
  padding: 0;
  display: flex;
  flex-direction: column;
}

.sheet.issue-form-sheet > .sheet__content > div {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-block-size: 0;
}

.issue-form-sheet__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--size-2) var(--size-2);
}

.issue-form-sheet input:focus,
.issue-form-sheet textarea:focus {
  background-color: var(--color-border-light) !important;
}

.sheet__content {
  block-size: var(--size-full);
  padding: var(--size-6);
  overflow-y: auto;
}

/* The close button sits inside a method="dialog" form (native no-JS close
   fallback); display:contents keeps the form out of the sheet's layout. */
.sheet__close-form {
  display: contents;
}

.sheet__close {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  border-radius: var(--rounded);
  display: flex;
  align-items: center;
  justify-content: center;
  inset-block-start: var(--size-3);
  inset-inline-end: var(--size-3);
  position: absolute;
  /* Own stacking context + compositing layer: without these, iOS Safari
     hit-tests taps on the button into the overlapping .sheet__content
     scroll layer — the X is visible but not tappable on real devices. */
  z-index: 20;
  transform: translateZ(0);
}

.sheet__close:hover {
  background: var(--color-border-light);
}

/* showModal() autofocuses the close button, which would draw the global
   .btn:focus-visible outline on every open (not just keyboard nav). Replace
   the outline with a subtle background fill so keyboard focus stays visible. */
.sheet__close:focus-visible {
  outline: none;
  background: var(--color-border-light);
}

.sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.5rem;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg);
  position: sticky;
  top: 0;
  z-index: 10;
}

.sheet__title {
  font-size: 1.25rem;
  font-weight: 600;
  margin: 0;
}

.sheet__body {
  padding: 1.5rem;
}

.sheet__content--flush {
  padding: 0;
  display: flex;
  flex-direction: column;

  > * {
    flex: 1;
    min-block-size: 0;
    display: flex;
    flex-direction: column;
  }
}

/* Like --flush (no padding, flex column so the content fills the sheet), but
   without the edge-to-edge corner/border reset that --flush triggers via
   :has(). For detented bottom sheets that keep their rounded top corners while
   still needing a scrolling, fill-height body (e.g. the global-search sheet). */
.sheet__content--fill {
  padding: 0;
  display: flex;
  flex-direction: column;

  > * {
    flex: 1;
    min-block-size: 0;
    display: flex;
    flex-direction: column;
  }
}

.sheet:has(.sheet__content--flush) {
  border-inline-width: 0;
  border-radius: 0;
}

/* Flush detail sheets unify their close button with the gallery image-picker
   "X": a plain 3rem circle that only fills on hover, with a larger stroke icon. */
.sheet:has(.sheet__content--flush) .sheet__close {
  background: transparent;
  border-radius: 9999px;
  width: 3rem;
  height: 3rem;
  z-index: 20;
  inset-block-start: 0.75rem;
  inset-inline-end: 0.75rem;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.sheet:has(.sheet__content--flush) .sheet__close:hover {
  background: var(--color-border-light);
}

.sheet:has(.sheet__content--flush) .sheet__close .sheet__close-icon {
  width: 1.75rem !important;
  height: 1.75rem !important;
}

/* Width modifiers only apply from the desktop breakpoint up — below it,
   side sheets render full-width as bottom sheets (see media block above). */
@media (width >= 40rem) {
  .sheet--wide {
    --sheet-size: 33.333%;
    max-inline-size: 33.333%;
    min-inline-size: 28rem;
  }

  .sheet--half {
    --sheet-size: 50%;
    max-inline-size: 50%;
    min-inline-size: 32rem;
  }

  .sheet--xl {
    --sheet-size: 75%;
    max-inline-size: 75%;
    min-inline-size: 40rem;
  }
}
/* Emoji picker mounted into a bottom sheet (EmojiPickerComponent, mobile).
   The emoji-mart web component defaults to a fixed 352px inline size, which
   leaves a gap on phones — stretch it to the full sheet width instead. */
.emoji-picker__sheet {
  padding-block-end: max(1rem, env(safe-area-inset-bottom));
}

.emoji-picker__sheet em-emoji-picker {
  width: 100%;
}


/* ===== bottom_nav.css ===== */
/* Safe-area: on devices with a home indicator (viewport-fit=cover) the bar
 * extends into the inset so the items sit above it instead of under it. */
.bottom-nav {
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/*
 * BottomNavComponent — FAB integration.
 *
 * Only the :center_notch FAB position needs custom CSS. The other two positions
 * (:right, :center_inline) are pure Tailwind utility composition.
 *
 * The notch carves a concave semicircle out of the top-center of the bar so a
 * docked FAB can sit half over the top edge with the page scrolling through the
 * cutout (classic docked-FAB pattern).
 *
 * The mask CANNOT sit on the <nav> itself — a mask clips the element AND all of
 * its children, so it would erase the FAB that lives inside the cutout. Instead
 * a ::before pseudo-element paints the bar surface (background + top border +
 * shadow) and carries the mask; the <nav> is made transparent, so its real
 * children (the items and the FAB) stay unmasked and paint above the carved
 * surface.
 */

.bottom-nav--notch {
  /* Radius of the carved semicircle. A touch larger than the 56px docked FAB
   * (28px radius) so an even ring of gap surrounds it. */
  --bottom-nav-notch-radius: 38px;

  background: transparent;
  border-top: 0;
  box-shadow: none;
}

.bottom-nav--notch::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;

  background: #fff;
  border-top: 1px solid var(--color-brand-neutral-200, #e5e7eb);
  box-shadow: 0 -4px 20px rgb(0 0 0 / 8%);

  -webkit-mask-image: radial-gradient(
    circle var(--bottom-nav-notch-radius) at 50% 0,
    transparent 0 var(--bottom-nav-notch-radius),
    #000 calc(var(--bottom-nav-notch-radius) + 0.5px)
  );
  mask-image: radial-gradient(
    circle var(--bottom-nav-notch-radius) at 50% 0,
    transparent 0 var(--bottom-nav-notch-radius),
    #000 calc(var(--bottom-nav-notch-radius) + 0.5px)
  );
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.dark .bottom-nav--notch::before {
  background: var(--color-brand-neutral-900, #171717);
  border-top-color: var(--color-brand-neutral-700, #404040);
}


/* ===== sidebar_menu.css ===== */
/* Sidebar layout: header top, sidebar left, main right */
.sidebar-layout {
  display: grid;
  /* The sidebar column is driven by --sidebar-width so collapse can animate it.
     Expanded defaults to 16rem; the collapsed rail overrides the token below. */
  grid-template-columns: var(--sidebar-width, 16rem) 1fr;
  grid-template-rows: auto 1fr;
  height: 100dvh;
  /* Collapse animates the grid column (a layout property, so every frame
     reflows the main column). This is a one-shot, non-interruptible transition,
     so the per-frame reflow cost is acceptable — same trade-off the host app's
     layouts.css makes. */
  transition: grid-template-columns 0.25s var(--ease-crisp);
}

.sidebar-layout > header {
  grid-column: 1 / -1;
  padding: var(--size-2) var(--size-4);
  background-color: var(--color-navbar-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 2px solid var(--color-navbar-border);
}

.sidebar-layout > .sidebar {
  grid-row: 2;
  grid-column: 1;
  inline-size: var(--sidebar-width, 16rem);
  border-right: var(--border) solid var(--color-border);
  overflow-y: auto;
  padding: var(--size-2);
  background-color: var(--color-sidebar-bg);
  /* Stays in lockstep with the .sidebar-layout grid-template-columns transition. */
  transition: inline-size 0.25s var(--ease-crisp);
}

.sidebar-layout > main {
  grid-row: 2;
  grid-column: 2;
  overflow-y: auto;
  padding: var(--size-4) var(--size-8);
  background-color: var(--color-main-bg);
}

/* Collapsed: the rail width becomes the sidebar column, driven by a token so
   both the grid column and the sidebar inline-size animate in lockstep.
   Defaults to the simple icon+label rail; the complex icon-only rail (see :has
   below) is narrower. */
.sidebar-layout--collapsed {
  --collapsed-rail-width: 5.75rem;
  --sidebar-width: var(--collapsed-rail-width);
}

.sidebar-layout--collapsed:has(.sidebar-menu__collapsed--complex),
.sidebar-layout--collapsed:has(.sidebar-menu__collapsed--accordion) {
  --collapsed-rail-width: 4.75rem;
}

/* Trim the side padding when collapsed so the rail labels get full width. */
#sidebar.sidebar--collapsed {
  padding-inline: var(--size-1);
}

/* The complex rail's flyouts and the accordion rail's tooltips escape the
   sidebar, so it must not clip them and has to stack above the main column. */
#sidebar.sidebar--collapsed:has(.sidebar-menu__collapsed--complex),
#sidebar.sidebar--collapsed:has(.sidebar-menu__collapsed--accordion) {
  overflow: visible;
  position: relative;
  z-index: 10;
}

/* Reduced motion: the collapse should snap to its new width instantly rather
   than sliding. Covers both the grid columns and the synced sidebar width. */
@media (prefers-reduced-motion: reduce) {
  .sidebar-layout,
  .sidebar-layout > .sidebar,
  .sidebar-menu__toggle-icon--close,
  .sidebar-menu__toggle-icon--open,
  .sidebar-menu--accordion .sidebar-section,
  .sidebar-menu--accordion .sidebar-section__wrap,
  .sidebar-rail__accordion,
  .sidebar-rail__acc-wrap {
    transition: none;
  }
}

@media (max-width: 768px) {
  .sidebar-layout {
    grid-template-columns: 1fr;
  }

  .sidebar-layout > .sidebar {
    display: none;
  }

  .sidebar-layout > main {
    grid-column: 1;
    padding: var(--size-4);
  }
}

.sidebar-menu {
  display: flex;
  flex-direction: column;
  row-gap: var(--size-4);
  block-size: var(--size-full);
}

#sidebar .sidebar-menu__collapsed {
  display: none;
  flex-direction: column;
}

#sidebar.sidebar--collapsed .sidebar-menu__collapsed {
  display: flex;
}


.sidebar--collapsed .sidebar-menu__full {
  display: none;
}

/* ---------- Collapsed rail (shared) ---------- */
.sidebar-menu__rail {
  display: flex;
  flex-direction: column;
  gap: var(--size-1);
}

.sidebar-menu__rail svg {
  stroke: currentColor;
  fill: none;
  flex-shrink: 0;
}

/* ---------- SIMPLE: icon + label, fully clickable ---------- */
.sidebar-rail__section {
  display: flex;
  flex-direction: column;
  gap: var(--size-1);
}

.sidebar-rail__section:not(:first-child) {
  margin-top: var(--size-1);
  padding-top: var(--size-2);
  border-top: var(--border) solid var(--color-border);
}

.sidebar-rail__link {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--size-1);
  padding: var(--size-2) var(--size-1);
  border-radius: var(--rounded-lg);
  color: var(--color-text);
  text-decoration: none;
  transition: background-color var(--time-200);
}

.sidebar-rail__link:hover {
  background-color: var(--color-selected);
}

.sidebar-rail__icon {
  display: inline-flex;
  line-height: 0;
}

.sidebar-rail__icon svg {
  width: var(--size-5);
  height: var(--size-5);
}

.sidebar-rail__label {
  max-width: 100%;
  font-size: 0.6875rem;
  font-weight: var(--font-medium);
  line-height: 1.2;
  text-align: center;
  overflow-wrap: anywhere;
  hyphens: none;
}

.sidebar-rail__link[aria-current="page"] {
  background-color: var(--color-selected-dark);
  color: var(--color-primary);
}

.sidebar-rail__link[aria-current="page"]::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block: var(--size-1);
  inline-size: 3px;
  border-radius: var(--rounded);
  background-color: var(--color-primary);
}

/* ---------- COMPLEX: icon rail + hover/focus flyout ---------- */
.sidebar-menu__collapsed--complex .sidebar-menu__rail {
  align-items: center;
  gap: var(--size-2);
}

.sidebar-rail__group {
  position: relative;
  display: flex;
  justify-content: center;
  width: var(--size-full);
}

.sidebar-rail__group:has(.sidebar-rail__trigger[aria-current="true"])::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block: var(--size-1);
  inline-size: 3px;
  border-radius: var(--rounded);
  background-color: var(--color-primary);
}

.sidebar-rail__trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--size-10);
  height: var(--size-10);
  border: 0;
  background: transparent;
  border-radius: var(--rounded-lg);
  color: var(--color-text);
  cursor: pointer;
  transition: background-color var(--time-200);
}

.sidebar-rail__trigger svg {
  width: var(--size-5);
  height: var(--size-5);
}

.sidebar-rail__trigger:hover,
.sidebar-rail__group:focus-within .sidebar-rail__trigger {
  background-color: var(--color-selected);
}

.sidebar-rail__trigger[aria-current="true"] {
  color: var(--color-primary);
  background-color: var(--color-selected-dark);
}

.sidebar-rail__flyout {
  position: absolute;
  inset-inline-start: 100%;
  top: 0;
  margin-inline-start: var(--size-2);
  min-width: 12rem;
  padding: var(--size-2);
  background-color: var(--color-bg);
  border: var(--border) solid var(--color-border);
  border-radius: var(--rounded-xl);
  box-shadow: var(--shadow-lg);
  display: none;
  z-index: 50;
}

.sidebar-rail__group:hover > .sidebar-rail__flyout,
.sidebar-rail__group:focus-within > .sidebar-rail__flyout {
  display: block;
}

.sidebar-rail__flyout-title {
  padding: var(--size-1) var(--size-2) var(--size-1_5);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-text-subtle);
}

.sidebar-rail__flyout-link {
  display: flex;
  align-items: center;
  gap: var(--size-2);
  padding: var(--size-1_5) var(--size-2);
  border-radius: var(--rounded-md);
  color: var(--color-text);
  text-decoration: none;
  transition: background-color var(--time-200);
}

.sidebar-rail__flyout-link:hover {
  background-color: var(--color-selected);
}

.sidebar-rail__flyout-link[aria-current="page"] {
  background-color: var(--color-selected-dark);
  color: var(--color-primary);
  font-weight: var(--font-medium);
}

.sidebar-rail__dot {
  flex: none;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: var(--color-text-subtle);
}

.sidebar-rail__flyout-link[aria-current="page"] .sidebar-rail__dot {
  background-color: var(--color-primary);
}

/* ---------- ACCORDION: icon rail with inline-expanding icon stacks ----------
   Each section is a native <details> pill: the summary holds the section icon,
   and the open state expands a vertical stack of the item icons beneath it
   under one shared rounded background. A shared name makes the groups mutually
   exclusive. Tooltips appear to the right on hover/focus. */
.sidebar-menu__collapsed--accordion .sidebar-menu__rail {
  align-items: center;
  gap: var(--size-1);
}

.sidebar-rail__accordion {
  position: relative;
  width: var(--size-full);
  border-radius: var(--rounded-xl);
  transition: background-color var(--time-300) var(--ease-crisp);
}

.sidebar-rail__accordion[open] {
  background-color: var(--color-selected);
  padding-block-end: var(--size-1);
}

/* Current-section indicator: a 3px bar on the leading edge, matching the
   simple and complex rails. */
.sidebar-rail__accordion[data-current="true"]::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block: var(--size-1);
  inline-size: 3px;
  border-radius: var(--rounded);
  background-color: var(--color-primary);
}

.sidebar-rail__acc-trigger {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--size-full);
  height: var(--size-10);
  border-radius: var(--rounded-lg);
  color: var(--color-text);
  cursor: pointer;
  list-style: none;
  transition: background-color var(--time-200);
}

.sidebar-rail__acc-trigger::-webkit-details-marker {
  display: none;
}

.sidebar-rail__acc-trigger svg {
  width: var(--size-5);
  height: var(--size-5);
}

.sidebar-rail__acc-trigger:hover,
.sidebar-rail__acc-trigger:focus-visible {
  background-color: var(--color-selected);
}

/* Height + opacity animation of the item stack via the grid 0fr -> 1fr trick. */
.sidebar-rail__accordion::details-content {
  content-visibility: visible;
}

.sidebar-rail__acc-wrap {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--time-300) var(--ease-crisp),
    opacity var(--time-200) ease;
}

.sidebar-rail__accordion[open] .sidebar-rail__acc-wrap {
  grid-template-rows: 1fr;
  opacity: 1;
}

.sidebar-rail__acc-items {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--size-1);
  min-block-size: 0;
  overflow: hidden;
}

.sidebar-rail__acc-link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--size-10);
  height: var(--size-10);
  border-radius: var(--rounded-lg);
  color: var(--color-text);
  text-decoration: none;
  transition: background-color var(--time-200);
}

.sidebar-rail__acc-link svg {
  width: var(--size-5);
  height: var(--size-5);
}

.sidebar-rail__acc-link:hover,
.sidebar-rail__acc-link:focus-visible {
  background-color: var(--color-selected);
}

.sidebar-rail__acc-link[aria-current="page"] {
  background-color: var(--color-selected-dark);
  color: var(--color-primary);
}

/* CSS-only tooltip: a dark pill to the right of the icon, shown on hover/focus.
   Positioned outside the rail, which is why the sidebar runs overflow: visible
   in this mode. */
.sidebar-rail__acc-trigger[data-tooltip]::after,
.sidebar-rail__acc-link[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  inset-inline-start: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-inline-start: var(--size-2);
  padding: var(--size-1) var(--size-2);
  white-space: nowrap;
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  color: var(--color-white);
  background-color: var(--color-neutral-900);
  border-radius: var(--rounded-md);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 50;
  transition: opacity var(--time-150) ease;
}

.sidebar-rail__acc-trigger:hover::after,
.sidebar-rail__acc-trigger:focus-visible::after,
.sidebar-rail__acc-link:hover::after,
.sidebar-rail__acc-link:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

.sidebar-menu__toggle {
  --btn-background: transparent;
  --btn-border-color: transparent;
  --btn-box-shadow: none;
  --btn-hover-color: var(--color-selected);
  --btn-padding: var(--size-1);
  --btn-outline-size: 0;
  border-radius: var(--radius-2);
  
  &:focus-visible {
    --btn-background: var(--color-secondary);
  }
  
  &:hover {
    --btn-background: var(--color-selected-dark);
  }
}

/* The two toggle icons are stacked in a single grid cell so both stay in flow
   and can cross-fade. A `display:none` swap (the previous approach) is not
   animatable and keyed the swap on `.sidebar--collapsed` (the <aside>), which
   never contains the header toggle — so it never matched. The collapsed state
   is keyed on `.sidebar-layout--collapsed` (wraps the header); `.sidebar--collapsed`
   is kept via :is() so a consumer placing the toggle inside #sidebar still works.
   Both classes are set synchronously by sidebar_collapse_controller.js. */
.sidebar-menu__toggle-icon {
  display: grid;
  place-items: center;
  color: var(--color-text-subtle);
}

.sidebar-menu__toggle-icon--close,
.sidebar-menu__toggle-icon--open {
  grid-area: 1 / 1;
  transition: transform var(--time-300) ease, opacity var(--time-300) ease,
    visibility var(--time-300) ease;
}

.sidebar-menu__toggle-icon--close {
  opacity: 1;
  visibility: visible;
  transform: rotate(0deg);
}

.sidebar-menu__toggle-icon--open {
  opacity: 0;
  visibility: hidden;
  transform: rotate(-180deg);
}

:is(.sidebar-layout--collapsed, .sidebar--collapsed) .sidebar-menu__toggle-icon--close {
  opacity: 0;
  visibility: hidden;
  transform: rotate(180deg);
}

:is(.sidebar-layout--collapsed, .sidebar--collapsed) .sidebar-menu__toggle-icon--open {
  opacity: 1;
  visibility: visible;
  transform: rotate(0deg);
}

.sidebar-menu__button {
  --btn-background: transparent;
  --btn-border-color: transparent;
  --btn-box-shadow: none;
  --btn-font-weight: var(--font-normal);
  --btn-hover-color: var(--color-selected);
  --btn-justify-content: start;
  --btn-outline-size: 0;
  --btn-inline-size: var(--size-full);
  --btn-padding: var(--size-1) var(--size-2);
  --btn-text-align: start;

  &[aria-current=page] {
    --btn-background: var(--color-selected-dark);
  }

  &:focus-visible {
    --btn-background: var(--color-secondary);
  }

  &:is(summary) {
    &::after {
      background-image: url("/assets/chevron-right-63c462ab.svg");
      background-size: cover;
      block-size: var(--size-4);
      content: "";
      filter: var(--color-filter-text);
      inline-size: var(--size-4);
      margin-inline-start: auto;
      min-inline-size: var(--size-4);
      transition: transform var(--time-200);
    }

    details[open] > &::after {
      transform: var(--rotate-90);
    }

    &::-webkit-details-marker a{
      display: none;
    }
  }
}

.sidebar-menu__content {
  display: flex;
  flex-direction: column;
  row-gap: var(--size-4);
  overflow-y: scroll;
}

.sidebar-menu__group {
  display: flex;
  flex-direction: column;
}

.sidebar-menu__group-label {
  color: var(--color-text-subtle);
  font-size: var(--text-xs);
  font-weight: var(--font-medium);
  padding: var(--size-1_5) var(--size-2);
}

.sidebar-menu__items {
  display: flex;
  flex-direction: column;
  row-gap: var(--size-1);
}

.sidebar-menu__sub {
  border-inline-start-width: var(--border);
  display: flex;
  flex-direction: column;
  margin-inline-start: var(--size-4);
  padding: var(--size-0_5) var(--size-2);
  row-gap: var(--size-1);
}

.sidebar-menu li:last-child .menu-link {
  border-bottom: none !important;
}

/* Mobile menu specific styles */
.mobile-menu .sidebar-menu__collapsed {
  display: none !important;
}

/* ---------- Accordion mode (opt-in exclusive accordion) ----------
   The open section reads as one tinted card enclosing its header and items,
   its height animates via the grid 0fr -> 1fr trick, and the indentation line
   gives way to the card. Scoped to .sidebar-menu--accordion so the default
   layout stays byte-identical. */
.sidebar-menu--accordion .sidebar-section {
  border-radius: var(--rounded-xl);
  transition: background-color var(--time-300) var(--ease-crisp);
}

.sidebar-menu--accordion .sidebar-section[open] {
  background-color: var(--color-selected);
  padding-block-end: var(--size-1);
}

.sidebar-menu--accordion .sidebar-menu__sub {
  border-inline-start-width: 0;
  margin-inline-start: var(--size-2);
}

.sidebar-menu--accordion .sidebar-section::details-content {
  content-visibility: visible;
}

.sidebar-menu--accordion .sidebar-section__wrap {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--time-300) var(--ease-crisp),
    opacity var(--time-200) ease;
}

.sidebar-menu--accordion .sidebar-section[open] .sidebar-section__wrap {
  grid-template-rows: 1fr;
  opacity: 1;
}

.sidebar-menu--accordion .sidebar-section__inner {
  min-block-size: 0;
  overflow: hidden;
}

/* Keep the active child readable on the tinted card. */
.sidebar-menu--accordion .sidebar-section[open] .sidebar-menu__button[aria-current="page"] {
  --btn-background: var(--color-selected-dark);
}

/* Trailing external-link arrow (additive, every mode). */
.sidebar-menu__external-icon {
  margin-inline-start: var(--size-1);
  color: var(--color-text-subtle);
}


/* ===== skeleton.css ===== */
/*
 * Skeleton loading placeholders (SkeletonComponent / SkeletonTableComponent).
 *
 * .gui-skeleton          — bone base color, themed via --color-secondary so it
 *                          follows light/dark automatically.
 * .gui-skeleton--shimmer — moving highlight sweep instead of the default pulse.
 * .gui-skeleton-reveal   — delayed-reveal wrapper: content stays invisible for
 *                          --gui-skeleton-delay (default 400ms), so responses
 *                          faster than the delay never flash a skeleton.
 * .gui-skeleton-swap     — cross-fade reveal (SkeletonSwapComponent): a skeleton
 *                          layer and a content layer stacked in one slot; on
 *                          .is-revealed the skeleton fades out + blurs while the
 *                          content fades in + un-blurs over one shared 400ms
 *                          transition, so the swap reads as a single motion.
 */
@layer components.base {
  .gui-skeleton {
    background-color: var(--color-secondary);
  }

  .gui-skeleton--shimmer {
    background-image: linear-gradient(
      100deg,
      transparent 30%,
      color-mix(in srgb, var(--color-secondary) 45%, #fff) 50%,
      transparent 70%
    );
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: gui-skeleton-shimmer 1.4s ease-in-out infinite;
  }

  @keyframes gui-skeleton-shimmer {
    from { background-position: 200% 0; }
    to { background-position: -200% 0; }
  }

  .gui-skeleton-reveal {
    opacity: 0;
    animation: gui-skeleton-appear 0s linear var(--gui-skeleton-delay, 400ms) forwards;
  }

  @keyframes gui-skeleton-appear {
    to { opacity: 1; }
  }

  /*
   * Cross-fade reveal (SkeletonSwapComponent). Content layer sits in the normal
   * flow and defines the wrapper height; the skeleton layer is absolutely
   * positioned over it. So the wrapper never collapses while the content slot is
   * still empty, callers set a min-height on the wrapper for that loading state.
   */
  .gui-skeleton-swap {
    position: relative;
    --gui-skel-pulse-dur: 1000ms;
    --gui-skel-pulse-min: 0.5;
    --gui-skel-pulse-count: infinite;
    --gui-skel-reveal-dur: 400ms;
    --gui-skel-reveal-blur: 2px;
    --gui-skel-reveal-ease: ease-in-out;
  }

  .gui-skeleton-swap__skeleton {
    position: absolute;
    inset: 0;
    opacity: 1;
    filter: blur(0);
    transition:
      opacity var(--gui-skel-reveal-dur) var(--gui-skel-reveal-ease),
      filter var(--gui-skel-reveal-dur) var(--gui-skel-reveal-ease);
  }

  .gui-skeleton-swap__content {
    opacity: 0;
    filter: blur(var(--gui-skel-reveal-blur));
    transition:
      opacity var(--gui-skel-reveal-dur) var(--gui-skel-reveal-ease),
      filter var(--gui-skel-reveal-dur) var(--gui-skel-reveal-ease);
  }

  .gui-skeleton-swap.is-revealed .gui-skeleton-swap__skeleton {
    opacity: 0;
    filter: blur(var(--gui-skel-reveal-blur));
    pointer-events: none;
  }

  .gui-skeleton-swap.is-revealed .gui-skeleton-swap__content {
    opacity: 1;
    filter: blur(0);
  }

  /* Replay without a reverse animation: transitions are killed while the
     wrapper is reset to the loading state, then re-enabled after a reflow. */
  .gui-skeleton-swap.is-resetting .gui-skeleton-swap__skeleton,
  .gui-skeleton-swap.is-resetting .gui-skeleton-swap__content {
    transition: none !important;
  }

  /* Pulse lives on the skeleton layer's children so the layer's own opacity and
     filter stay free for the reveal transition. */
  .gui-skeleton-swap__skeleton.is-pulsing > * {
    animation: gui-skeleton-swap-pulse var(--gui-skel-pulse-dur) ease-in-out var(--gui-skel-pulse-count);
  }

  @keyframes gui-skeleton-swap-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: var(--gui-skel-pulse-min); }
  }

  /* The reveal delay is visibility gating, not decorative motion — keep it. */
  @media (prefers-reduced-motion: reduce) {
    .gui-skeleton--shimmer {
      animation: none;
    }

    .gui-skeleton-swap__skeleton,
    .gui-skeleton-swap__content {
      transition: none;
    }

    .gui-skeleton-swap__skeleton.is-pulsing > * {
      animation: none;
    }
  }
}


/* ===== table.css ===== */
:where(.table) {
  caption-side: bottom;
  font-size: var(--text-sm);
  inline-size: var(--size-full);
  caption {
    color: var(--color-text-subtle);
    margin-block-start: var(--size-4);
  }
  thead {
    color: var(--color-text-subtle);
  }
  tbody tr {
    border-block-start-width: var(--border);
  }
  tr:hover {
    background-color: rgb(from var(--color-border-light) r g b / .5);
  }
  th {
    font-weight: var(--font-medium);
    text-align: start;
  }
  th, td {
    padding: var(--size-2);
  }
  tfoot {
    background-color: rgb(from var(--color-border-light) r g b / .5);
    border-block-start-width: var(--border);
    font-weight: var(--font-medium);
  }
}

/* Zebra stripe table variant */
:where(.table--zebra) {
  tbody tr:nth-child(odd) {
    background-color: rgb(from var(--color-border-light) r g b / .3);
  }
  tbody tr:hover {
    background-color: rgb(from var(--color-border-light) r g b / .7);
  }
}

/* ===== tables.css ===== */
/* App-level overrides for the gui_components `.table`. Imported after the gem's
 * table.css so these win. */

/* Compact first column — sized for the bulk-select checkbox that tables render
 * as their first cell. */
.table th:first-child,
.table td:first-child {
  width: 2.5rem;
  padding-inline: 0.5rem;
}


/* ===== table-of-contents.css ===== */
/* Fix anchor link scroll offset for headers */
html {
  scroll-padding-top: 6rem;
}

/* Table of Contents Styles */
.table-of-contents {
  display: none;
}

/* Only show on medium screens and up */
@media (min-width: 1024px) {
  .table-of-contents {
    display: block;
    position: sticky;
    top: 2rem;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    padding: 1rem;
  }
}

.table-of-contents__heading {
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--neutral-600);
  margin-bottom: 1rem;
}

.table-of-contents__list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  border-left: 1px solid var(--neutral-200);
  list-style: none;
  padding: 0;
  margin: 0;
}

.table-of-contents__item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.table-of-contents__sublist {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  list-style: none;
  padding: 0;
  margin: 0;
  margin-top: 0.25rem;
}

.table-of-contents__subitem {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.table-of-contents__link {
  display: block;
  width: 100%;
  border-left: 2px solid transparent;
  padding-left: 1.25rem;
  font-size: 0.875rem;
  line-height: 1.5rem;
  color: var(--neutral-500);
  text-decoration: none;
  transition: border-left-color 0.2s ease, color 0.2s ease;
  margin-left: -1px;
}

.table-of-contents__link--h2 {
  padding-left: 1.25rem;
  font-weight: 500;
  color: var(--neutral-700);
}

.table-of-contents__link--h3 {
  padding-left: 2rem;
  font-size: 0.8125rem;
  color: var(--neutral-500);
}

.table-of-contents__link:hover {
  border-left-color: var(--neutral-300);
  color: var(--neutral-800);
}

.table-of-contents__link--active {
  border-left-color: var(--neutral-800);
  color: var(--neutral-900);
  font-weight: 600;
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
  .table-of-contents__list {
    border-left-color: var(--neutral-700);
  }

  .table-of-contents__link {
    color: var(--neutral-400);
  }

  .table-of-contents__link--h2 {
    color: var(--neutral-200);
  }

  .table-of-contents__link--h3 {
    color: var(--neutral-400);
  }

  .table-of-contents__link:hover {
    border-left-color: var(--neutral-400);
    color: var(--neutral-100);
  }

  .table-of-contents__link--active {
    border-left-color: var(--neutral-100);
    color: white;
  }
}

/* Dark mode support (attribute toggle) */
[data-color-scheme="dark"] .table-of-contents__list {
  border-left-color: var(--neutral-700);
}

[data-color-scheme="dark"] .table-of-contents__link {
  color: var(--neutral-400);
}

[data-color-scheme="dark"] .table-of-contents__link--h2 {
  color: var(--neutral-200);
}

[data-color-scheme="dark"] .table-of-contents__link--h3 {
  color: var(--neutral-400);
}

[data-color-scheme="dark"] .table-of-contents__link:hover {
  border-left-color: var(--neutral-400);
  color: var(--neutral-100);
}

[data-color-scheme="dark"] .table-of-contents__link--active {
  border-left-color: var(--neutral-100);
  color: white;
}

/* Layout for docs with table of contents */
.docs-with-toc {
  display: flex;
  gap: 2rem;
  max-width: 100%;
}

.docs-with-toc__content {
  flex: 1;
  min-width: 0;
}

.docs-with-toc__sidebar {
  display: none;
}

@media (min-width: 1024px) {
  .docs-with-toc__sidebar {
    display: block;
    width: 250px;
    flex-shrink: 0;
  }
}

/* =============================================================================
   CSS-ONLY TABLE OF CONTENTS (scroll-target-group / :target-current)
   Browser support: Chrome 135+
   ============================================================================= */

/* Container layout for CSS-based TOC pages */
.docs-with-css-toc {
  display: flex;
  gap: 2rem;
  max-width: 100%;
}

.docs-with-css-toc__content {
  flex: 1;
  min-width: 0;
}

.docs-with-css-toc__sidebar {
  display: none;
}

@media (min-width: 1024px) {
  .docs-with-css-toc__sidebar {
    display: block;
    width: 250px;
    flex-shrink: 0;
  }
}

/* CSS Table of Contents base styles */
.css-toc {
  display: none;
}

@media (min-width: 1024px) {
  .css-toc {
    display: block;
    position: sticky;
    top: 2rem;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    padding: 1rem;
  }
}

.css-toc__heading {
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--neutral-600);
  margin-bottom: 1rem;
}

/* TOC list with scroll-target-group for automatic active link detection */
.css-toc__list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  border-left: 1px solid var(--neutral-200);
  list-style: none;
  padding: 0;
  margin: 0;
  scroll-target-group: auto;
}

.css-toc__item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.css-toc__item--nested {
  /* Nested items (h3) have additional indentation handled by link */
}

/* TOC link base styles */
.css-toc__link {
  display: block;
  width: 100%;
  border-left: 2px solid transparent;
  font-size: 0.875rem;
  line-height: 1.5rem;
  color: var(--neutral-500);
  text-decoration: none;
  transition: border-left-color 0.2s ease, color 0.2s ease;
  margin-left: -1px;
}

.css-toc__link--h2 {
  padding-left: 1.25rem;
  font-weight: 500;
  color: var(--neutral-700);
}

.css-toc__link--h3 {
  padding-left: 2rem;
  font-size: 0.8125rem;
  color: var(--neutral-500);
}

/* Hover state */
.css-toc__link:hover {
  border-left-color: var(--neutral-300);
  color: var(--neutral-800);
}

/* Active state via CSS scroll tracking (Chrome 135+) */
@supports selector(:target-current) {
  .css-toc__link:target-current {
    border-left-color: var(--color-primary-600, #3b82f6);
    color: var(--color-primary-700, #1d4ed8);
    font-weight: 600;
  }
}

/* Fallback: active state when a TOC link's target is in the URL hash */
@supports not selector(:target-current) {
  .css-toc__link:target {
    border-left-color: var(--color-primary-600, #3b82f6);
    color: var(--color-primary-700, #1d4ed8);
    font-weight: 600;
  }
}

/* Active state driven by JS fallback controller */
.css-toc__link--active {
  border-left-color: var(--color-primary-600, #3b82f6);
  color: var(--color-primary-700, #1d4ed8);
  font-weight: 600;
}

/* Scroll margin for targeted sections */
.docs-with-css-toc__content h2,
.docs-with-css-toc__content h3 {
  scroll-margin-top: 6rem;
}

/* Dark mode support for CSS-only TOC */
@media (prefers-color-scheme: dark) {
  .css-toc__list {
    border-left-color: var(--neutral-700);
  }

  .css-toc__link {
    color: var(--neutral-400);
  }

  .css-toc__link--h2 {
    color: var(--neutral-200);
  }

  .css-toc__link--h3 {
    color: var(--neutral-400);
  }

  .css-toc__link:hover {
    border-left-color: var(--neutral-400);
    color: var(--neutral-100);
  }

  .css-toc__link:target-current {
    border-left-color: var(--color-primary-400, #60a5fa);
    color: var(--color-primary-300, #93c5fd);
  }

  .css-toc__link:target {
    border-left-color: var(--color-primary-400, #60a5fa);
    color: var(--color-primary-300, #93c5fd);
  }

  .css-toc__link--active {
    border-left-color: var(--color-primary-400, #60a5fa);
    color: var(--color-primary-300, #93c5fd);
  }
}

/* Dark mode support for CSS-only TOC (attribute toggle) */
[data-color-scheme="dark"] .css-toc__list {
  border-left-color: var(--neutral-700);
}

[data-color-scheme="dark"] .css-toc__link {
  color: var(--neutral-400);
}

[data-color-scheme="dark"] .css-toc__link--h2 {
  color: var(--neutral-200);
}

[data-color-scheme="dark"] .css-toc__link--h3 {
  color: var(--neutral-400);
}

[data-color-scheme="dark"] .css-toc__link:hover {
  border-left-color: var(--neutral-400);
  color: var(--neutral-100);
}

[data-color-scheme="dark"] .css-toc__link:target-current {
  border-left-color: var(--color-primary-400, #60a5fa);
  color: var(--color-primary-300, #93c5fd);
}

[data-color-scheme="dark"] .css-toc__link:target {
  border-left-color: var(--color-primary-400, #60a5fa);
  color: var(--color-primary-300, #93c5fd);
}

[data-color-scheme="dark"] .css-toc__link--active {
  border-left-color: var(--color-primary-400, #60a5fa);
  color: var(--color-primary-300, #93c5fd);
}

/* ===== tabs.css ===== */
/* Vertical orientation modifier */
.tabs--vertical {
  display: flex;
  flex-direction: column;
  gap: var(--size-4);
}

@media (min-width: 640px) {
  .tabs--vertical {
    flex-direction: row;
    gap: var(--size-4);
  }
}

.tabs--vertical [data-tabs-target="tabList"] {
  width: 100%;
}

@media (min-width: 640px) {
  .tabs--vertical [data-tabs-target="tabList"] {
    width: 12rem;
    min-width: 12rem;
    flex-shrink: 0;
  }
}

.tabs--vertical [data-tabs-target="panel"] {
  flex: 1;
  min-width: 0;
}

/* Panel reveal on tab change. The active-pill slide is a live CSS transition,
 * so the panel swap must not run inside a view transition (its snapshot would
 * cover the pill). A short opacity keyframe replaces the former root crossfade. */
@keyframes gc-tab-panel-enter {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.gc-tab-panel-enter {
  animation: gc-tab-panel-enter 200ms cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  .gc-tab-panel-enter {
    animation: none;
  }
}


/* ===== segmented_control.css ===== */
@layer components.base {
  /*
   * Lightweight pill-style toggle. Renders a row of buttons within a rounded
   * track; the active button gets a raised "card" treatment via inline
   * background + shadow. Companion to SegmentedControlComponent.
   *
   * Most styling is handled by Tailwind utilities on the component itself
   * (see segmented_control_component.rb). This file only adds two things
   * that don't translate cleanly to utilities:
   *   1) A focus-visible ring so keyboard nav stays accessible.
   *   2) A subtle transition on the active state so toggles feel smooth.
   */

  :where(.segmented-control) {
    user-select: none;
    -webkit-user-select: none;
  }

  :where(.segmented-control__segment) {
    transition:
      background-color 0.12s ease,
      color 0.12s ease,
      box-shadow 0.12s ease;
  }

  :where(.segmented-control__segment):focus-visible {
    outline: 2px solid var(--color-primary-500, #2855ff);
    outline-offset: 2px;
  }
}


/* ===== indicator_pill.css ===== */
@layer components.base {
  /*
   * Sliding pill indicator shared by TabComponent (:pill/:default) and
   * SegmentedControlComponent (button mode). An absolutely positioned,
   * aria-hidden element that JS (lib/indicator_pill.js) moves under the active
   * option via inline transform + width/height. The raised "card" surface
   * (background + shadow) lives on this element instead of the buttons, so it
   * travels with the pill; the options sit on z-index 1 above it, transparent.
   *
   * Motion tokens are overridable per instance; reduced-motion users get an
   * instant snap. The pill's own background/rounding is supplied by the
   * component as utility classes (theme tokens — no hard-coded colours here).
   */
  :where(.gc-indicator-pill) {
    --gc-indicator-dur: 250ms;
    --gc-indicator-ease: cubic-bezier(0.22, 1, 0.36, 1);
    position: absolute;
    top: 0;
    left: 0;
    z-index: 0;
    pointer-events: none;
    transform: translate(0, 0);
    will-change: transform, width, height;
    transition:
      transform var(--gc-indicator-dur) var(--gc-indicator-ease),
      width var(--gc-indicator-dur) var(--gc-indicator-ease),
      height var(--gc-indicator-dur) var(--gc-indicator-ease);
  }

  @media (prefers-reduced-motion: reduce) {
    :where(.gc-indicator-pill) {
      transition: none;
    }
  }

  /*
   * Underline surface (PillNav :underline). The indicator's box still matches
   * the full active link (measured by lib/indicator_pill.js), but instead of a
   * raised card it paints only a bottom edge that sits on the track's border.
   * Thickness and colour come from CSS variables so a consumer can override
   * them per breakpoint (e.g. a thicker primary underline on mobile) without
   * fighting the unlayered `* { border-color }` rule. Kept at :where()
   * specificity so an app class setting the variables always wins.
   */
  :where(.gc-indicator-pill--underline) {
    --gc-underline-size: 2px;
    --gc-underline-color: var(--brand-neutral-800);
    background: transparent;
    box-shadow: none;
    border-radius: 0;
  }

  :where([data-color-scheme="dark"] .gc-indicator-pill--underline) {
    --gc-underline-color: var(--brand-neutral-100);
  }

  .gc-indicator-pill--underline::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: var(--gc-underline-size);
    background: var(--gc-underline-color);
  }
}


/* ===== search_input.css ===== */
/* SearchInputComponent — round in-field clear button + Transitions.dev
 * "input clear with dissolve" animation.
 *
 * The wrapper (.t-clear) intentionally has no border/radius/padding so a
 * consumer can style it like a plain input field. All motion tunables live as
 * CSS custom properties and are read back by search_clear_controller.js via
 * getComputedStyle, so timings/distances can be tuned here without touching JS.
 */

.t-clear {
  /* ---- motion tunables (also read by the Stimulus controller) ----
   * Values mirror the Transitions.dev reference: a short, subtle streak
   * that flashes early (peak at 15%) instead of a heavy smudge. */
  --clear-out-dur: 400ms;   /* per-word fly-away duration */
  --clear-in-dur: 400ms;    /* placeholder ease-in duration */
  --clear-out-fly: 12px;    /* how far words rise */
  --clear-in-fly: 12px;     /* how far the placeholder rises while easing in */
  --clear-blur: 2px;        /* max blur applied to a departing word */
  --glow-delay: 50ms;       /* per-word stagger */
  --glow-peak-at: 0.15;     /* envelope peak position (0..1) */
  --glow-opacity: 0.42;
  --glow-spread: 1.5;       /* streak size as a ratio of the word's own box */
  --clear-glow-color: var(--brand-neutral-400, #9ca3af);

  position: relative;
  overflow: hidden;
  isolation: isolate;
}

/* Real input sits above the mirror/placeholder/glow layers. Caret must stay
 * visible even while the controller makes the text transparent for the clear. */
.t-clear-input {
  position: relative;
  z-index: 3;
  width: 100%;
  caret-color: var(--color-text);
}

/* Suppress the native WebKit/Blink cancel button + decoration so no second,
 * dark ✕ renders inside the field. */
.t-clear-input::-webkit-search-cancel-button,
.t-clear-input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
  display: none;
}

/* Word mirror — empty at rest, filled by the controller during the clear. The
 * controller copies the input's computed font metrics and text offset onto it
 * so the departing words sit exactly on the real glyphs; whitespace between
 * words is preserved as text nodes (white-space: pre keeps its width). */
.t-clear-mirror {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  padding: inherit;
  font: inherit;
  color: var(--color-text);
  white-space: pre;
  overflow: hidden;
  pointer-events: none;
}

.t-clear-word {
  display: inline-block;
  will-change: transform, opacity, filter;
}

/* Fake placeholder used only for the soft ease-in after a clear. Invisible at
 * rest (the native placeholder handles the empty state). */
.t-clear-placeholder {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  padding: inherit;
  font: inherit;
  color: var(--color-text-subtle, var(--brand-neutral-500, #6b7280));
  white-space: nowrap;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity var(--clear-in-dur) var(--ease-out-soft, cubic-bezier(0.16, 1, 0.3, 1)),
    transform var(--clear-in-dur) var(--ease-out-soft, cubic-bezier(0.16, 1, 0.3, 1));
}

/* While the dissolve plays (and until the fake placeholder has handed over),
 * the emptied input's native placeholder must not pop in underneath the
 * departing words — that double-render is exactly the overlap glitch. */
.t-clear.is-clearing .t-clear-input::placeholder {
  color: transparent;
  opacity: 0;
}

/* Per-word glow streak layer. multiply reads best over a light field; the dark
 * scheme switches to screen so the streaks lighten instead of darken. */
.t-clear-glow {
  position: absolute;
  inset: 0;
  z-index: 4;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: multiply;
}

[data-color-scheme="dark"] .t-clear-glow {
  mix-blend-mode: screen;
  --clear-glow-color: var(--brand-neutral-300, #d1d5db);
}

/* Round, filled neutral clear button, right-aligned inside the field. Only
 * shown while the wrapper carries .has-value. */
.t-clear-btn {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0.5rem;
  z-index: 5;
  margin-block: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border: 0;
  border-radius: 9999px;
  color: #fff;
  background-color: var(--brand-neutral-400, #9ca3af);
  cursor: pointer;
  opacity: 0;
  transform: scale(0.8);
  transition:
    opacity 150ms var(--ease-out-soft, cubic-bezier(0.16, 1, 0.3, 1)),
    transform 150ms var(--ease-out-soft, cubic-bezier(0.16, 1, 0.3, 1)),
    background-color 150ms ease;
  pointer-events: none;
}

.t-clear-btn:hover {
  background-color: var(--brand-neutral-500, #6b7280);
}

.t-clear-btn:focus-visible {
  outline: 2px solid var(--color-focus-ring, var(--brand-neutral-500, #6b7280));
  outline-offset: 2px;
}

.t-clear.has-value .t-clear-btn {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

/* Reduced motion: no fly-away, no glow, no placeholder fade — the controller
 * clears synchronously and this keeps the glow permanently hidden. */
@media (prefers-reduced-motion: reduce) {
  .t-clear-glow {
    display: none;
  }

  .t-clear-placeholder,
  .t-clear-btn {
    transition: none;
  }
}


/* ===== text_swap.css ===== */
/*
 * Text Swap (transitions.dev pattern) — driven by text_swap_controller.js.
 * The exit state lifts the text up with blur + fade; the enter-start state
 * seeds the incoming text below the baseline WITHOUT a transition so removing
 * the class animates it back to rest. Timing values are the component defaults
 * (kept in sync with the controller's `duration` value, 150ms).
 */

:root {
  --text-swap-dur: 150ms;
  --text-swap-translate-y: 4px;
  --text-swap-blur: 2px;
  --text-swap-ease: ease-in-out;
}

.gui-text-swap {
  display: inline-block;
  transform: translateY(0);
  filter: blur(0);
  opacity: 1;
  transition: transform var(--text-swap-dur) var(--text-swap-ease),
              filter var(--text-swap-dur) var(--text-swap-ease),
              opacity var(--text-swap-dur) var(--text-swap-ease);
  will-change: transform, filter, opacity;
}

.gui-text-swap.is-exit {
  transform: translateY(calc(var(--text-swap-translate-y) * -1));
  filter: blur(var(--text-swap-blur));
  opacity: 0;
}

.gui-text-swap.is-enter-start {
  transform: translateY(var(--text-swap-translate-y));
  filter: blur(var(--text-swap-blur));
  opacity: 0;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .gui-text-swap {
    transition: none !important;
  }
}


/* ===== liquid_chip.css ===== */
@layer components.base {
  /*
   * Companion CSS for LiquidChipComponent. Most styling is done via Tailwind
   * utilities on the component itself — this file only contains the leading
   * dot for required variants and the focus ring, both of which are easier
   * to express in plain CSS than as utility classes.
   */

  :where(.liquid-chip) {
    user-select: none;
    -webkit-user-select: none;
  }

  :where(.liquid-chip__dot) {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 9999px;
    background-color: var(--color-red-500, #ef4444);
    flex: none;
  }

  :where(.liquid-chip__used) {
    color: var(--color-green-600, #16a34a);
    font-size: 0.7rem;
    line-height: 1;
  }

  :where(.liquid-chip):focus-visible {
    outline: 2px solid var(--color-primary-500, #2855ff);
    outline-offset: 2px;
  }
}


/* ===== like_button.css ===== */
/* Like button — heart toggle with a particle burst.
 *
 * Ported from the customer gallery's grid heart (.pg-like). Unlike that
 * overlay heart, this is a standalone inline button that also shows an
 * optional count, so the heart is always visible (no hover-reveal). The
 * burst/pop keyframes and the per-particle CSS vars (--px/--py/--pdur/
 * --pdelay/--p-end-scale/--psize, set from like_button_controller.js) are
 * kept identical to the original spray. */

.ui-like {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-subtle, oklch(0.55 0.02 260));
  cursor: pointer;
  line-height: 1;
  isolation: isolate;
  -webkit-tap-highlight-color: transparent;
}

.ui-like:focus-visible {
  outline: 2px solid var(--color-primary, oklch(0.62 0.19 265));
  outline-offset: 3px;
  border-radius: 999px;
}

/* Size modifiers scale the icon and count together. */
.ui-like--sm { font-size: 0.8125rem; }
.ui-like--md { font-size: 0.9375rem; }
.ui-like--lg { font-size: 1.125rem; }

.ui-like--sm .ui-like__icon { width: 20px; height: 20px; }
.ui-like--md .ui-like__icon { width: 26px; height: 26px; }
.ui-like--lg .ui-like__icon { width: 32px; height: 32px; }

/* Spring pop on toggle lives on an HTML wrapper, never the inline <svg>,
 * so Chromium doesn't raster the heart at 1x on HiDPI. */
.ui-like__pop {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.ui-like__icon {
  stroke: currentColor;
  fill: transparent;
  stroke-width: 1.6;
  stroke-linejoin: round;
  transition:
    transform 420ms var(--ease-spring, cubic-bezier(0.34, 1.56, 0.64, 1)),
    fill 150ms ease,
    stroke 150ms ease;
}

.ui-like:hover .ui-like__icon {
  transform: scale(1.08);
}

.ui-like.is-liked .ui-like__icon {
  fill: #ff4d6d;
  stroke: transparent;
}

/* Bookmark glyph (favorite semantics): liked fill and burst follow the
 * theme's primary color instead of the warm heart palette. */
.ui-like--bookmark.is-liked .ui-like__icon {
  fill: var(--color-primary-500, var(--color-primary, #ff4d6d));
}

.ui-like--bookmark .ui-like__particle {
  background: var(--color-primary-400, #ff8a3d);
  box-shadow: 0 0 6px color-mix(in srgb, var(--color-primary-500, #ff5e8a) 60%, transparent);
}

.ui-like.is-bursting .ui-like__pop {
  animation: ui-like-pop 350ms cubic-bezier(0.34, 1.96, 0.64, 1) forwards;
}

.ui-like__count {
  font-variant-numeric: tabular-nums;
  color: var(--color-primary-text, currentColor);
}

/* Particle spray: dots fly along per-like random vectors set from JS. */
.ui-like__burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: block;
}

.ui-like__particle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(5px * var(--psize, 1));
  height: calc(5px * var(--psize, 1));
  border-radius: 999px;
  background: linear-gradient(135deg, #ff5e8a, #ff8a3d);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.2);
  box-shadow: 0 0 6px rgba(255, 120, 90, 0.6);
}

.ui-like.is-bursting .ui-like__particle {
  animation: ui-like-particle var(--pdur, 600ms) var(--ease-crisp, cubic-bezier(0.2, 0.6, 0.2, 1)) var(--pdelay, 0ms) forwards;
}

@keyframes ui-like-pop {
  0%   { transform: scale(1); }
  30%  { transform: scale(0.82); }
  100% { transform: scale(1); }
}

@keyframes ui-like-particle {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.2);
  }
  20% { opacity: 1; }
  100% {
    opacity: 0;
    transform:
      translate(calc(-50% + var(--px, 0px)), calc(-50% + var(--py, 0px)))
      scale(var(--p-end-scale, 0.6));
  }
}

@media (prefers-reduced-motion: reduce) {
  .ui-like__icon,
  .ui-like__pop,
  .ui-like__particle {
    transition: none;
    animation: none;
  }
}


/* ===== resizable_split.css ===== */
@layer components.base {
  /*
   * Companion CSS for ResizableSplitComponent. The component itself sets
   * `display: grid` and the columns/rows template inline so the JS
   * controller can update sizing without fighting Tailwind. This file
   * just adds the visual treatment for the drag handle.
   */

  :where(.resizable-split) {
    --resizable-split-handle-size: 6px;
    --resizable-split-handle-color: var(--color-border, #e6e7eb);
    --resizable-split-handle-hover: var(--color-primary-500, #2855ff);
  }

  :where(.resizable-split--horizontal) > :where(.resizable-split__handle) {
    width: var(--resizable-split-handle-size);
    height: 100%;
    border-left: 1px solid var(--resizable-split-handle-color);
    border-right: 1px solid var(--resizable-split-handle-color);
    background: transparent;
  }

  :where(.resizable-split--vertical) > :where(.resizable-split__handle) {
    height: var(--resizable-split-handle-size);
    width: 100%;
    border-top: 1px solid var(--resizable-split-handle-color);
    border-bottom: 1px solid var(--resizable-split-handle-color);
    background: transparent;
  }

  :where(.resizable-split__handle) {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    user-select: none;
    -webkit-user-select: none;
    transition: background-color 0.15s ease;
    flex: none;
  }

  :where(.resizable-split__handle):hover,
  :where(.resizable-split__handle):active,
  :where(.resizable-split__handle):focus-visible {
    background-color: color-mix(in oklab, var(--resizable-split-handle-hover) 12%, transparent);
  }

  :where(.resizable-split__handle):focus-visible {
    outline: 2px solid var(--resizable-split-handle-hover);
    outline-offset: -2px;
  }

  /* shadcn-style grip pill (3 dots vertically arranged for horizontal,
     horizontally for vertical). Implemented with a single span + box-
     shadow stacking so we don't need extra DOM. */
  :where(.resizable-split__grip) {
    display: block;
    width: 2px;
    height: 16px;
    background: var(--color-text-muted, #6b6f76);
    border-radius: 9999px;
    opacity: 0.5;
    pointer-events: none;
  }

  :where(.resizable-split--vertical) > :where(.resizable-split__handle) > :where(.resizable-split__grip) {
    width: 16px;
    height: 2px;
  }

  :where(.resizable-split__handle):hover > :where(.resizable-split__grip),
  :where(.resizable-split__handle):focus-visible > :where(.resizable-split__grip) {
    opacity: 1;
    background: var(--resizable-split-handle-hover);
  }
}


/* ===== collapse_rail.css ===== */
/* CollapseRailComponent — slim vertical tab (icon + vertical label) that
   re-expands a collapsed side panel. Intrinsic look only: placement inside a
   host layout (grid cell, show/hide gating, enter animation) stays with the
   host via an additional class on the component. */

.collapse-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.875rem;
  width: 2.5rem;
  padding-block: 1rem 1.25rem;
  border: var(--border) solid var(--color-border);
  border-right: none;
  border-radius: 0.75rem 0 0 0.75rem;
  background-color: var(--color-bg);
  box-shadow: var(--shadow-sm);
  color: var(--color-text-subtle);
  cursor: pointer;
  transition: color var(--time-150), background-color var(--time-150);
}

.collapse-rail:hover {
  color: var(--color-text);
  background-color: var(--color-main-bg);
}

.collapse-rail__label {
  writing-mode: vertical-rl;
  font-size: 0.75rem;
  font-weight: var(--font-semibold);
  letter-spacing: 0.06em;
}

@media (prefers-reduced-motion: reduce) {
  .collapse-rail {
    transition: none;
  }
}


/* ===== toast.css ===== */
/* Toast Notifications */
.toast-item {
  @apply absolute w-full left-0 select-none;
  z-index: var(--toast-z-index, 100);
  max-width: var(--toast-max-width, 320px);

  /* Position based on data attributes */
  top: var(--toast-top, auto);
  bottom: var(--toast-bottom, auto);
  transform: var(--toast-transform, translateY(0));
  scale: var(--toast-scale, 100%);
  opacity: var(--toast-opacity, 1);

  /* Separate transitions for better control - like Sonner. Transform and
     scale ride a gentle overshoot curve so toasts fly in with a springy
     settle instead of a flat ease; positional/stacking moves stay eased. */
  transition: transform 400ms var(--ease-toast), opacity 300ms ease,
    scale 400ms var(--ease-toast), top 400ms ease, bottom 400ms ease,
    height 200ms ease;
}

/* Status icon pops in once the toast has mounted (data-mounted flips to true
   right after insertion, which starts the animation). The loading spinner is
   excluded — it already carries its own animation (animate-spin). */
@keyframes toast-icon-pop {
  0%   { transform: scale(0.8); opacity: 0; }
  60%  { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

.toast-item[data-mounted="true"] .toast-item__icon-wrap svg:not(.animate-spin) {
  animation: toast-icon-pop 0.45s var(--ease-spring) both;
}

@media (prefers-reduced-motion: reduce) {
  .toast-item[data-mounted="true"] .toast-item__icon-wrap svg:not(.animate-spin) {
    animation: none;
  }
}

/* Initial hidden state for enter animation */
.toast-item[data-mounted="false"] {
  opacity: 0;
}

.toast-item[data-mounted="false"][data-position*="bottom"] {
  transform: translateY(100%);
}

.toast-item[data-mounted="false"][data-position*="top"] {
  transform: translateY(-100%);
}

/* Removed state for exit animation */
.toast-item[data-removed="true"] {
  opacity: 0;
  scale: 95%;
  pointer-events: none;
}

/* In stacked mode, removed toasts should slide away */
.toast-item[data-removed="true"][data-expanded="false"][data-position*="bottom"] {
  transform: translateY(calc(var(--toast-index) * 14px + 5%));
}

.toast-item[data-removed="true"][data-expanded="false"][data-position*="top"] {
  transform: translateY(calc(-1 * (var(--toast-index) * 14px + 5%)));
}

/* Overflow toasts (removed due to limit) slide in opposite direction */
.toast-item[data-removed="true"][data-overflow="true"][data-expanded="false"][data-position*="bottom"] {
  transform: translateY(calc(-1 * (var(--toast-index) * 14px + 25%)));
  scale: 78%;
}

.toast-item[data-removed="true"][data-overflow="true"][data-expanded="false"][data-position*="top"] {
  transform: translateY(calc(var(--toast-index) * 14px + 25%));
  scale: 78%;
}

/* Pointer events based on visibility */
.toast-item[data-visible="false"] {
  pointer-events: none;
}

/* Expanded mode styles */
.toast-item[data-expanded="true"] {
  --toast-scale: 100%;
  height: var(--initial-height);
}

.toast-item[data-expanded="true"][data-position*="bottom"] {
  --toast-top: auto;
  --toast-bottom: var(--toast-offset, 0px);
  --toast-transform: translateY(0);
}

.toast-item[data-expanded="true"][data-position*="top"] {
  --toast-top: var(--toast-offset, 0px);
  --toast-bottom: auto;
  --toast-transform: translateY(0);
}

/* Stacked mode styles */
.toast-item[data-expanded="false"][data-front="true"] {
  --toast-scale: 100%;
  --toast-opacity: 1;
  height: var(--initial-height);
}

.toast-item[data-expanded="false"][data-front="true"][data-position*="bottom"] {
  --toast-top: auto;
  --toast-bottom: 0px;
  --toast-transform: translateY(0);
}

.toast-item[data-expanded="false"][data-front="true"][data-position*="top"] {
  --toast-top: 0px;
  --toast-bottom: auto;
  --toast-transform: translateY(0);
}

/* Non-front toasts in stack - Sonner approach */
.toast-item[data-expanded="false"]:not([data-front="true"]) {
  height: var(--front-toast-height);
  overflow: hidden;
  --toast-scale: calc(100% - (var(--toast-index) * 6%));
  display: flex;
  flex-direction: column;
}

.toast-item[data-expanded="false"]:not([data-front="true"])[data-position*="bottom"] {
  --toast-top: auto;
  --toast-bottom: 0px;
  --toast-transform: translateY(calc(-1 * var(--toast-index) * 14px));
  justify-content: flex-start; /* Content at top, bottom gets cut */
}

.toast-item[data-expanded="false"]:not([data-front="true"])[data-position*="top"] {
  --toast-top: 0px;
  --toast-bottom: auto;
  --toast-transform: translateY(calc(var(--toast-index) * 14px));
  justify-content: flex-end; /* Content at bottom, top gets cut */
}

/* Hidden toasts (beyond 3rd) */
.toast-item[data-expanded="false"][data-visible="false"] {
  --toast-opacity: 0;
  --toast-scale: 82%;
}

.toast-item[data-expanded="false"][data-visible="false"][data-position*="bottom"] {
  --toast-top: auto;
  --toast-bottom: -200px;
  --toast-transform: translateY(0);
}

.toast-item[data-expanded="false"][data-visible="false"][data-position*="top"] {
  --toast-top: -200px;
  --toast-bottom: auto;
  --toast-transform: translateY(0);
}

/* Mobile exit animation: shrink horizontally, then slide right + fade */
@keyframes toast-mobile-exit {
  0% {
    transform: scaleX(1) translateX(0);
    opacity: 1;
  }
  40% {
    transform: scaleX(0.18) translateX(0);
    opacity: 1;
  }
  100% {
    transform: scaleX(0.18) translateX(110%);
    opacity: 0;
  }
}

/* Mobile overrides: bottom-anchored, full-width, YouTube-style exit */
@media (max-width: 639px) {
  /* Container: pin to bottom full-width regardless of configured position */
  #toast_triggers > ul {
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    bottom: 0 !important;
    width: auto !important;
    margin: 0 12px 12px !important;
    max-width: none !important;
    /* Neutralise Tailwind's -translate-x-1/2 on center positions by
       resetting its custom properties rather than the translate/transform
       shorthand, which Lightning CSS optimises in ways that break this. */
    --tw-translate-x: 0 !important;
    --tw-translate-y: 0 !important;
    transform: none !important;
  }

  /* Toast item: fill the row, origin at right for exit */
  .toast-item {
    max-width: none;
    transform-origin: right center;
  }

  /* Always enter from the bottom on mobile (configured top positions are ignored) */
  .toast-item[data-mounted="false"] {
    transform: translateY(100%) !important;
  }

  /* Exit animation overrides desktop fade+scale for non-overflow removals */
  .toast-item[data-removed="true"]:not([data-overflow="true"]) {
    animation: toast-mobile-exit 400ms var(--ease-standard) forwards;
    opacity: 1;
    scale: 1;
  }

  /* Mobile stacked peek/offset uses bottom-based positioning regardless of configured position */
  .toast-item[data-expanded="false"]:not([data-front="true"]) {
    --toast-top: auto;
    --toast-bottom: 0px;
    --toast-transform: translateY(calc(-1 * var(--toast-index) * 14px));
    justify-content: flex-start;
  }

  .toast-item[data-expanded="false"][data-front="true"] {
    --toast-top: auto;
    --toast-bottom: 0px;
    --toast-transform: translateY(0);
  }

  .toast-item[data-expanded="true"] {
    --toast-top: auto;
    --toast-bottom: var(--toast-offset, 0px);
    --toast-transform: translateY(0);
  }

  .toast-item[data-removed="true"][data-overflow="true"][data-expanded="false"] {
    transform: translateY(calc(-1 * (var(--toast-index) * 14px + 25%)));
    scale: 78%;
  }

  .toast-item[data-expanded="false"][data-visible="false"] {
    --toast-top: auto;
    --toast-bottom: -200px;
    --toast-transform: translateY(0);
  }
}


/* ===== tooltip.css ===== */
/* Tooltip CSS Styles */

/* Light theme tooltip (default) */
.tooltip-content {
  position: absolute;
  z-index: 1000;
  pointer-events: none;
  word-wrap: break-word;
  /* Preserve newlines in multi-line content (e.g. all events of a calendar
     day, one per line) while still collapsing runs of spaces. Single-line
     tooltips are unaffected. */
  white-space: pre-line;
  border-radius: var(--rounded-lg);
  border: 1px solid var(--zinc-300);
  background-color: var(--zinc-800);
  color: white;
  padding: var(--size-1) var(--size-2);
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
  opacity: 0;
  visibility: hidden;
}

/* Light theme alternative - brighter tooltip */
.tooltip-content.tooltip-light {
  background-color: var(--color-bg);
  color: var(--zinc-900);
  border: 1px solid var(--zinc-200);
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}

/* Dark theme tooltip */
[data-color-scheme="dark"] .tooltip-content {
  background-color: var(--zinc-900);
  color: var(--zinc-100);
  border-color: var(--zinc-700);
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
}

/* Dark theme alternative - even darker tooltip */
[data-color-scheme="dark"] .tooltip-content.tooltip-dark {
  background-color: black;
  color: white;
  border-color: var(--zinc-800);
}

/* Size variants */
.tooltip-content.tooltip-size-small {
  font-size: var(--text-xs);
}

.tooltip-content.tooltip-size-regular {
  font-size: var(--text-sm);
}

.tooltip-content.tooltip-size-large {
  font-size: var(--text-base);
}

/* Animation transitions */
.tooltip-content.tooltip-transition {
  transition: opacity 150ms ease-out, transform 150ms ease-out;
}

.tooltip-content.tooltip-scale {
  transform: scale(0.95);
}

.tooltip-content.tooltip-scale-100 {
  transform: scale(1);
}

/* Visibility states */
.tooltip-content.tooltip-visible {
  opacity: 1;
}

/* Transform origins for scale animations */
.tooltip-content.tooltip-origin-top {
  transform-origin: top;
}

.tooltip-content.tooltip-origin-bottom {
  transform-origin: bottom;
}

.tooltip-content.tooltip-origin-left {
  transform-origin: left;
}

.tooltip-content.tooltip-origin-right {
  transform-origin: right;
}

/* Arrow container */
.tooltip-arrow-container {
  position: absolute;
  z-index: 1000;
}

/* Arrow element - Light theme (default) */
.tooltip-arrow-element {
  width: var(--size-2);
  height: var(--size-2);
  background-color: var(--zinc-800);
  border: 1px solid var(--zinc-300);
  transform: rotate(45deg);
}

/* Light theme alternative - brighter arrow */
.tooltip-arrow-element.tooltip-light {
  background-color: var(--color-bg);
  border-color: var(--zinc-200);
}

/* Dark theme arrow */
[data-color-scheme="dark"] .tooltip-arrow-element {
  background-color: var(--zinc-900);
  border-color: var(--zinc-700);
}

/* Dark theme alternative - even darker arrow */
[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-dark {
  background-color: black;
  border-color: var(--zinc-800);
}

/* Arrow padding for different orientations */
.tooltip-arrow-container.tooltip-arrow-h {
  padding: 0 var(--size-1);
}

.tooltip-arrow-container.tooltip-arrow-v {
  padding: var(--size-1) 0;
}

/* Arrow border directions */
.tooltip-arrow-element.tooltip-arrow-top {
  border-top: 1px solid var(--zinc-300);
  border-left: 1px solid var(--zinc-300);
  border-bottom: none;
  border-right: none;
}

.tooltip-arrow-element.tooltip-arrow-bottom {
  border-bottom: 1px solid var(--zinc-300);
  border-right: 1px solid var(--zinc-300);
  border-top: none;
  border-left: none;
}

.tooltip-arrow-element.tooltip-arrow-left {
  border-bottom: 1px solid var(--zinc-300);
  border-left: 1px solid var(--zinc-300);
  border-top: none;
  border-right: none;
}

.tooltip-arrow-element.tooltip-arrow-right {
  border-top: 1px solid var(--zinc-300);
  border-right: 1px solid var(--zinc-300);
  border-bottom: none;
  border-left: none;
}

/* Light theme arrow borders */
.tooltip-arrow-element.tooltip-light.tooltip-arrow-top {
  border-top-color: var(--zinc-200);
  border-left-color: var(--zinc-200);
}

.tooltip-arrow-element.tooltip-light.tooltip-arrow-bottom {
  border-bottom-color: var(--zinc-200);
  border-right-color: var(--zinc-200);
}

.tooltip-arrow-element.tooltip-light.tooltip-arrow-left {
  border-bottom-color: var(--zinc-200);
  border-left-color: var(--zinc-200);
}

.tooltip-arrow-element.tooltip-light.tooltip-arrow-right {
  border-top-color: var(--zinc-200);
  border-right-color: var(--zinc-200);
}

/* Dark theme arrow borders */
[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-arrow-top {
  border-top-color: var(--zinc-700);
  border-left-color: var(--zinc-700);
}

[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-arrow-bottom {
  border-bottom-color: var(--zinc-700);
  border-right-color: var(--zinc-700);
}

[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-arrow-left {
  border-bottom-color: var(--zinc-700);
  border-left-color: var(--zinc-700);
}

[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-arrow-right {
  border-top-color: var(--zinc-700);
  border-right-color: var(--zinc-700);
}

/* Dark theme alternative arrow borders */
[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-dark.tooltip-arrow-top {
  border-top-color: var(--zinc-800);
  border-left-color: var(--zinc-800);
}

[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-dark.tooltip-arrow-bottom {
  border-bottom-color: var(--zinc-800);
  border-right-color: var(--zinc-800);
}

[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-dark.tooltip-arrow-left {
  border-bottom-color: var(--zinc-800);
  border-left-color: var(--zinc-800);
}

[data-color-scheme="dark"] .tooltip-arrow-element.tooltip-dark.tooltip-arrow-right {
  border-top-color: var(--zinc-800);
  border-right-color: var(--zinc-800);
}
/* Neutralisiert den Browser-Default-Crossfade der Turbo View Transitions
   (sonst ~0,25s Ganzseiten-Ghosting bei JEDER Navigation — liest sich auf
   bildlastigen Seiten als „Bilder flackern/laden neu"). Nur benannte
   Hero-Elemente animieren. */

/* ===== view_transitions.css ===== */
/*
 * View Transitions styling for Turbo-driven page changes.
 *
 * Paired with app/javascript/controllers/view_transitions_controller.js —
 * Turbo 8 itself wraps renders in document.startViewTransition() when the
 * `view-transition` meta tag is present; the controller only maintains the
 * `vt-hero` names and the rules below shape the animation.
 *
 * IMPORTANT: this file must be @imported into the consuming app's CSS
 * bundle. Without it the browser's DEFAULT transition runs: a ~0.25s
 * full-page cross-fade on EVERY navigation, whose old/new ghosting reads
 * as "the images flicker/reload" on image-heavy pages.
 *
 * Hero-Morph: any element that should morph between two views must carry
 * a matching, unique `view-transition-name` in both DOM states, e.g.
 *
 *   <a class="vc-tile" style="view-transition-name: hero-42">…</a>      (grid)
 *   <div class="detail-image" style="view-transition-name: hero-42">…</div>  (detail)
 *
 * The name must be unique per transition — use a record ID.
 */

/*
 * Default: root swaps instantly, only elements with a named
 * view-transition-name animate. This keeps page chrome (nav,
 * sidebars, docs content) visually stable and focuses attention
 * on the hero morph.
 *
 * Opt into a root slide/fade by overriding per-page.
 */

::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-image-pair(root) {
  isolation: auto;
}

::view-transition-group(*) {
  animation-duration: 0.42s;
  animation-timing-function: var(--ease-drawer);
}

/*
 * The root group must not inherit the 0.42s duration: its old/new images
 * are already static (animation: none above), but a RUNNING group animation
 * keeps the ::view-transition overlay alive — and while it is up the page
 * ignores all input. Zeroing it makes plain navigations finish (and become
 * interactive) immediately; only named hero groups animate for 0.42s.
 * Placed after ::view-transition-group(*) — same specificity, later wins.
 */
::view-transition-group(root) {
  animation-duration: 0s;
}

/*
 * Opt-in Frame/Stream crossfade (view_transitions_controller.js).
 *
 * Turbo 8 wraps only Drive visits in a view transition — Frame and Stream
 * renders never animate. The controller opts a Frame carrying `data-vt-frame`
 * (or a whitelisted Stream) into a manually started transition and gives the
 * mutating element the fixed `vt-frame` name so ONLY that element forms an
 * animating group; the rest of the page is the static root and swaps instantly.
 *
 * Deliberately short (0.2s) — the VT overlay blocks input while it runs, and a
 * frame update is a smaller change than a full navigation. Reused crossfade
 * keyframes (defined below). One frame animates per interaction; the fixed
 * name is set only for the transition's lifetime and cleared afterwards.
 */
::view-transition-group(vt-frame) {
  animation-duration: 0.2s;
}
::view-transition-old(vt-frame) {
  animation: 0.2s var(--ease-drawer) both vt-crossfade-out;
}
::view-transition-new(vt-frame) {
  animation: 0.2s var(--ease-drawer) both vt-crossfade-in;
}

/*
 * Directional frame slide (calendar ‹ / › navigation).
 *
 * The ‹/› calendar nav links carry `data-vt-frame-nav="back|forward"`;
 * view_transitions_controller.js mirrors that onto `html[data-vt-frame-nav]`
 * for the lifetime of the frame transition. When set, the generic vt-frame
 * crossfade above is upgraded to a horizontal slide — "forward" (next
 * month/week) pushes the old content out to the left and brings the new in
 * from the right; "back" is mirrored. The attribute presence outranks the
 * plain crossfade rules, so an unmarked frame render (Heute button, view
 * switch, month picker) keeps the crossfade. Reused slide keyframes; the
 * image-pair clip below keeps the sliding snapshots inside the card.
 */
::view-transition-image-pair(vt-frame) {
  overflow: clip;
}

html[data-vt-frame-nav="forward"]::view-transition-old(vt-frame) {
  animation: 0.2s var(--ease-drawer) both vt-slide-out-to-left;
}
html[data-vt-frame-nav="forward"]::view-transition-new(vt-frame) {
  animation: 0.2s var(--ease-drawer) both vt-slide-in-from-right;
}
html[data-vt-frame-nav="back"]::view-transition-old(vt-frame) {
  animation: 0.2s var(--ease-drawer) both vt-slide-out-to-right;
}
html[data-vt-frame-nav="back"]::view-transition-new(vt-frame) {
  animation: 0.2s var(--ease-drawer) both vt-slide-in-from-left;
}

/*
 * Unpaired hero entrance/exit (card → detail cover).
 *
 * Card → detail navigations name ONLY the destination cover `vt-hero`
 * (view_transitions_controller.js leaves the tapped card unnamed): a paired
 * morph would fly the cover snapshot through the ::view-transition top layer
 * ACROSS the page chrome — visibly over the sidebar. The resulting lone group
 * has a single image, matched here via `:only-child`: the incoming cover
 * fades/scales in from its center, on history-back it fades/scales out in
 * place. Paired morphs (mobile chat, docs demo) have both images, so these
 * rules never touch them.
 */
::view-transition-new(vt-hero):only-child {
  animation: 0.35s var(--ease-drawer) both vt-hero-zoom-in;
}
::view-transition-old(vt-hero):only-child {
  animation: 0.25s var(--ease-drawer) both vt-hero-zoom-out;
}

@keyframes vt-hero-zoom-in {
  from {
    opacity: 0;
    transform: scale(0.94);
  }
}
@keyframes vt-hero-zoom-out {
  to {
    opacity: 0;
    transform: scale(0.96);
  }
}

/*
 * ARD-style launch of a tapped card cover (card → detail).
 *
 * The tapped card gets its OWN `vt-hero-out` name (view_transitions_controller
 * handleClick) so it never pairs with the destination's `vt-hero` — no
 * snapshot flying over the sidebar. Its lone OLD group grows slightly while
 * fading out; on history-back the card's cached inline name forms a lone NEW
 * group and the card the user came from zooms back in.
 */
::view-transition-old(vt-hero-out):only-child {
  animation: 0.3s var(--ease-drawer) both vt-hero-launch;
}
::view-transition-new(vt-hero-out):only-child {
  animation: 0.35s var(--ease-drawer) both vt-hero-zoom-in;
}

@keyframes vt-hero-launch {
  to {
    opacity: 0;
    transform: scale(1.07);
  }
}

/*
 * Directional tab slide (shooting detail tabs).
 *
 * The tab-panel container (#main on shooting-tab pages) carries
 * `view-transition-name: vt-content`; the tab bar inside it carries its own
 * `vt-tabbar` name so it is lifted into a separate, static group and stays
 * put. The topbar chrome (compact mobile header) and the bottom nav live
 * OUTSIDE #main, so they are part of the static root and never move.
 *
 * By default vt-content is static (no cross-fade) exactly like root — a plain
 * navigation onto or off a tab page must not animate. Only when the companion
 * controller has detected a tab-to-tab move and set `html[data-vt-direction]`
 * does the content slide in that direction. Same mechanic on mobile and
 * desktop (deliberately not viewport-gated — the content column slides within
 * #main either way).
 */
::view-transition-group(vt-content),
::view-transition-group(vt-tabbar) {
  animation-duration: 0s;
}

/*
 * The sliding snapshots are NOT clipped by #main's overflow — they live in
 * the ::view-transition top layer, so translateX(±100%) would march the old
 * panel straight across the sidebar (and the incoming one across the right
 * rail). Clipping the image pair confines the slide to the content box.
 */
::view-transition-image-pair(vt-content) {
  overflow: clip;
}

::view-transition-old(vt-content),
::view-transition-new(vt-content) {
  animation: none;
}

/*
 * Mobile bottom nav — held static across section switches.
 *
 * BottomNavComponent renders `view-transition-name: vt-bottom-nav` on its fixed
 * <nav> in both the old and the new DOM (default; suppressible via `vt_name: nil`).
 * Without its own name the fixed nav is captured only in the static root snapshot,
 * but `#main` carries `vt-content` and its box reaches under the nav (the layout
 * spacer clears the fixed bar). The named `vt-content` group is painted ABOVE the
 * root in the VT overlay, so its opaque snapshot covers the bottom nav for the
 * 0.2/0.25s fade-through of a Dashboard↔Shootings↔… section switch and the bar
 * visibly blinks. Giving the nav its own static group (paint order after #main,
 * zeroed animation) lifts it above vt-content and keeps it painted throughout —
 * same mechanic as vt-tabbar/vt-sidebar-pill above.
 */
::view-transition-group(vt-bottom-nav) {
  animation-duration: 0s;
}
::view-transition-old(vt-bottom-nav),
::view-transition-new(vt-bottom-nav) {
  animation: none;
}

html[data-vt-direction="forward"]::view-transition-old(vt-content) {
  animation: 0.25s var(--ease-drawer) both vt-slide-out-to-left;
}
html[data-vt-direction="forward"]::view-transition-new(vt-content) {
  animation: 0.25s var(--ease-drawer) both vt-slide-in-from-right;
}
html[data-vt-direction="back"]::view-transition-old(vt-content) {
  animation: 0.25s var(--ease-drawer) both vt-slide-out-to-right;
}
html[data-vt-direction="back"]::view-transition-new(vt-content) {
  animation: 0.25s var(--ease-drawer) both vt-slide-in-from-left;
}

/*
 * Sidebar active-pill morph + content fade-through (cross-document).
 *
 * The active sidebar entry carries a STATIC `vt-sidebar-pill` name, present
 * in both the old and the new document of a Turbo Drive visit, so the browser
 * pairs the two snapshots and its built-in group animation slides the active
 * pill from the old nav entry to the new one. Named via CSS (not inline) so
 * the expanded menu and the collapsed rail contribute the name only while
 * they are rendered — `display: none` variants are never captured; two
 * simultaneously rendered matches would abort the whole transition. The
 * mobile drawer copy is deliberately not named (it lives outside #sidebar).
 *
 * Both the pill morph and the #main fade-through below are gated on
 * `html[data-vt-nav]`, set by view_transitions_controller.js only when the
 * active sidebar entry actually changes (never on restore visits). On every
 * other navigation the groups stay zeroed, so pages keep swapping instantly
 * and the input-blocking VT overlay closes immediately.
 */
#sidebar .sidebar-menu__full a.sidebar-menu__button[aria-current="page"],
#sidebar .sidebar-rail__link[aria-current="page"] {
  view-transition-name: vt-sidebar-pill;
}

::view-transition-group(vt-sidebar-pill) {
  animation-duration: 0s;
}
::view-transition-old(vt-sidebar-pill),
::view-transition-new(vt-sidebar-pill) {
  animation: none;
}

html[data-vt-nav]::view-transition-group(vt-sidebar-pill) {
  animation-duration: 0.3s;
  animation-timing-function: var(--ease-drawer);
}

/*
 * Labels differ between nav entries, so the two snapshots have different
 * intrinsic sizes — pin both to the group box and crossfade them in sync
 * with the morph.
 */
html[data-vt-nav]::view-transition-old(vt-sidebar-pill) {
  animation: 0.3s var(--ease-drawer) both vt-crossfade-out;
  height: 100%;
}
html[data-vt-nav]::view-transition-new(vt-sidebar-pill) {
  animation: 0.3s var(--ease-drawer) both vt-crossfade-in;
  height: 100%;
}

/*
 * Fade-through of the #main content when the active sidebar entry changes:
 * the old page drifts a few pixels toward the direction of travel while the
 * new one drifts in from the opposite side — `forward` = moving down the nav
 * list. Deliberately a subtle drift, not a slide: main-nav sections are
 * peers; only the shooting tabs (vt-direction above) get the full panel
 * slide. The vt-content image-pair clip above keeps the drifting snapshots
 * inside the content box.
 */
html[data-vt-nav]::view-transition-old(vt-content) {
  animation: 0.2s var(--ease-drawer) both vt-nav-fade-out;
}
html[data-vt-nav]::view-transition-new(vt-content) {
  animation: 0.25s var(--ease-drawer) both vt-nav-fade-in;
}

html[data-vt-nav="forward"]::view-transition-old(vt-content) {
  --vt-nav-drift: -14px;
}
html[data-vt-nav="forward"]::view-transition-new(vt-content) {
  --vt-nav-drift: 14px;
}
html[data-vt-nav="back"]::view-transition-old(vt-content) {
  --vt-nav-drift: 14px;
}
html[data-vt-nav="back"]::view-transition-new(vt-content) {
  --vt-nav-drift: -14px;
}

@keyframes vt-nav-fade-out {
  to {
    opacity: 0;
    transform: translateY(var(--vt-nav-drift, 0px));
  }
}
@keyframes vt-nav-fade-in {
  from {
    opacity: 0;
    transform: translateY(var(--vt-nav-drift, 0px));
  }
}

/*
 * Manually started crossfades (withViewTransition helper in lib/view_transition.js).
 *
 * The default rules above make the root swap instantly so plain Turbo
 * navigations don't animate. A DOM toggle wrapped in withViewTransition adds
 * `vt-crossfade` to <html> for the lifetime of the transition to opt THIS
 * transition back into a short root crossfade — overriding both the
 * `animation: none` on the root old/new images and the 0s root group duration
 * that would otherwise swallow it. The class is removed again once the
 * transition settles.
 */
html.vt-crossfade::view-transition-group(root) {
  animation-duration: 0.18s;
}
html.vt-crossfade::view-transition-old(root) {
  animation: 0.18s var(--ease-drawer) both vt-crossfade-out;
}
html.vt-crossfade::view-transition-new(root) {
  animation: 0.18s var(--ease-drawer) both vt-crossfade-in;
}

/*
 * An app whose #main is permanently named vt-content has its content lifted
 * OUT of the root group — without these companions a manual crossfade would
 * fade the page chrome but hard-swap the content, which is where the wrapped
 * mutation usually happens. Same 0.18s lifetime as the root rules above.
 */
html.vt-crossfade::view-transition-group(vt-content) {
  animation-duration: 0.18s;
}
html.vt-crossfade::view-transition-old(vt-content) {
  animation: 0.18s var(--ease-drawer) both vt-crossfade-out;
}
html.vt-crossfade::view-transition-new(vt-content) {
  animation: 0.18s var(--ease-drawer) both vt-crossfade-in;
}

@keyframes vt-crossfade-out {
  to { opacity: 0; }
}
@keyframes vt-crossfade-in {
  from { opacity: 0; }
}

@keyframes vt-slide-out-to-left {
  to { transform: translateX(-100%); }
}
@keyframes vt-slide-in-from-right {
  from { transform: translateX(100%); }
}
@keyframes vt-slide-out-to-right {
  to { transform: translateX(100%); }
}
@keyframes vt-slide-in-from-left {
  from { transform: translateX(-100%); }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation-duration: 0.01ms;
  }

  /*
   * The tab slide keys off explicit old/new animations, which the group rule
   * above does not cover — collapse them here too so reduced-motion users get
   * an instant swap.
   */
  html[data-vt-direction]::view-transition-old(vt-content),
  html[data-vt-direction]::view-transition-new(vt-content) {
    animation-duration: 0.01ms;
  }

  /*
   * The unpaired hero entrance/exit keys off explicit old/new animations,
   * which the group rule above does not cover — collapse them here too so
   * reduced-motion users get an instant swap.
   */
  ::view-transition-new(vt-hero):only-child,
  ::view-transition-old(vt-hero):only-child,
  ::view-transition-new(vt-hero-out):only-child,
  ::view-transition-old(vt-hero-out):only-child {
    animation-duration: 0.01ms;
  }

  /*
   * The opt-in frame crossfade keys off explicit vt-frame old/new animations,
   * which the group rule above does not cover — collapse them here too so
   * reduced-motion users get an instant swap.
   */
  ::view-transition-old(vt-frame),
  ::view-transition-new(vt-frame) {
    animation-duration: 0.01ms;
  }

  /*
   * The directional frame slide gates its old/new animations on
   * `html[data-vt-frame-nav]`, which outranks the plain vt-frame collapse
   * above — collapse the gated selectors too so reduced-motion users get an
   * instant frame swap.
   */
  html[data-vt-frame-nav]::view-transition-old(vt-frame),
  html[data-vt-frame-nav]::view-transition-new(vt-frame) {
    animation-duration: 0.01ms;
  }

  /*
   * The manual crossfade keys off explicit root old/new animations, which the
   * group rule above does not cover — collapse them here too so reduced-motion
   * users get an instant swap instead of a fade.
   */
  html.vt-crossfade::view-transition-old(root),
  html.vt-crossfade::view-transition-new(root),
  html.vt-crossfade::view-transition-group(vt-content),
  html.vt-crossfade::view-transition-old(vt-content),
  html.vt-crossfade::view-transition-new(vt-content) {
    animation-duration: 0.01ms;
  }

  /*
   * The sidebar-nav pill morph and content fade key off explicit vt-nav-gated
   * animations, which the group rule above does not cover — collapse them
   * here too so reduced-motion users get an instant swap.
   */
  html[data-vt-nav]::view-transition-group(vt-sidebar-pill),
  html[data-vt-nav]::view-transition-old(vt-sidebar-pill),
  html[data-vt-nav]::view-transition-new(vt-sidebar-pill),
  html[data-vt-nav]::view-transition-old(vt-content),
  html[data-vt-nav]::view-transition-new(vt-content) {
    animation-duration: 0.01ms;
  }
}


/* App-specific CSS */

/* ===== tabpanel_focus.css ===== */
/* Tabpanel focus ring: suppress the browser default outline when a tabpanel
 * receives focus via mouse/pointer (e.g. a mousedown on empty grid space that
 * bubbles focus to the focusable `[role="tabpanel"]` ancestor), while keeping a
 * visible focus ring for keyboard users via :focus-visible.
 *
 * Covers all tabpanels — app views (galleries/show, kunden/show) and the
 * gui_components TabComponent, which all emit role="tabpanel" tabindex="0". */
[role="tabpanel"]:focus:not(:focus-visible) {
  outline: none;
}


/* ===== color_scheme.css ===== */
/* Dark mode toggle button */
.color-scheme-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--size-8);
  height: var(--size-8);
  border-radius: var(--rounded-md);
  color: var(--color-text-subtle);
  background: transparent;
  border: none;
  cursor: pointer;
  transition: color var(--time-150), background-color var(--time-150);

  &:hover {
    color: var(--color-text);
    background-color: var(--color-border-light);
  }
}

/* Icon visibility controlled by data-color-scheme on <html> */
:root,
[data-color-scheme="light"] {
  .icon-light { display: flex; }
  .icon-dark  { display: none; }
}

[data-color-scheme="dark"] {
  .icon-light { display: none; }
  .icon-dark  { display: flex; }
}


/* ===== dashboard_cards.css ===== */
/* Dashboard card grid (Views::Pages::Home): overview card left, activity
   panel right. Below lg the dashboard-tabs controller switches between the
   two panels; from lg both are visible and the activity column can collapse
   into a slim edge-docked handle (activity-collapse controller +
   .dashboard-cards--activity-collapsed, persisted in a cookie).

   Collapse/expand is animated: the right grid track transitions between two
   flex values (fr ↔ fr interpolates; fr ↔ length would jump) and the column
   gap shrinks with it, while the panel and the handle cross-fade inside the
   same grid cell. The handle's margin-box keeps a floor under the 0fr track.
   Browsers without `allow-discrete`/`@starting-style` fall back to instant
   swaps but still animate the column width.

   Sticky offsets are deliberately 1rem everywhere (#main's own padding):
   a larger offset would push the sticky pieces below the card tops at
   scroll 0, which made the expand animation read as "hop up, then widen". */

.dashboard-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  align-items: start;
}

/* The collapsed-state handle is a desktop-only affordance. */
.dashboard-cards__rail {
  display: none;
}

@media (width >= 64rem) {
  .dashboard-cards {
    grid-template-columns: 2fr 1fr;
    column-gap: 1.5rem;
    /* Couple the two columns' heights: stretch the auto row so the activity
       cell fills the calendar-driven height. The activity card carries
       lg:h-full to end flush with the calendar's lower edge; its internal
       min-h-0/overflow-y-auto feed scrolls when it has more than that height.
       The mobile single-column layout keeps align-items: start (base rule). */
    align-items: stretch;
  }

  /* All enter/exit transitions and @starting-style fades are gated behind
     --animating (added by activity_collapse_controller only for the duration
     of an interactive toggle). A fresh Turbo render carries no such class, so
     the panel/rail land in their final state instantly instead of replaying
     the fade on every dashboard visit (issue #1170). */
  .dashboard-cards--animating {
    /* Deliberately not --ease-out-soft: its front-loaded curve covers ~95% of
       the distance in the first third, which reads as a jump on a 400px track
       change. A calmer symmetric S-curve keeps the movement legible. */
    transition: grid-template-columns 0.45s cubic-bezier(0.45, 0, 0.15, 1),
                column-gap 0.45s cubic-bezier(0.45, 0, 0.15, 1);
  }

  .dashboard-cards--activity-collapsed {
    grid-template-columns: 2fr 0fr;
    column-gap: 1rem;
  }

  /* Panel and handle share the second cell so they overlap while cross-fading
     instead of stacking (which would push the panel down mid-transition). */
  .dashboard-cards__aside,
  .dashboard-cards__rail {
    grid-column: 2;
    grid-row: 1;
  }

  /* Panel fade-in when expanding; the transition on this (target) state also
     carries the delay so the column widens before the feed reappears. */
  .dashboard-cards__aside {
    opacity: 1;
  }

  .dashboard-cards--animating .dashboard-cards__aside {
    transition: opacity 0.26s ease-out 0.1s, display 0.26s allow-discrete 0.1s;
  }

  @starting-style {
    .dashboard-cards--animating .dashboard-cards__aside {
      opacity: 0;
    }
  }

  /* Panel fade-out when collapsing: quick, no delay, gone (display) once the
     fade finishes while the track keeps shrinking. */
  .dashboard-cards--activity-collapsed .dashboard-cards__aside {
    display: none;
    opacity: 0;
    pointer-events: none;
  }

  .dashboard-cards--animating.dashboard-cards--activity-collapsed .dashboard-cards__aside {
    transition: opacity 0.14s ease-out, display 0.14s allow-discrete;
  }

  /* Docked pull-out handle: hugs the right viewport edge (the negative margin
     swallows #main's 1rem padding), rounded only towards the content, so the
     collapsed state reserves barely any width and reads as a drawer tab.
     Not sticky on purpose: as a grid item its containing block is the whole
     (tall) row, so any sticky offset immediately shoves it below the card
     tops. Top-aligned via the grid instead, mirroring the expanded panel.

     The full tab chrome lives on this base (hidden) state, NOT on the
     collapsed modifier: during the expand cross-fade the handle stays
     visible for 150ms (display via allow-discrete), and if flex-direction/
     width/padding dropped with the modifier class the vertical label
     snapped up next to the icon before fading — the "hop".

     justify-self: end pins the handle to its cell's right edge, which stays
     put while the column track animates — start-aligned it rode the growing
     track ~200px into the page mid-fade. */
  /* Placement + show/hide gating only — the intrinsic tab look (width,
     border, colors, vertical label) is the gem's .collapse-rail
     (collapse_rail.css); the component carries both classes. */
  .dashboard-cards__rail {
    display: none;
    /* Pin to the cell top: the grid's align-items: stretch would otherwise
       pull the slim tab to the full calendar height. */
    align-self: start;
    justify-self: end;
    margin-right: -1rem;
    pointer-events: none;
    opacity: 0;
    translate: 0.75rem 0;
  }

  .dashboard-cards--animating .dashboard-cards__rail {
    transition: color var(--time-150), background-color var(--time-150),
                opacity 0.15s ease-out, translate 0.15s ease-out, display 0.15s allow-discrete;
  }

  /* Entry is delayed so the panel has faded before the handle slides in. */
  .dashboard-cards--activity-collapsed .dashboard-cards__rail {
    display: flex;
    pointer-events: auto;
    opacity: 1;
    translate: 0 0;
  }

  .dashboard-cards--animating.dashboard-cards--activity-collapsed .dashboard-cards__rail {
    transition: color var(--time-150), background-color var(--time-150),
                opacity 0.2s ease-out 0.16s, translate 0.25s var(--ease-out-soft) 0.16s;
  }

  @starting-style {
    .dashboard-cards--animating.dashboard-cards--activity-collapsed .dashboard-cards__rail {
      opacity: 0;
      translate: 0.75rem 0;
    }
  }

}

@media (prefers-reduced-motion: reduce) {
  .dashboard-cards,
  .dashboard-cards--animating,
  .dashboard-cards__aside,
  .dashboard-cards--animating .dashboard-cards__aside,
  .dashboard-cards--activity-collapsed .dashboard-cards__aside,
  .dashboard-cards--animating.dashboard-cards--activity-collapsed .dashboard-cards__aside,
  .dashboard-cards__rail,
  .dashboard-cards--animating .dashboard-cards__rail,
  .dashboard-cards--activity-collapsed .dashboard-cards__rail,
  .dashboard-cards--animating.dashboard-cards--activity-collapsed .dashboard-cards__rail {
    transition: none;
  }
}


/* ===== dashboard_mobile.css ===== */
/* Dashboard mobile layer (Views::Pages::Home + DashboardCalendar below lg):
 * the day-view heading of the dashboard's DayAgenda instance and the
 * "Demnächst" empty placeholder. Everything else on the mobile dashboard is
 * styled with Tailwind utilities or lives in the gui_components calendar
 * styles. (The former docked "Neues Shooting" CTA now lives in the gem
 * BottomNav center-notch FAB — see ApplicationLayout.) */

/* Dashboard flavor of the gem DayAgenda: a single "Sonntag, 6. Juli" heading
   (the redundant weekday/today label is dropped) at the section-title size of
   the mobile dashboard. */

/* Small breathing space above the heading when a day tap scrolls the agenda
   to the top of #main, so it does not sit flush against the edge. */
.dashboard-day-agenda {
  scroll-margin-top: 0.5rem;
}

.dashboard-day-agenda .day-agenda__header {
  margin: 0 0 var(--size-1);
}

.dashboard-day-agenda .day-agenda__day-label {
  display: none;
}

.dashboard-day-agenda .day-agenda__date-label {
  font-size: 16px;
  font-weight: var(--font-bold);
  color: var(--color-text);
}

/* "Demnächst" placeholder when nothing follows the selected day; toggled by
   the month-agenda controller. */
.dashboard-upcoming__empty {
  border: 1.5px dashed var(--color-border);
  border-radius: var(--radius-3);
  padding: 18px;
  text-align: center;
  font-size: 13px;
  color: var(--color-text-subtle);
}

.dashboard-upcoming__empty[hidden] {
  display: none;
}


/* ===== photo_timeline.css ===== */
/* PhotoTimelineComponent (dashboard "Aktivitäten" panel) — mobile override of
 * the gem PillNav :underline indicator for the Shootings/Kunden filter tabs.
 * The gem default (desktop) is a 2px dark neutral underline; below lg the
 * dashboard design wants a thicker 3px primary underline. Set via the gem's
 * CSS variables (kept out of Tailwind border utilities, which the gem's global
 * `* { border-color: var(--color-border) }` rule silently beats). */
@media (max-width: 1023.98px) {
  .dashboard-activity-underline {
    --gc-underline-size: 3px;
    --gc-underline-color: var(--color-primary);
  }
}

/* Kunden-tab (client-clustered) cards. The gallery name in a view line is
   emphasised in the theme's primary colour (adapts to light/dark); using
   var(--color-primary) keeps it a theme token rather than a hardcoded hex. */
.photo-timeline__gallery-name {
  color: var(--color-primary);
  font-weight: 600;
}

/* Divider between the action lines of a clustered client card. Set here (not
   via a Tailwind border utility) because the gem's unlayered
   `* { border-color: var(--color-border) }` rule beats those utilities. */
.photo-timeline__customer-line + .photo-timeline__customer-line {
  border-top: 1px solid var(--color-border);
}

/* Clamp a client comment to two lines with an ellipsis. */
.photo-timeline__comment-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}


/* ===== shooting_event_sheet.css ===== */
/* Dashboard shooting-event detail sheet (design variant 4a): hero cover with
 * scrim + type badge, ticket-style date card, hairline-separated gallery and
 * customer rows, bottom-pinned CTA. Colors, radii and typography come from the
 * theme tokens so dark mode works via [data-color-scheme="dark"] for free.
 * Whites/overlays that sit on top of the cover photo are intentionally
 * scheme-independent. */

.shooting-event {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

/* ── Hero cover ─────────────────────────────────────────────────────────── */

.shooting-event__hero {
  position: relative;
  height: 260px;
  flex-shrink: 0;
  overflow: hidden;
}

.shooting-event__hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(
    180deg,
    rgba(10, 14, 25, 0.25) 0%,
    rgba(10, 14, 25, 0) 35%,
    rgba(10, 14, 25, 0) 45%,
    rgba(10, 14, 25, 0.78) 100%
  );
}

.shooting-event__hero-heading {
  position: absolute;
  left: var(--size-6);
  right: var(--size-6);
  bottom: 18px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

.shooting-event__badge {
  padding: 3px 9px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.shooting-event__title {
  margin: 0;
  font: 600 26px/1.15 var(--font-grotesk);
  color: #fff;
  text-wrap: balance;
}

/* No converted cover yet: neutral surface with a camera icon, heading colors
   switch to the theme text colors since there is no photo to sit on. */
.shooting-event__hero--empty {
  background: var(--color-border-light);
  display: flex;
  align-items: center;
  justify-content: center;
}

.shooting-event__hero-fallback {
  color: var(--color-text-subtle);
  opacity: 0.6;
}

.shooting-event__hero--empty .shooting-event__badge {
  background: var(--color-bg);
  color: var(--color-primary);
  backdrop-filter: none;
}

.shooting-event__hero--empty .shooting-event__title {
  color: var(--color-text);
}

/* ── Body ───────────────────────────────────────────────────────────────── */

.shooting-event__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: var(--size-6) var(--size-6) 0;
}

.shooting-event__divider {
  height: 1px;
  flex-shrink: 0;
  background: var(--color-border-light);
  margin: 22px 0;
}

/* ── Ticket-style date card ─────────────────────────────────────────────── */

.shooting-event__ticket {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--color-border-light);
  border-radius: var(--rounded-xl);
  overflow: hidden;
}

.shooting-event__ticket-date {
  width: 96px;
  flex-shrink: 0;
  background: var(--color-bg);
  border-right: 1px solid var(--color-border-light);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 14px 0;
}

.shooting-event__ticket-month {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-primary);
}

.shooting-event__ticket-day {
  font: 600 34px/1.1 var(--font-grotesk);
  color: var(--color-text);
}

.shooting-event__ticket-weekday {
  font-size: 12px;
}

.shooting-event__ticket-facts {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  padding: 14px 16px;
}

.shooting-event__fact {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text);
  min-width: 0;
}

.shooting-event__fact-icon {
  display: flex;
  flex-shrink: 0;
}

/* ── Activity stats block ───────────────────────────────────────────────── */

.shooting-event__stats-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.shooting-event__stats-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.shooting-event__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

/* ── Gallery / customer rows ────────────────────────────────────────────── */

.shooting-event__row {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.shooting-event__row-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.shooting-event__row-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text);
}

.shooting-event__row-sub {
  font-size: 12px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
}

.shooting-event__thumbs {
  display: flex;
  flex-shrink: 0;
}

.shooting-event__thumb {
  width: 44px;
  height: 44px;
  box-sizing: border-box;
  border-radius: 10px;
  border: 2px solid var(--color-bg);
  object-fit: cover;
}

.shooting-event__thumb + .shooting-event__thumb {
  margin-left: -16px;
}

/* The "+N" tile sits on top of the stack: the next image darkened under a
   white count. */
.shooting-event__thumb--more {
  position: relative;
  z-index: 1;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.shooting-event__thumb--more img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.shooting-event__thumb-more-overlay {
  position: absolute;
  inset: 0;
  background: rgba(10, 14, 25, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
}

/* ── Bottom-pinned CTA ──────────────────────────────────────────────────── */

.shooting-event__cta {
  position: sticky;
  bottom: 0;
  z-index: 5;
  margin-top: auto;
  background: var(--color-bg);
  padding: var(--size-4) 0 var(--size-6);
}

/* ── Close button: light blur circle on top of the cover ────────────────── */

.sheet.shooting-event-sheet .sheet__close {
  width: 2.125rem;
  height: 2.125rem;
  inset-block-start: 14px;
  inset-inline-end: 14px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(6px);
  color: #111;
}

.sheet.shooting-event-sheet .sheet__close:hover {
  background: rgba(255, 255, 255, 1);
}

.sheet.shooting-event-sheet .sheet__close .sheet__close-icon {
  width: 1.125rem !important;
  height: 1.125rem !important;
}

/* ── Mobile bottom-sheet presentation ───────────────────────────────────── */

@media (width < 40rem) {
  /* Restore the rounded top corners the generic flush-sheet rule removes. */
  .sheet--right.shooting-event-sheet {
    border-radius: var(--rounded-xl) var(--rounded-xl) 0 0;
  }

  /* Fill the sheet to the viewport (minus the top gap) instead of growing with
     content, so the CTA docks at the bottom edge (thumb zone) even for short
     shootings. Matches the settings-sheet mobile pattern; the flush content
     then flexes and .shooting-event__cta's margin-top:auto pins it down. */
  .sheet--right.shooting-event-sheet:not(.sheet--mobile-side) {
    block-size: calc(100dvh - var(--size-6));
    display: flex;
    flex-direction: column;
  }

  .sheet--right.shooting-event-sheet:not(.sheet--mobile-side) .sheet__content {
    flex: 1;
    min-block-size: 0;
  }

  /* Float the drag handle over the cover instead of pushing it down. */
  .sheet--right.shooting-event-sheet::before {
    position: absolute;
    top: 0.5rem;
    left: 50%;
    translate: -50% 0;
    margin: 0;
    z-index: 30;
    background: rgba(255, 255, 255, 0.8);
  }

  /* Flatter cover so the ticket card and CTA stay visible without scrolling.
     Sized via height instead of aspect-ratio + max-height: WebKit transfers
     that max-height through the ratio into a max-width (400px), leaving a
     white stripe on viewports wider than 400pt. The sheet spans the full
     viewport in this media query, so 50vw keeps the 2:1 shape. */
  .shooting-event__hero {
    height: min(50vw, 200px);
  }

  /* ≥44px touch target for the close button. */
  .sheet--right.shooting-event-sheet .sheet__close {
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    inset-block-start: 12px;
    background: rgba(255, 255, 255, 0.85);
  }

  .shooting-event__cta {
    padding-bottom: max(var(--size-6), env(safe-area-inset-bottom));
  }
}


/* ===== help_guide.css ===== */
/*
 * help_guide.css — restyles the Ui::GuideComponent "?" icon trigger
 * (Components::Shared::HelpGuide) so it blends into the top-bar chrome.
 *
 * The gem's guide.css gives the icon trigger a white surface, border and a
 * fixed 44px size for the light guest gallery. In the backend top bar it should
 * read like the other ghost icon buttons (search, bell, theme toggle): flat,
 * transparent, using the app's semantic tokens so it also works in dark mode
 * (the gem's --guide-bg/--guide-fg are hardcoded light values). Scoped to the
 * .help-guide-launch root so the guest gallery trigger is untouched.
 */

.help-guide-launch .guide__trigger--icon {
  width: var(--size-8);
  height: var(--size-8);
  /* Same shape as the neighbouring icon buttons (bell, theme toggle) so the
     hover surfaces match across the whole icon row. */
  border-radius: var(--rounded-md);
  color: var(--color-text-subtle);
  background: transparent;
  border: none;
  box-shadow: none;
  transition: color var(--time-150), background-color var(--time-150);
}

.help-guide-launch .guide__trigger--icon:hover {
  color: var(--color-text);
  background-color: var(--color-border-light);
}

.help-guide-launch .guide__trigger-q {
  font-size: 1.125rem;
  font-weight: 700;
}

/* Keep the pulsing "new" dot legible on the transparent button: ring it with
 * the bar background rather than the gem's hardcoded white. */
.help-guide-launch .guide__trigger-dot {
  border-color: var(--color-bg, #fff);
}


/* ===== topbar_icons.css ===== */
/*
 * topbar_icons.css — shared chrome for the desktop top-bar icon triggers
 * (notification bell, language flag; the help "?" and the color-scheme toggle
 * get the same recipe via help_guide.css / color_scheme.css). One size, one
 * resting color, one hover surface, so the icon row reads as a single family.
 * Unlayered on purpose: it must beat the button variant's Tailwind utilities.
 */

.topbar-icon-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--size-8);
  height: var(--size-8);
  border-radius: var(--rounded-md);
  color: var(--color-text-subtle);
  transition: color var(--time-150), background-color var(--time-150);
}

.topbar-icon-button:hover {
  color: var(--color-text);
  background-color: var(--color-border-light);
}


/* ===== layouts.css ===== */
html {
  overflow: clip;
  block-size: 100dvh;
  overscroll-behavior: none;
}

body {
  position: fixed;
  inset: 0;
  overflow: clip;
  overscroll-behavior: none;
}

.sidebar-layout {
  display: grid;
  grid-template-areas: "header header" "main main";
  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr;
  block-size: 100dvh;
  /* Collapse animates grid-template-columns (a layout property, so every frame
     reflows the main column). A perf trace of a large masonry gallery during the
     toggle stayed well under the 8ms/frame 60fps budget — this is a one-shot,
     non-interruptible transition, not a continuous one — so we keep the cheap
     grid animation (Option A of #906) and only tighten it: shorter duration and
     the house decelerate curve for a crisper feel with fewer reflow frames. */
  transition: grid-template-columns 0.25s var(--ease-crisp);
  overflow: hidden;
  position: relative;

  /* Mobile default: hide sidebar completely */
  --sidebar-width: 0;

  @media (width >= 48rem) {
    grid-template-areas: "header header" "sidebar main";
    grid-template-columns: var(--sidebar-width, var(--max-i-3xs)) 1fr;
    --sidebar-border-width: var(--border);
    --sidebar-padding: var(--size-2);
    --sidebar-width: var(--max-i-3xs);
  }

  &.sidebar-layout--collapsed {
    /* Width of the collapsed rail. Defaults to the simple icon+label rail;
       the complex icon-only rail (see :has below) is narrower. */
    --collapsed-rail-width: 5.75rem;

    @media (width >= 48rem) {
      grid-template-columns: var(--collapsed-rail-width) 1fr;
      /* The gem's shell (sidebar_menu.css) sizes the sidebar element itself via
         inline-size: var(--sidebar-width). Without this override the sidebar
         stays 16rem wide inside the 5.75rem rail column, so the rail content
         sits outside the clipped area and the nav items disappear. */
      --sidebar-width: var(--collapsed-rail-width);
    }

    @media (width < 48rem) {
      grid-template-areas: "header header" "main main";
      grid-template-columns: 1fr;
    }
  }

  &.sidebar-layout--collapsed:has(.sidebar-menu__collapsed--complex) {
    --collapsed-rail-width: 4.75rem;
  }
}

/* Reduced motion: the collapse should snap to its new width instantly rather
   than sliding. Covers both the grid columns and the synced header segment. */
@media (prefers-reduced-motion: reduce) {
  .sidebar-layout,
  .sidebar-layout .header__left {
    transition: none;
  }
}

.header-layout {
  display: grid;
  grid-template-areas: "header" "main";
  grid-template-rows: auto 1fr;
  block-size: 100dvh;
}

.centered-layout {
  display: grid;
  place-items: center;
  block-size: 100dvh;
}

.container {
  inline-size: 100%;
  margin-inline: auto;
  max-inline-size: var(--container-width, 64rem);
}

.sidebar-layout:has(#page-sub-header) {
  grid-template-areas: "header header" "page-sub-header page-sub-header" "main main";
  grid-template-rows: auto auto 1fr;

  @media (width >= 48rem) {
    grid-template-areas: "header header" "sidebar page-sub-header" "sidebar main";
  }
}

#page-sub-header {
  grid-area: page-sub-header;
  background-color: rgb(from var(--color-border-light) r g b / 0.5);
  border-block-end-width: var(--border);
  padding-block: var(--size-3);
  padding-inline: var(--size-4);
  display: flex;
  align-items: center;
  gap: var(--size-2);
  z-index: 5;
  overflow: hidden;
}

#header {
  align-items: center;
  background-color: rgb(from var(--color-border-light) r g b / 0.5);
  border-block-end-width: var(--border);
  min-block-size: var(--size-16);
  column-gap: var(--size-4);
  display: flex;
  grid-area: header;
  padding-inline: var(--size-4);
  position: relative;
  z-index: 10;
  /* Own compositing layer: without it Chrome shares the header's raster
   * tiles with the scrolling #main, and under heavy repaint churn (posting
   * a comment: entrance animation + smooth scroll) the bar blinks blank
   * white for a frame. Promoting it keeps the bar's pixels stable. */
  transform: translateZ(0);

  @media (width < 48rem) {
    padding-inline-start: var(--size-2);
    padding-inline-end: var(--size-3);
    column-gap: var(--size-1);
  }

  .header__left {
    inline-size: calc(var(--sidebar-width, var(--max-i-3xs)) - var(--size-4));
    border-inline-end-width: var(--border);
    padding-inline-end: var(--size-4);
    /* Stays in lockstep with the .sidebar-layout grid transition above. */
    transition: inline-size 0.25s var(--ease-crisp);

    .sidebar-layout--collapsed & {
      inline-size: auto;
      border-inline-end-width: 0;
      padding-inline-end: 0;
    }
  }
}

/* Thinner stroke for the sidebar collapse/expand toggle icon */
.sidebar-menu__toggle svg {
  stroke-width: 1.5;
}

/* Toggle-Icon-Crossfade (gestapelte Icons + Dreh-Crossfade) lebt jetzt im Gem
   (gui_components sidebar_menu.css, ab v1.81.2, #1235) — der frühere App-Override
   hier war nur nötig, weil der Gem-Swap am falschen Vorfahren hing. */

#sidebar {
  background-color: var(--color-sidebar-bg);
  border-inline-end-width: var(--sidebar-border-width, 0);
  flex-direction: column;
  grid-area: sidebar;
  overflow-x: hidden;
  overflow-y: auto;
  padding: var(--sidebar-padding, 0);
  row-gap: var(--size-2);

  /* Hide sidebar on mobile, show on desktop */
  display: none;

  @media (width >= 48rem) {
    display: flex;
  }

  &.sidebar--collapsed {
    /* Only show collapsed sidebar on desktop */
    @media (width >= 48rem) {
      display: flex !important;
    }

    @media (width < 48rem) {
      display: none !important;
    }

    .sidebar-menu > :not(.sidebar-menu__header):not(.sidebar-menu__collapsed) {
      opacity: 0;
      transition: opacity 0.2s ease;
      pointer-events: none;
    }

    .sidebar-menu__header {
      opacity: 1;
    }
  }

  .sidebar-menu > :not(.sidebar-menu__header) {
    opacity: 1;
    transition: opacity 0.2s ease;
    pointer-events: auto;
  }
}

.sidebar-layout #main {
  display: flex;
  background-color: var(--color-main-bg);
  flex-direction: column;
  grid-area: main;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--size-4);
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  /* Reserviert die Scrollleisten-Rinne dauerhaft, damit ihr Ein-/Ausblenden
     (z. B. wenn das JS-Masonry Spans setzt und die Inhaltshöhe kippt) die
     nutzbare Breite und damit die auto-fill-Spaltenbreite nicht mehr
     verändert — sonst schaukelt sich der ResizeObserver auf und die Kacheln
     flackern. Auf Overlay-Scrollleisten (macOS) ein No-op. */
  scrollbar-gutter: stable;
  position: relative;
  z-index: 0;
}

/* Direct flex children of #main must not shrink below their content height.
   #main is a flex column with overflow-y: auto; overflow != visible disables a
   flex item's automatic minimum size (min-height: auto), so a card carrying
   overflow-hidden would be squeezed under its content height and clip it — and
   because everything then "fits", #main.scrollHeight == clientHeight and the
   page has nothing to scroll (pagination / rows-per-page fall out of reach).
   flex-shrink: 0 lets every card/table grow naturally with its content so the
   page scrolls normally. */
.sidebar-layout #main > * {
  flex-shrink: 0;
}

/* Spacer flex-item that clears the fixed bottom nav on mobile and, on desktop,
   restores the last row's bottom padding. Using ::after instead of
   padding-bottom because Chrome does not reliably include padding-bottom of a
   flex+overflow container (grid item) in the scrollable overflow area, so at
   max scroll the bottom-most element would stay clipped by ~16px. */
.sidebar-layout #main::after {
  content: '';
  display: block;
  flex-shrink: 0;
  height: calc(3.5rem + var(--size-4)); /* bottom nav + extra buffer */

  @media (width >= 40rem) {
    height: var(--size-4); /* just the missing bottom padding on desktop */
  }
}


/* ===== pages/settings.css ===== */
.settings {
  &.gap {
    column-gap: var(--column-gap, 2rem);
    row-gap: var(--row-gap, 1rem);
  }

  .gap-large {
    column-gap: var(--column-gap, 4rem);
    row-gap: var(--row-gap, 2rem);
  }

  .sidebar {
    width: 12rem;
  }

  .menu-link {
    display: block;
    border-block-end-width: var(--border-size, 1px);
    padding-block: var(--size-3);
    border-block-end-color: var(--gray-200);
  }
}

/* Settings navigation show/hide lives on the elements as Tailwind utilities
 * (hidden md:block / md:hidden) in SettingsSidebarComponent — mobile-first,
 * no desktop-first override block needed. */

/* Desktop: give the vertical settings tabs a link-like hand cursor on hover.
 * The TabComponent renders each entry as a <button> (default arrow cursor);
 * on pointer/hover devices we want them to read as clickable nav links.
 * Gated on (hover: hover) so touch devices are unaffected. */
@media (hover: hover) {
  .settings [role="tab"] {
    cursor: pointer;
  }
}

/* Restore list bullets inside the Lexxy contenteditable area.
 * Tailwind's preflight zeroes list-style on all ul/ol, so without this
 * the editor renders bullet/number lists as bare lines even though the
 * preview iframe (own document, no preflight) shows them correctly. */
.lexxy-editor__content ul,
.lexxy-editor__content ol {
  padding-inline-start: 1.5rem;
}
.lexxy-editor__content ul {
  list-style: disc outside;
}
.lexxy-editor__content ol {
  list-style: decimal outside;
}


/* ===== settings_sheet.css ===== */
/* Mobile settings sheet: panel chrome (header with close/back at the
   top-left), nav list rows and the slide animations for in-sheet
   navigation between the overview and sub-page panels. */

/* As a bottom sheet the settings sheet always spans the full screen
   height (minus the top gap) instead of growing with its content, so
   the height does not jump between the overview and sub-page panels. It
   is a flex column so the drag-handle pill (the sheet's ::before) and the
   content partition cleanly without the content overflowing past the
   rounded bottom edge. */
@media (width < 40rem) {
  .sheet--right.settings-sheet:not(.sheet--mobile-side) {
    block-size: calc(100dvh - var(--size-6));
    display: flex;
    flex-direction: column;
  }

  /* Drop the gem's default content scroll + bottom padding: the panel body
     is the sole scroller and the save bar carries its own safe-area inset,
     so the bar can sit flush at the bottom edge. */
  .sheet--right.settings-sheet:not(.sheet--mobile-side) .sheet__content {
    flex: 1;
    min-block-size: 0;
    padding-block-end: 0;
    overflow: hidden;
  }
}

.settings-sheet .sheet__content {
  padding: 0;
  display: flex;
  flex-direction: column;
}

/* Every wrapper between .sheet__content and the panel must carry the flex
   column so the chain reaches the panel body and the save bar stays pinned.
   The auto-opened sheet nests two wrappers — #sheet-content (the Turbo
   Stream content wrapper) and the settings-sheet controller div — so both
   are flexed here; without the second one the chain breaks and a longer
   panel (e.g. Passwort) scrolls the whole sheet, hiding the save bar. */
.settings-sheet .sheet__content > div,
.settings-sheet [data-controller="settings-sheet"] {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-block-size: 0;
}

.settings-sheet turbo-frame {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-block-size: 0;
  overflow: hidden;
}

.settings-sheet-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-block-size: 0;
}

.settings-sheet-panel__header {
  display: flex;
  align-items: center;
  gap: var(--size-2);
  padding: var(--size-2) var(--size-3);
  border-block-end: 1px solid var(--color-border);
  background: var(--color-bg);
  flex-shrink: 0;
}

/* The overview header carries only the dialog close button and a title the
   mobile page header already shows — both meaningless when the panel renders
   inline on the /settings page, so it only appears inside the sheet. */
.settings-sheet-panel__header:has(.settings-sheet-panel__close-form) {
  display: none;
}

.settings-sheet .settings-sheet-panel__header:has(.settings-sheet-panel__close-form) {
  display: flex;
}

/* The close button sits inside a method="dialog" form (native no-JS close
   fallback); display:contents keeps the button a direct flex item. */
.settings-sheet-panel__close-form {
  display: contents;
}

.settings-sheet-panel__leading {
  flex-shrink: 0;
  margin-inline-start: calc(var(--size-2) * -1);
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: 9999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  color: var(--color-text);
  cursor: pointer;
  text-decoration: none;
}

/* The back link is 2.75rem wide (size-11) vs. the 2.5rem close button, so it
   pulls 2px further left to keep both icon centers on the same axis. */
.settings-sheet-panel__back {
  margin-inline-start: calc(-1 * var(--size-2) - 2px);
}

.settings-sheet-panel__leading:hover {
  background: var(--color-border-light);
}

.settings-sheet-panel__title {
  flex: 1;
  min-inline-size: 0;
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  outline: none;
}

.settings-sheet-panel__banner {
  margin: var(--size-3) var(--size-4) 0;
  padding: var(--size-2) var(--size-3);
  border-radius: var(--rounded-lg);
  font-size: 0.875rem;
}

.settings-sheet-panel__banner--notice {
  background: var(--color-positive-bg, #ecfdf5);
  color: var(--color-positive-text, #047857);
}

.settings-sheet-panel__banner--alert {
  background: var(--color-negative-bg, #fef2f2);
  color: var(--color-negative-text, #b91c1c);
}

.settings-sheet-panel__body {
  flex: 1;
  min-block-size: 0;
  overflow-y: auto;
  padding: var(--size-4);
  padding-block-end: max(var(--size-6), env(safe-area-inset-bottom));
}

/* Enter animations: the freshly rendered panel slides in from the right
   (forward) or the left (back). No `forwards` fill — a persisting transform
   would make the panel the containing block for position:fixed children
   (same trap documented in the gem's sheet.css). */
.settings-sheet-panel--enter-forward {
  animation: settingsSheetPanelEnterForward 280ms ease-out;
}

.settings-sheet-panel--enter-back {
  animation: settingsSheetPanelEnterBack 280ms ease-out;
}

/* Keyframes settingsSheetPanelEnterForward / …EnterBack live in animations.css. */

@media (prefers-reduced-motion: reduce) {
  .settings-sheet-panel--enter-forward,
  .settings-sheet-panel--enter-back {
    animation: none;
  }
}

.settings-sheet-nav__group-title {
  margin: var(--size-4) 0 var(--size-1);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-subtle, var(--color-text));
}

.settings-sheet-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.settings-sheet-nav li + li {
  border-block-start: 1px solid var(--color-border-light, var(--color-border));
}

.settings-sheet-nav__link {
  display: flex;
  align-items: center;
  gap: var(--size-3);
  padding: var(--size-3) var(--size-1);
  color: var(--color-text);
  text-decoration: none;
  min-block-size: 3.25rem;
}

.settings-sheet-nav__link:active {
  background: var(--color-border-light);
}

.settings-sheet-nav__icon {
  flex-shrink: 0;
  color: var(--color-text-subtle, var(--color-text));
}

.settings-sheet-nav__label {
  flex: 1;
  min-inline-size: 0;
  font-size: 0.9375rem;
  font-weight: 500;
}

.settings-sheet-nav__chevron {
  flex-shrink: 0;
  color: var(--color-text-subtle, var(--color-text));
  opacity: 0.6;
}

/* ----------------------------------------------------------------------- *
   Mobile sub-page panels (redesign)
 * ----------------------------------------------------------------------- */

/* Sticky save bar pinned at the bottom of a panel. Sits outside the
   scrollable body; its button submits the in-body form via the `form`
   attribute. */
.settings-sheet-panel__footer {
  flex-shrink: 0;
  padding: var(--size-3) var(--size-4);
  padding-block-end: max(var(--size-3), env(safe-area-inset-bottom));
  background: var(--color-bg);
  border-block-start: 1px solid var(--color-border);
  box-shadow: 0 -6px 18px -12px rgba(20, 20, 50, 0.18);
}

.settings-mobile-panel {
  display: flex;
  flex-direction: column;
}

.settings-mobile-panel__fields {
  display: flex;
  flex-direction: column;
  gap: var(--size-3);
  margin-block-end: var(--size-4);
}

.settings-sheet-panel__intro {
  margin: 0 0 var(--size-4);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--color-text-subtle);
}

.settings-sheet-panel__section-label {
  margin: var(--size-1) 0 var(--size-2);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-text-subtle);
}

.settings-sheet-panel__hint {
  margin: var(--size-1) var(--size-1) 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--color-text-subtle);
}

.settings-sheet-panel__rule {
  block-size: 1px;
  background: var(--color-border-light, var(--color-border));
  margin: var(--size-5) 0;
}

/* Avatar entry: centered profile photo with a camera badge that opens the
   crop bottom sheet. */
.settings-sheet-panel__avatar-trigger {
  display: block;
  inline-size: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.settings-avatar-entry {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--size-2);
  padding: var(--size-1) 0 var(--size-5);
}

.settings-avatar-entry__photo {
  position: relative;
  isolation: isolate;
  inline-size: 5.5rem;
  block-size: 5.5rem;
  border-radius: 9999px;
  overflow: visible;
}

/* Neutral tile with centred spinner behind the (transparent while loading)
   img; the image-loader controller hides it once the image is ready. */
.settings-avatar-entry__spinner {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-shade, var(--color-border-light));
  border: 1px solid var(--color-border);
  pointer-events: none;
}

.settings-avatar-entry__img,
.settings-avatar-entry__initials {
  inline-size: 5.5rem;
  block-size: 5.5rem;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  object-fit: cover;
  background: var(--color-shade, var(--color-border-light));
  border: 1px solid var(--color-border);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-text-subtle);
}

/* The img sits above the spinner tile and stays transparent while loading so
   the spinner shows through (same stacking as the gem LazyImage pattern). */
.settings-avatar-entry__img {
  position: relative;
  z-index: 1;
  background: transparent;
}

.settings-avatar-entry__badge {
  position: absolute;
  inset-inline-end: -2px;
  inset-block-end: -2px;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-primary);
  border: 3px solid var(--color-bg);
  box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.1));
}

.settings-avatar-entry__action {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-primary);
}

.settings-avatar-entry__hint {
  font-size: 0.78rem;
  color: var(--color-text-subtle);
  text-align: center;
}

/* Account deactivation danger card */
.settings-sheet-panel__danger-card {
  margin-block-start: var(--size-5);
  padding: var(--size-4);
  border-radius: var(--rounded-xl, 0.9rem);
  background: var(--color-negative-bg, #fef2f2);
  border: 1px solid var(--color-negative-border, #fecaca);
}

.settings-sheet-panel__danger-title {
  margin: 0 0 var(--size-1);
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--color-negative-text, #b91c1c);
}

.settings-sheet-panel__danger-text {
  margin: 0 0 var(--size-3);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--color-negative-text, #b91c1c);
  opacity: 0.85;
}

/* Neutral info callout (password "last changed", etc.) */
.settings-sheet-panel__callout {
  display: flex;
  gap: var(--size-3);
  margin-block-start: var(--size-4);
  padding: var(--size-4);
  border-radius: var(--rounded-xl, 0.9rem);
  background: var(--color-shade, var(--color-border-light));
  border: 1px solid var(--color-border);
}

.settings-sheet-panel__callout-title {
  margin: 0 0 var(--size-1);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text);
}

.settings-sheet-panel__callout-text {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--color-text-subtle);
}

/* Password requirements panel */
.settings-sheet-panel__requirements {
  padding: var(--size-4);
  border-radius: var(--rounded-xl, 0.9rem);
  background: var(--color-shade, var(--color-border-light));
  border: 1px solid var(--color-border);
}

.settings-sheet-panel__requirements-head {
  display: flex;
  align-items: center;
  gap: var(--size-2);
  margin-block-end: var(--size-3);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-text);
}

/* Studio: logo file input + preview */
.settings-sheet-panel__file {
  display: block;
  inline-size: 100%;
  font-size: 0.8125rem;
  color: var(--color-text-subtle);
}

.settings-sheet-panel__file::file-selector-button {
  margin-inline-end: var(--size-3);
  padding: var(--size-2) var(--size-3);
  border: 0;
  border-radius: var(--rounded-md, 0.5rem);
  background: var(--color-shade, var(--color-border-light));
  color: var(--color-text);
  cursor: pointer;
}

.settings-sheet-panel__logo-card {
  display: flex;
  flex-direction: column;
  gap: var(--size-4);
  margin-block-start: var(--size-4);
  padding: var(--size-4);
  border-radius: var(--rounded-xl, 0.9rem);
  border: 1px solid var(--color-border);
}

.settings-sheet-panel__logo-preview {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--rounded-md, 0.5rem);
  overflow: hidden;
}

/* Theme card list */
.settings-theme-list {
  display: flex;
  flex-direction: column;
  gap: var(--size-3);
  margin-block-end: var(--size-2);
}

.settings-theme-card {
  display: flex;
  flex-direction: column;
  gap: var(--size-2);
  inline-size: 100%;
  padding: var(--size-3);
  text-align: start;
  border-radius: var(--rounded-xl, 0.9rem);
  border: 2px solid var(--color-border);
  background: var(--color-bg);
  cursor: pointer;
}

.settings-theme-card.theme-card--active {
  border-color: var(--color-primary);
  background: var(--color-selected, var(--color-shade));
}

.settings-theme-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--size-2);
}

.settings-theme-card__name {
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.settings-theme-card__desc {
  margin-block-start: 2px;
  font-size: 0.72rem;
  color: var(--color-text-subtle);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.settings-theme-card__swatches {
  display: flex;
  gap: var(--size-2);
}

.settings-theme-card__swatch {
  flex: 1;
  block-size: 1.6rem;
  border-radius: var(--rounded-md, 0.5rem);
  border: 1px solid rgba(0, 0, 0, 0.06);
}

/* ----------------------------------------------------------------------- *
   Profile-picture crop bottom sheet

   Mirrors the desktop crop modal (image_crop_settings.css): header with a
   subtitle, a rectangular dark crop stage with a circular mask, a zoom row and
   a bordered footer. The footer is the gem ImageCropComponent's actions block,
   so its buttons stay inside the image-crop controller scope; dialog#close
   resolves to the SheetComponent's own dialog controller.
 * ----------------------------------------------------------------------- */
/* The crop sheet hugs its content instead of the default near-full-height
   bottom sheet, and drops the dialog focus outline for a clean panel. */
.sheet.image-crop-sheet {
  block-size: auto;
  max-block-size: 90dvh;
}

.sheet.image-crop-sheet:focus,
.sheet.image-crop-sheet:focus-visible {
  outline: none;
}

/* The crop dialog is nested inside the settings sheet, so the descendant rule
   `.settings-sheet .sheet__content` zeroes this sheet's content padding. Carry
   the horizontal inset here instead so the heading, photo, slider and save
   button all align to a consistent margin from the sheet edges. */
.image-crop-sheet__inner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--size-4);
  padding-inline: var(--size-4);
  padding-block-end: var(--size-2);
  /* Suppress iOS double-tap-to-zoom on the sheet chrome (buttons, slider row);
     the cropper stage keeps its own touch-action: none for drag/pinch. */
  touch-action: manipulation;
}

.image-crop-sheet__grabber {
  inline-size: 2.4rem;
  block-size: 5px;
  border-radius: 3px;
  background: var(--color-border);
  margin: 0 auto var(--size-1);
}

.image-crop-sheet__head {
  text-align: left;
}

.image-crop-sheet__title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--color-text);
}

.image-crop-sheet__subtitle {
  margin: var(--size-1) 0 0;
  font-size: 0.8125rem;
  color: var(--color-text-subtle);
}

.image-crop-sheet__crop {
  display: block;
  inline-size: 100%;
}

/* The gem ImageCropComponent's own layout/hide/round rules ship in the gem's
   image_crop.css (now loaded again — the app gallery dialog CSS was renamed to
   image_crop_dialog.css, so the gem file is no longer shadowed). The rules below
   are the crop-sheet-specific overrides on that base, scoped to the crop sheet
   so they never collide with the app modal. The image-crop controller flips
   container/zoom/actions to block/flex via inline styles once a file is picked,
   so they start hidden here. */
.image-crop-sheet .image-crop__container {
  display: none;
  inline-size: auto;
  max-block-size: min(56vh, 420px);
  margin-inline: 0;
  border-radius: var(--rounded-lg);
  background: #1e293b;
  overflow: hidden;
}

.image-crop-sheet .image-crop__container .cropper-container {
  max-block-size: min(56vh, 420px);
}

/* Replace Cropper.js' checkerboard with the stage's solid dark backdrop so no
   squares ever show through — the cover-zoom minimum keeps the circle filled,
   this covers any transparent margins revealed while dragging. */
.image-crop-sheet .cropper-bg {
  background-image: none;
  background-color: #1e293b;
}

.image-crop-sheet .image-crop__image {
  max-inline-size: 100%;
  block-size: auto;
}

.image-crop-sheet .image-crop__zoom {
  display: none;
  align-items: center;
  gap: var(--size-3);
  inline-size: 100%;
  /* The gem base rule caps .image-crop__zoom at max-width: 400px, which pins
     the row narrow; release the cap so it spans the full crop-image width. */
  max-inline-size: none;
  margin-block-start: var(--size-3);
  /* No inline inset so the row is flush with the crop image above (both sit at
     the .image-crop-sheet__inner padding edge). */
  padding-inline: 0;
}

/* On mobile the +/- buttons are redundant (pinch-zoom in the cropper plus the
   full-width slider cover zooming) and only invite double-tap page zoom, so
   they are hidden here. The desktop modal keeps them (image_crop_settings.css). */
.image-crop-sheet .image-crop__zoom-btn {
  display: none;
}

/* The wrapper's flex:1 (grow to fill the row) now comes from the gem's own
   .image-crop__zoom-slider-wrap rule in image_crop.css, which loads again since
   it is no longer shadowed — so it is not re-supplied here. With the +/- buttons
   hidden the wrapper is the row's only flex child and fills it; the slider below
   fills the wrapper so it matches the crop image width above. */
.image-crop-sheet .image-crop__zoom-slider {
  flex: 1;
  /* iOS WebKit keeps native ranges at their ~129px intrinsic width and refuses
     to grow them under flex; strip the native appearance and force full width
     so the slider fills the row (and thus matches the crop image above). */
  -webkit-appearance: none;
  appearance: none;
  inline-size: 100%;
  min-inline-size: 0;
  /* A 44px-tall hit area for the thumb; the visible track stays thin (styled
     below), the height only enlarges the touch target. pan-x lets vertical
     jitter scroll/settle without hijacking the drag as a page gesture. */
  block-size: 2.75rem;
  background: transparent;
  touch-action: pan-x;
  cursor: pointer;
}

/* appearance: none removes the native track/thumb AND accent-color, so the
   slider must be re-styled explicitly (theme primary, no hardcoded hex).
   Separate WebKit and Firefox selectors — a shared list would drop both rules
   in each engine. Track height 6px, thumb centred over it, 44px hit area kept
   via the slider's own block-size. */
.image-crop-sheet .image-crop__zoom-slider::-webkit-slider-runnable-track {
  block-size: 6px;
  border-radius: 999px;
  background: var(--color-border);
}

.image-crop-sheet .image-crop__zoom-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  inline-size: 20px;
  block-size: 20px;
  margin-block-start: -7px;
  border-radius: 50%;
  background: var(--color-primary);
  border: 2px solid var(--color-bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.image-crop-sheet .image-crop__zoom-slider::-moz-range-track {
  block-size: 6px;
  border-radius: 999px;
  background: var(--color-border);
}

.image-crop-sheet .image-crop__zoom-slider::-moz-range-thumb {
  inline-size: 20px;
  block-size: 20px;
  border-radius: 50%;
  background: var(--color-primary);
  border: 2px solid var(--color-bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

/* Footer: the image-crop controller's actions block, stacked for thumb reach
   (re-pick link on top, cancel + save side by side at the bottom). */
.image-crop-sheet .image-crop__actions {
  display: none;
  flex-direction: column;
  align-items: stretch;
  gap: var(--size-3);
  margin-block-start: var(--size-2);
  padding-block-start: var(--size-4);
  border-block-start: 1px solid var(--color-border-light);
}

.image-crop-sheet__choose {
  align-self: center;
  padding: var(--size-1);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-link);
  cursor: pointer;
}

.image-crop-sheet .image-crop__error {
  display: none;
  margin-block-start: var(--size-1);
  font-size: 0.8125rem;
  color: var(--color-negative, #dc2626);
}

/* Round crop overlay (controller adds .round to the cropper internals) */
.image-crop-sheet .cropper-crop-box.round,
.image-crop-sheet .cropper-face.round {
  border-radius: 50% !important;
  overflow: hidden !important;
}

.image-crop-sheet .cropper-view-box.round {
  border-radius: 50% !important;
  outline: 2px solid rgba(255, 255, 255, 0.92) !important;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.3) !important;
}

.image-crop-sheet .cropper-container.round .cropper-line,
.image-crop-sheet .cropper-container.round .cropper-point {
  display: none !important;
}

.image-crop-sheet .cropper-container.round .cropper-modal {
  background-color: rgba(15, 23, 42, 0.62) !important;
}


/* ===== theme_settings.css ===== */
/* Theme settings: theme cards and component previews */

/* Theme card grid */
.theme-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1rem;
}

/* Individual theme card */
.theme-card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem;
  border-radius: 0.75rem;
  background: var(--color-bg);
  border: 2px solid var(--color-border-light);
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
}

.theme-card:hover {
  border-color: var(--color-border-dark);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px oklch(0 0 0 / 0.08);
}

/* Currently selected theme. Written as a flat selector on purpose: the
   Sass-style `&--active` nesting suffix is invalid in plain CSS nesting and
   would be silently dropped, leaving the active theme unmarked. */
.theme-card--active {
  position: relative;
  border-color: var(--color-text);
  box-shadow: 0 4px 12px oklch(0 0 0 / 0.1);
  background: oklch(0.97 min(0.01, var(--primary-chroma)) var(--primary-hue));
  transform: translateY(-1px);
}

.theme-card--active::after {
  content: "";
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--color-text);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='white'%3E%3Cpath fill-rule='evenodd' d='M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z' clip-rule='evenodd'/%3E%3C/svg%3E");
  background-size: 0.875rem;
  background-repeat: no-repeat;
  background-position: center;
}

/* Theme card header */
.theme-card__name {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-text);
}

.theme-card__description {
  font-size: 0.75rem;
  color: var(--color-text-subtle);
  line-height: 1.4;
}

/* Color swatches row inside the card */
.theme-card__swatches {
  display: flex;
  gap: 0.375rem;
}

.theme-card__swatch {
  flex: 1;
  height: 2rem;
  border-radius: 0.375rem;
}

.theme-card__swatch:first-child {
  border-radius: 0.375rem 0 0 0.375rem;
}

.theme-card__swatch:last-child {
  border-radius: 0 0.375rem 0.375rem 0;
}

/* Section titles */
.theme-section-title {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-subtle);
  margin-bottom: 0.75rem;
}

/* Palette strip for color scale visualization */
.palette-strip {
  display: flex;
  border-radius: 0.75rem;
  overflow: hidden;
  border: 1px solid var(--color-border-light);
}

.palette-strip__stop {
  flex: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 0.375rem;
  min-height: 3.5rem;
}

.palette-strip__label {
  font-size: 0.5625rem;
  font-weight: 600;
  color: white;
  mix-blend-mode: difference;
}

.palette-section {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.palette-section__label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-subtle);
}

/* Demo card for component previews */
.theme-demo-card {
  background: var(--color-border-light);
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  padding: 1rem;
}

/* Live readout of the active theme's key colors. The colored area and the
   text label are kept separate so the label never sits on a swatch whose
   contrast is unknown (light themes like azure-horizon / sunset-glow). */
.theme-color-readout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
  margin-bottom: 2rem;
}

.theme-color-readout__tile {
  display: flex;
  flex-direction: column;
  border-radius: 0.75rem;
  overflow: hidden;
  border: 1px solid var(--color-border-light);
  background: var(--color-bg);
}

.theme-color-readout__swatch {
  min-height: 4rem;
}

.theme-color-readout__body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding: 0.625rem 0.75rem;
}

.theme-color-readout__label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text);
}

.theme-color-readout__role {
  font-size: 0.6875rem;
  color: var(--color-text-subtle);
}


/* ===== theme_switcher.css ===== */
/* Theme switcher navbar dropdown cards */

.theme-switcher-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem;
  border-radius: 0.5rem;
  border: 2px solid var(--color-border-light);
  background: var(--color-bg);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s, box-shadow 0.15s;

  &:hover {
    border-color: var(--color-border-dark);
    box-shadow: 0 2px 8px oklch(0 0 0 / 0.06);
  }

  &.theme-card--active {
    border-color: var(--color-text);
    box-shadow: 0 2px 8px oklch(0 0 0 / 0.1);
    background: oklch(0.97 min(0.01, var(--primary-chroma)) var(--primary-hue));
  }
}

.theme-switcher-card__header {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.theme-switcher-card__name {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text);
}

.theme-switcher-card__description {
  font-size: 0.6875rem;
  color: var(--color-text-subtle);
}

.theme-switcher-card__swatches {
  display: flex;
  gap: 0.25rem;
}

.theme-switcher-card__swatch {
  flex: 1;
  height: 1.5rem;
  border-radius: 0.25rem;
}

.theme-switcher-card__swatch:first-child {
  border-radius: 0.25rem 0 0 0.25rem;
}

.theme-switcher-card__swatch:last-child {
  border-radius: 0 0.25rem 0.25rem 0;
}


/* ===== upload_popup.css ===== */
/* Upload Progress Popup — fixed bottom-right panel.
   Driven by the `upload-popup` Stimulus controller (UploadPopupComponent). */

.upload-popup {
  position: fixed;
  bottom: var(--size-6, 1.5rem);
  right: var(--size-6, 1.5rem);
  width: 380px;
  max-height: 560px;
  z-index: 50;
  border-radius: var(--radius-3, 0.75rem);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 1;
  transform: translateX(0);
  transition:
    opacity 0.45s ease,
    transform 0.55s var(--ease-standard),
    width 0.5s var(--ease-standard),
    max-width 0.5s var(--ease-standard);
}

.upload-popup[hidden] {
  display: none;
}

/* Slides in from the right when the popup first appears. JS removes the
   class after one frame so the transition runs. */
.upload-popup--entering {
  opacity: 0;
  transform: translateX(calc(100% + var(--size-6, 1.5rem)));
}

/* Slides out to the right when uploads finish and the popup hides itself. */
.upload-popup--closing {
  opacity: 0;
  transform: translateX(calc(100% + var(--size-6, 1.5rem)));
  pointer-events: none;
}

.upload-popup__header {
  display: flex;
  align-items: center;
  gap: var(--size-2, 0.5rem);
  padding: var(--size-3, 0.75rem) var(--size-4, 1rem);
  border-bottom: 1px solid var(--color-border);
}

.upload-popup__title {
  font-size: var(--text-sm);
  font-weight: 600;
}

.upload-popup__counter-line {
  padding: var(--size-2, 0.5rem) var(--size-4, 1rem);
  border-bottom: 1px solid var(--color-border);
}

.upload-popup__counter-line[hidden] {
  display: none;
}

.upload-popup__counter {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  font-variant-numeric: tabular-nums;
}

.upload-popup__close {
  margin-left: auto;
  padding: var(--size-1, 0.25rem);
  border-radius: var(--radius-1, 0.25rem);
  cursor: pointer;
  background: transparent;
  border: none;
  color: var(--color-text-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
}
.upload-popup__close:hover {
  background-color: var(--color-secondary);
  color: var(--color-text);
}

.upload-popup__list {
  overflow-y: auto;
  max-height: 340px;
  padding: var(--size-2, 0.5rem);
}

.upload-popup__item {
  padding: var(--size-2, 0.5rem) var(--size-2, 0.5rem);
  border-radius: var(--radius-2, 0.5rem);
  max-height: 4rem;
  overflow: hidden;
  transition:
    opacity 0.6s ease,
    max-height 0.8s var(--ease-standard) 0.2s,
    padding 0.8s var(--ease-standard) 0.2s,
    margin 0.8s var(--ease-standard) 0.2s;
}

.upload-popup__item--done {
  opacity: 0;
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
}

/* Failed row: keeps the file name, hides the progress track and shows the
   error message in red instead. Allowed to grow past the single-row height so
   long messages / file names wrap rather than clip. */
.upload-popup__item--failed {
  max-height: none;
}

.upload-popup__item--failed .upload-popup__progress-track {
  display: none;
}

.upload-popup__item-error {
  font-size: var(--text-xs);
  color: var(--color-negative, #dc2626);
  word-break: break-word;
}

.upload-popup__item-error[hidden] {
  display: none;
}

.upload-popup__item-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--size-1-5, 0.375rem);
}

.upload-popup__item-name {
  font-size: var(--text-xs);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 260px;
}

.upload-popup__item-percent {
  font-size: var(--text-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-subtle);
}

.upload-popup__progress-track {
  height: 0.375rem;
  background-color: var(--primary-100);
  border-radius: 9999px;
  overflow: hidden;
  width: 100%;
}

.upload-popup__progress-fill {
  height: 100%;
  background-color: var(--primary-600);
  border-radius: 9999px;
  width: 100%;
  transform: translateX(calc(var(--upload-fill, 0%) - 100%));
  transform-origin: left;
  /* Long soft-landing curve: progress events arrive in bursts, the fill
     glides between them instead of stepping. Deliberately no overshoot —
     a progress bar must never show more than has actually happened.
     Slides via transform (compositor-only) in the clipped, pill-radius
     track, so the rounded right cap stays crisp at every fill level. */
  transition: transform 0.35s var(--ease-out-soft);
}

.upload-popup__footer {
  display: flex;
  gap: var(--size-2, 0.5rem);
  justify-content: flex-end;
  padding: var(--size-2-5, 0.625rem) var(--size-3, 0.75rem);
  border-top: 1px solid var(--color-border);
}

/* Muted cancel button: subtle text colour, no fill, no border. Reads as a
   destructive option only on hover. */
.upload-popup__cancel {
  color: var(--color-text-subtle);
  font-size: var(--text-xs);
}

.upload-popup__cancel:hover {
  color: var(--color-negative, #dc2626);
}

/* Minimized state — popup collapses to title bar + counter line; uploads
   keep running visibly. Click on the chevron re-expands. */
.upload-popup--minimized {
  width: 480px;
  max-width: calc(100vw - var(--size-12, 3rem));
}

.upload-popup--minimized .upload-popup__list,
.upload-popup--minimized .upload-popup__footer {
  display: none;
}

.upload-popup--minimized .upload-popup__counter-line {
  border-bottom: none;
}

/* Mobile (<640px): the popup becomes a non-modal bottom sheet — full width,
   docked to the bottom edge, rounded top corners and a decorative grabber
   pill. Slides up/down instead of in from the right. Same motion language as
   sheet.css. JS is unchanged; only position and transform direction differ. */
@media (width < 40rem) {
  .upload-popup {
    inset-inline: 0;
    bottom: 0;
    width: auto;
    max-width: 100%;
    max-height: min(80dvh, 560px);
    border-radius: var(--rounded-xl, 0.75rem) var(--rounded-xl, 0.75rem) 0 0;
    border-inline: 0;
    border-block-end: 0;
    border-block-start: 1px solid var(--color-border);
  }

  /* Decorative grabber pill — no drag gesture, minimize/close still steer. */
  .upload-popup::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 0.25rem;
    background-color: var(--color-border);
    border-radius: 9999px;
    margin: 0.5rem auto 0;
    flex: none;
  }

  /* Slide down out of view on enter/close instead of off to the right. */
  .upload-popup--entering,
  .upload-popup--closing {
    transform: translateY(calc(100% + var(--size-6, 1.5rem)));
  }

  /* Full-width bar at the bottom edge when minimized (not 480px). */
  .upload-popup--minimized {
    width: auto;
    max-width: 100%;
  }

  /* File names use the full sheet width; ellipsis still applies. */
  .upload-popup__item-name {
    max-width: none;
  }

  /* Keep the sheet from eating the screen when many files are queued. */
  .upload-popup__list {
    max-height: min(340px, 45dvh);
  }

  /* Respect the iPhone home indicator on the docked footer. */
  .upload-popup__footer {
    padding-block-end: max(var(--size-2-5, 0.625rem), env(safe-area-inset-bottom));
  }
}


/* ===== upload_dropzone.css ===== */
/* Upload Dropzone — drag-and-drop file picker tile (UploadDropzoneComponent).
   Wires to the page-level `upload` Stimulus controller. */

.upload-dropzone {
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-3, 0.75rem);
  padding: var(--size-12, 3rem) var(--size-8, 2rem);
  text-align: center;
  transition: border-color 0.15s ease, background-color 0.15s ease;
  cursor: pointer;
}

.upload-dropzone:hover,
.upload-dropzone--active {
  border-color: var(--primary-400);
  background-color: var(--primary-50);
}

.upload-dropzone__content {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Compact variant used inside the gallery show header. Smaller than the
   default dropzone but tall enough that the icon, hint and format line
   sit comfortably stacked. */
.upload-dropzone--compact {
  padding: var(--size-5, 1.25rem) var(--size-4, 1rem);
}

.upload-dropzone--compact .upload-dropzone__content {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--size-1, 0.25rem);
}

.upload-dropzone--compact .upload-dropzone__icon {
  flex-shrink: 0;
}

.upload-dropzone--compact .upload-dropzone__text {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-text-primary, #18181b);
}

.upload-dropzone--compact .upload-dropzone__browse {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--primary-600, #2563eb);
  font-weight: 600;
  cursor: pointer;
}

.upload-dropzone--compact .upload-dropzone__browse:hover {
  text-decoration: underline;
}

.upload-dropzone--compact .upload-dropzone__hint {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-subtle, #6b7280);
}


/* ===== post_upload_popup.css ===== */
/* Post-upload popup body — reuses the gui_components `.upload-popup` outer
   chrome (border/shadow/slide), but swaps in a "choose what to do next" body
   (create gallery / add to gallery) rendered by PostUploadPopupComponent. */

.upload-popup__body {
  padding: var(--size-3, 0.75rem) var(--size-4, 1rem);
}

.upload-popup__body[hidden] {
  display: none;
}

.upload-popup__hint {
  font-size: var(--text-xs);
  color: var(--color-text-subtle);
  margin-bottom: var(--size-3, 0.75rem);
}

.upload-popup__choice {
  display: flex;
  flex-direction: column;
  gap: var(--size-2, 0.5rem);
}

.upload-popup__choice-button {
  width: 100%;
  justify-content: flex-start;
}

.upload-popup__form {
  display: flex;
  flex-direction: column;
  gap: var(--size-2, 0.5rem);
}

.upload-popup__label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text);
}

.upload-popup__input {
  width: 100%;
  padding: var(--size-2, 0.5rem) var(--size-3, 0.75rem);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2, 0.5rem);
  font-size: var(--text-sm);
  background-color: var(--color-bg);
  color: var(--color-text);
}

/* Prevent iOS Safari from zooming when the field is focused inside the
   bottom sheet — inputs below 16px trigger an auto-zoom on focus. */
@media (width < 40rem) {
  .upload-popup__input {
    font-size: 1rem;
  }
}

.upload-popup__input:focus {
  outline: none;
  border-color: var(--primary-500);
  box-shadow: 0 0 0 3px var(--primary-100);
}

.upload-popup__form-actions {
  display: flex;
  gap: var(--size-2, 0.5rem);
  justify-content: flex-end;
  margin-top: var(--size-1, 0.25rem);
}

/* Success beat — a one-shot "upload done" moment. The controller flips
   data-mounted to "true" as the popup settles into its resting slot; the
   header check pops and the choice rows stagger in. transform/opacity only
   (no layout shift), total run ≤600ms, and it fires exactly once per
   appearance because data-mounted is set once and cleared on close(). */
.upload-popup__status {
  display: inline-flex;
  align-items: center;
  color: var(--color-success-icon);
}

@keyframes post-upload-icon-pop {
  0%   { transform: scale(0.8); opacity: 0; }
  60%  { transform: scale(1.12); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

@keyframes post-upload-choice-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.upload-popup--post[data-mounted="true"] .upload-popup__status svg,
.post-upload-choice-sheet[data-mounted="true"] .upload-popup__status svg {
  animation: post-upload-icon-pop 0.35s var(--ease-spring) 0.15s both;
}

/* Hint + the two choice buttons stagger in behind the icon. Buttons stay
   clickable throughout — opacity/translate never block pointer events and we
   deliberately leave pointer-events alone. The mobile bottom-sheet variant
   (#post-upload-choice-sheet) reuses the same class names, so it plays the
   same beat when its data-mounted flips. */
.upload-popup--post[data-mounted="true"] .upload-popup__choice > *,
.post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > * {
  animation: post-upload-choice-in 0.2s ease-out both;
}

.upload-popup--post[data-mounted="true"] .upload-popup__choice > *:nth-child(1),
.post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *:nth-child(1) {
  animation-delay: 0.2s;
}

.upload-popup--post[data-mounted="true"] .upload-popup__choice > *:nth-child(2),
.post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *:nth-child(2) {
  animation-delay: 0.25s;
}

.upload-popup--post[data-mounted="true"] .upload-popup__choice > *:nth-child(3),
.post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *:nth-child(3) {
  animation-delay: 0.3s;
}

/* Reduced motion: a plain fade, no pop and no stagger. */
@media (prefers-reduced-motion: reduce) {
  @keyframes post-upload-icon-pop {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  @keyframes post-upload-choice-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  .upload-popup--post[data-mounted="true"] .upload-popup__status svg,
  .post-upload-choice-sheet[data-mounted="true"] .upload-popup__status svg {
    animation: post-upload-icon-pop 0.2s ease-out both;
  }

  .upload-popup--post[data-mounted="true"] .upload-popup__choice > *,
  .upload-popup--post[data-mounted="true"] .upload-popup__choice > *:nth-child(1),
  .upload-popup--post[data-mounted="true"] .upload-popup__choice > *:nth-child(2),
  .upload-popup--post[data-mounted="true"] .upload-popup__choice > *:nth-child(3),
  .post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *,
  .post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *:nth-child(1),
  .post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *:nth-child(2),
  .post-upload-choice-sheet[data-mounted="true"] .upload-popup__choice > *:nth-child(3) {
    animation: post-upload-choice-in 0.2s ease-out both;
    animation-delay: 0s;
  }
}

/* Mobile bottom-sheet variant of the post-upload choice dialog (<640px). The
   gem SheetComponent renders a near-full-height .sheet--bottom by default; the
   choice UI is only two buttons, so collapse it to its content height and cap
   it. A decorative grabber pill (::before) echoes the other bottom sheets; the
   sheet is dismissed by swipe/backdrop/Esc, not a drag gesture, so no handle
   element is wired. */
.post-upload-choice-sheet {
  block-size: auto;
  max-block-size: 80dvh;
}

.post-upload-choice-sheet::before {
  content: "";
  position: absolute;
  inset-block-start: 0.5rem;
  inset-inline: 0;
  margin-inline: auto;
  width: 2.5rem;
  height: 0.3125rem;
  border-radius: 9999px;
  background-color: var(--color-border);
}

/* Extra top room clears the grabber pill; the bottom padding respects the iOS
   home indicator. */
.post-upload-choice-sheet .sheet__content {
  block-size: auto;
  padding-block-start: var(--size-7, 2rem);
  padding-block-end: max(var(--size-6, 1.5rem), env(safe-area-inset-bottom));
}

.post-upload-choice-sheet__head {
  display: flex;
  align-items: center;
  gap: var(--size-2, 0.5rem);
  margin-block-end: var(--size-3, 0.75rem);
}

.post-upload-choice-sheet__title {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-text);
}


/* ===== empty_polaroid_base.css ===== */
/* ============================================================
 * Empty-state "polaroid + dashed glow" — shared base.
 * Primitives and keyframes shared by the two photographer empty
 * states: the all-images dropzone (`images_empty.css`) and the
 * empty-gallery / no-cover shooting card (`shoot_empty_card.css`).
 * Plain CSS has no `@extend`, so the shared parts are additive
 * `.ep-*` utility classes set NEXT TO the existing BEM classes;
 * per-component deltas stay as overrides in the two variant files.
 * Imported BEFORE both variant files in `application.css` (app CSS
 * is unlayered — import order is the cascade), so a variant rule of
 * equal specificity always wins over its `.ep-*` counterpart.
 * ============================================================ */

/* ----- Shared keyframes -----
 * Parametrized via custom properties so each component keeps its
 * own timings and values: every `var()` resolves to a constant, so
 * interpolation is identical to the old per-component keyframes.
 * The strongly divergent three-polaroid floats/drop/catch of the
 * all-images stack stay unshared in `animations.css`. */

@keyframes ep-breathe {
  0%, 100% { opacity: var(--ep-breathe-min); transform: scale(1); }
  50%      { opacity: var(--ep-breathe-max); transform: scale(var(--ep-breathe-scale)); }
}

@keyframes ep-float {
  0%, 100% { transform: rotate(var(--ep-float-rot)) translateY(0); }
  50%      { transform: rotate(var(--ep-float-rot-mid)) translateY(-0.25rem); }
}

@keyframes ep-arrow-bob {
  0%, 100% { transform: translateY(-0.1rem); opacity: var(--ep-arrow-min); }
  50%      { transform: translateY(var(--ep-arrow-to)); opacity: 1; }
}

@keyframes ep-dot-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary-500, #3b82f6) 70%, transparent); }
  70%  { box-shadow: 0 0 0 var(--ep-pulse-ring, 6px) color-mix(in srgb, var(--primary-500, #3b82f6) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary-500, #3b82f6) 0%, transparent); }
}

/* ----- Surface: dashed frame + breathing glow -----
 * Deltas per variant: padding, border-radius, background gradients,
 * width/cursor, and the glow shape/opacity/timing (via `--ep-breathe-*`
 * set on the variant's `::before`). */
.ep-surface {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  border: 2px dashed color-mix(in srgb, var(--primary-400, #60a5fa) 60%, var(--color-border, #e5e7eb));
  isolation: isolate;
  overflow: hidden;
  transition:
    border-color 200ms ease,
    background 200ms ease,
    transform 200ms ease,
    box-shadow 200ms ease;
}

.ep-surface::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  animation: ep-breathe var(--ep-breathe-duration, 5.5s) ease-in-out infinite;
}

/* ----- Polaroid tile -----
 * Deltas per variant: position, size, border-radius, padding,
 * box-shadow, transform/animation, and the `::after` caption line's
 * bottom/width/height. */
.ep-polaroid {
  background: var(--color-bg-main, #ffffff);
  display: flex;
  align-items: stretch;
  justify-content: stretch;
}

.ep-polaroid::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 999px;
  background: color-mix(in srgb, var(--color-text-subtle, #6b7280) 35%, transparent);
}

/* Inner photo surface — identical between both components except the
 * corner radius, which stays a per-variant delta. */
.ep-polaroid-inner {
  flex: 1;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--primary-200, #bfdbfe) 45%, transparent) 0%,
      color-mix(in srgb, var(--primary-100, #dbeafe) 70%, transparent) 55%,
      color-mix(in srgb, var(--color-bg-shade, #f1f5f9) 90%, transparent) 100%);
  display: grid;
  place-items: center;
  color: color-mix(in srgb, var(--primary-700, #1d4ed8) 60%, transparent);
  position: relative;
  overflow: hidden;
}

.ep-polaroid-inner::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.6) 0%, transparent 35%),
    radial-gradient(circle at 70% 80%, color-mix(in srgb, var(--primary-300, #93c5fd) 50%, transparent) 0%, transparent 40%);
  pointer-events: none;
}

/* ----- Eyebrow chip + pulsing dot -----
 * Deltas per variant: gap, font-size, padding, margin-bottom (chip)
 * and size + `--ep-pulse-ring` (dot). */
.ep-eyebrow {
  display: inline-flex;
  align-items: center;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary-700, #1d4ed8);
  background: color-mix(in srgb, var(--primary-100, #dbeafe) 80%, white);
  border: 1px solid color-mix(in srgb, var(--primary-300, #93c5fd) 50%, transparent);
  border-radius: 999px;
}

.ep-eyebrow-dot {
  border-radius: 999px;
  background: var(--primary-600, #2563eb);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary-500, #3b82f6) 70%, transparent);
  animation: ep-dot-pulse 1.6s ease-out infinite;
}

[data-color-scheme="dark"] .ep-eyebrow {
  background: color-mix(in srgb, var(--primary-900, #1e3a8a) 60%, transparent);
  color: color-mix(in srgb, var(--primary-200, #bfdbfe) 85%, white);
  border-color: color-mix(in srgb, var(--primary-700, #1d4ed8) 60%, transparent);
}

/* ----- Idle breathe for the branded empty-state circle (#1247) -----
 * Additive class for `Components::Ui::BrandedEmptyStateMedia`, so the five
 * branded empty states (Kunden, Announcements, Shootings-Galerie, Members)
 * share the delight already carried by the photo empty state. Reuses the
 * shared `ep-breathe` keyframe but drives ONLY its transform track: it sets
 * `--ep-breathe-scale` while the opacity custom props stay unset on purpose,
 * so `opacity: var(--ep-breathe-min)` is invalid-at-computed-value-time and
 * dropped from the keyframe — the circle scales as a whole, its tint and the
 * plus badge never fade. Compositor-friendly (transform-only). Duration and
 * curve match the photo empty state (`images_empty.css`: 5.5s ease-in-out).
 * The global `prefers-reduced-motion` reset (gem `base.css`) freezes it. */
.ep-media-breathe {
  --ep-breathe-scale: 1.04;
  transform-origin: center;
  animation: ep-breathe 5.5s ease-in-out infinite;
}


/* ===== images_empty.css ===== */
/* ============================================================
 * Photo-shooting "All images" — empty state.
 * A generous, photographer-flavored dropzone that itself accepts
 * files. The whole tile is the drop target, hence no separate
 * compact dropzone is rendered above when the grid is empty.
 *
 * Shares its dashed-glow surface, polaroid tiles, eyebrow chip and
 * pulsing dot with `shoot_empty_card.css` via the additive `.ep-*`
 * primitives in `empty_polaroid_base.css`; only the per-component
 * deltas (sizes, timings, gradients, the three-polaroid stack and
 * drag-drop states) live here.
 * ============================================================ */

/* The empty state is the lone child of #images-grid, whose default
   masonry layout is a CSS multi-column flow (display:block + column-width).
   Tailwind's `col-span-full` only works in a CSS grid, so it leaves the tile
   stuck in a single ~250px column. `column-span: all` makes it break out and
   span the full container width; harmless in the grid/flex layouts. */
#images-empty-state {
  column-span: all;
  width: 100%;
}

/* Surface deltas — shared frame + transition come from `.ep-surface`. */
.images-empty {
  width: 100%;
  padding: clamp(2.5rem, 5vw, 4.5rem) clamp(1.25rem, 4vw, 3rem);
  border-radius: 1.25rem;
  background:
    radial-gradient(120% 80% at 50% 0%, var(--primary-50, #eff6ff) 0%, transparent 60%),
    linear-gradient(180deg, var(--color-bg-main, #ffffff) 0%, var(--color-bg-shade, #f8fafc) 100%);
  cursor: pointer;
}

/* Breathing glow — shape + timing here, boilerplate + `ep-breathe`
   animation come from `.ep-surface::before`. */
.images-empty::before {
  --ep-breathe-duration: 5.5s;
  --ep-breathe-min: 0.45;
  --ep-breathe-max: 0.85;
  --ep-breathe-scale: 1.04;
  background:
    radial-gradient(60% 50% at 50% 30%, color-mix(in srgb, var(--primary-200, #bfdbfe) 55%, transparent) 0%, transparent 70%);
  opacity: 0.55;
}

/* Dragover / hover */
.images-empty:hover,
.images-empty.upload-dropzone--active {
  border-color: var(--primary-500, #3b82f6);
  background:
    radial-gradient(120% 80% at 50% 0%, var(--primary-100, #dbeafe) 0%, transparent 65%),
    linear-gradient(180deg, var(--color-bg-main, #ffffff) 0%, var(--primary-50, #eff6ff) 100%);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary-500, #3b82f6) 18%, transparent);
}

.images-empty.upload-dropzone--active {
  transform: scale(1.005);
  border-style: solid;
}

.images-empty.upload-dropzone--active .images-empty__polaroids {
  animation: images-empty-catch 600ms var(--ease-spring) both;
}

/* ----- Polaroid stack ----- */

.images-empty__stage {
  position: relative;
  width: min(100%, 30rem);
  height: 16rem;
  margin-bottom: 1.75rem;
}

.images-empty__polaroids {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  transition: transform 250ms ease;
}

/* Polaroid deltas — shared tile surface comes from `.ep-polaroid`. */
.images-empty__polaroid {
  position: absolute;
  width: 12rem;
  height: 14.5rem;
  border-radius: 0.4rem;
  padding: 0.5rem 0.5rem 1.6rem;
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.04),
    0 12px 28px -10px rgba(15, 23, 42, 0.18),
    0 4px 8px -4px rgba(15, 23, 42, 0.08);
}

.images-empty__polaroid::after {
  bottom: 0.55rem;
  width: 2.25rem;
  height: 0.18rem;
}

.images-empty__polaroid-inner {
  border-radius: 0.2rem;
}

.images-empty__polaroid--1 {
  transform: rotate(-12deg) translate(-4.6rem, 0.6rem);
  animation: images-empty-float-1 6s ease-in-out infinite;
}
.images-empty__polaroid--2 {
  transform: rotate(3deg) translate(0.1rem, -0.4rem);
  z-index: 2;
  animation: images-empty-float-2 6s ease-in-out infinite;
}
.images-empty__polaroid--3 {
  transform: rotate(11deg) translate(4.7rem, 0.5rem);
  animation: images-empty-float-3 6s ease-in-out infinite;
}

/* Falling polaroid hint above the stack */
.images-empty__hint {
  position: absolute;
  left: 50%;
  top: -1.4rem;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  pointer-events: none;
  z-index: 3;
}

.images-empty__hint-card {
  width: 2.6rem;
  height: 3.1rem;
  border-radius: 0.3rem;
  background: var(--color-bg-main, #ffffff);
  padding: 0.25rem 0.25rem 0.55rem;
  box-shadow:
    0 8px 20px -6px rgba(15, 23, 42, 0.25),
    0 2px 4px -2px rgba(15, 23, 42, 0.12);
  display: flex;
  align-items: stretch;
  position: relative;
  animation: images-empty-drop 2.2s var(--ease-spring) infinite;
}

.images-empty__hint-card::after {
  content: "";
  flex: 1;
  border-radius: 0.15rem;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--primary-300, #93c5fd) 60%, transparent),
    color-mix(in srgb, var(--primary-100, #dbeafe) 80%, transparent));
}

.images-empty__hint-arrow {
  width: 1rem;
  height: 1rem;
  color: color-mix(in srgb, var(--primary-600, #2563eb) 80%, transparent);
  --ep-arrow-min: 0.55;
  --ep-arrow-to: 0.35rem;
  animation: ep-arrow-bob 2.2s ease-in-out infinite;
}

/* ----- Copy ----- */

/* Eyebrow deltas — shared chip styling comes from `.ep-eyebrow`. */
.images-empty__eyebrow {
  gap: 0.5rem;
  font-size: 0.6875rem;
  padding: 0.3rem 0.7rem;
  margin-bottom: 0.9rem;
}

/* Dot deltas — shared styling + `ep-dot-pulse` come from `.ep-eyebrow-dot`. */
.images-empty__eyebrow-dot {
  width: 0.45rem;
  height: 0.45rem;
  --ep-pulse-ring: 6px;
}

.images-empty__title {
  font-size: clamp(1.5rem, 2.5vw, 2rem);
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--color-text-primary, #0f172a);
  margin: 0 0 0.55rem;
  line-height: 1.15;
}

.images-empty__description {
  font-size: 1rem;
  color: var(--color-text-subtle, #64748b);
  margin: 0 auto 1.6rem;
  max-width: 28rem;
  line-height: 1.5;
}

.images-empty__description strong {
  color: var(--color-text-primary, #0f172a);
  font-weight: 600;
}

.images-empty__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 1.25rem;
}

.images-empty__primary {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.75rem 1.25rem;
  font-size: 0.95rem;
  font-weight: 600;
  border-radius: 0.625rem;
  background: var(--primary-600, #2563eb);
  color: #ffffff;
  border: none;
  cursor: pointer;
  box-shadow:
    0 6px 16px -6px color-mix(in srgb, var(--primary-600, #2563eb) 60%, transparent),
    0 1px 0 0 color-mix(in srgb, var(--primary-700, #1d4ed8) 70%, transparent) inset;
  transition: transform 120ms ease, box-shadow 200ms ease, background 200ms ease;
}

.images-empty__primary:hover {
  background: var(--primary-700, #1d4ed8);
  transform: translateY(-1px);
  box-shadow:
    0 10px 22px -8px color-mix(in srgb, var(--primary-600, #2563eb) 60%, transparent),
    0 1px 0 0 color-mix(in srgb, var(--primary-700, #1d4ed8) 70%, transparent) inset;
}

.images-empty__primary:active {
  transform: translateY(0);
}

.images-empty__or {
  font-size: 0.875rem;
  color: var(--color-text-subtle, #94a3b8);
  font-weight: 500;
}

.images-empty__drop-cue {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--primary-700, #1d4ed8);
}

.images-empty__drop-cue-icon {
  width: 1.05rem;
  height: 1.05rem;
}

.images-empty__formats {
  margin-top: 1.6rem;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-subtle, #94a3b8);
}

.images-empty__formats-divider {
  width: 0.25rem;
  height: 0.25rem;
  border-radius: 999px;
  background: currentColor;
  opacity: 0.5;
}

/* Mobile: the dashed drop-zone frame is a desktop metaphor — on touch there
   is nothing to drag in. Drop the border and background, shrink the polaroid
   stage and center the empty state vertically between tabs and bottom nav
   (design handoff: Mobile Optimierung, screen 5). */
@media (width < 48rem) {
  .images-empty {
    padding: 2.5rem 1.25rem;
    border-radius: 1rem;
    border: none;
    background: none;
    min-height: 55dvh;
    justify-content: center;
  }
  .images-empty__stage {
    height: 8rem;
    margin-bottom: 1.25rem;
  }
  .images-empty__polaroid {
    width: 6.5rem;
    height: 8rem;
  }
  .images-empty__hint {
    display: none;
  }
  .images-empty__title {
    font-size: 1.35rem;
  }
  .images-empty__description {
    font-size: 0.9375rem;
    max-width: 18rem;
  }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .images-empty::before,
  .images-empty__polaroid--1,
  .images-empty__polaroid--2,
  .images-empty__polaroid--3,
  .images-empty__hint-card,
  .images-empty__hint-arrow,
  .images-empty__eyebrow-dot {
    animation: none !important;
  }
}

/* Dark mode tuning */
[data-color-scheme="dark"] .images-empty {
  background:
    radial-gradient(120% 80% at 50% 0%, color-mix(in srgb, var(--primary-900, #1e3a8a) 35%, transparent) 0%, transparent 60%),
    linear-gradient(180deg, color-mix(in srgb, var(--color-bg-main, #0b0f17) 100%, transparent) 0%, color-mix(in srgb, var(--color-bg-shade, #0f172a) 100%, transparent) 100%);
  border-color: color-mix(in srgb, var(--primary-500, #3b82f6) 55%, var(--color-border, #1f2937));
}

[data-color-scheme="dark"] .images-empty__polaroid {
  background: #0f172a;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.04),
    0 14px 30px -10px rgba(0, 0, 0, 0.55),
    0 4px 8px -4px rgba(0, 0, 0, 0.4);
}

[data-color-scheme="dark"] .images-empty__hint-card {
  background: #0f172a;
}


/* ===== page_drop_overlay.css ===== */
/* ============================================================
 * Page-wide drag-and-drop overlay (photo-shooting images).
 * Revealed by the upload Stimulus controller when files are
 * dragged anywhere over the page. It is positioned over the
 * #main content area (rect set inline by JS, respecting the
 * collapsible sidebar) and captures the drop itself.
 *
 * Full-bleed: the whole content area becomes the colored drop
 * surface — no bordered card. Colors use the brand --primary-*
 * tokens (blue hexes are only fallbacks when a token is undefined).
 * ============================================================ */

.page-drop-overlay {
  position: fixed;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: clamp(1.5rem, 4vw, 3rem);
  background:
    radial-gradient(120% 80% at 50% 0%, var(--primary-100, #dbeafe) 0%, transparent 70%),
    linear-gradient(180deg, color-mix(in srgb, var(--primary-50, #eff6ff) 92%, transparent) 0%, color-mix(in srgb, var(--primary-100, #dbeafe) 80%, transparent) 100%);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: auto;
}

.page-drop-overlay--active {
  opacity: 1;
}

.page-drop-overlay__card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.65rem;
  transform: scale(0.97);
  transition: transform 200ms var(--ease-spring);
}

.page-drop-overlay--active .page-drop-overlay__card {
  transform: scale(1);
}

.page-drop-overlay__icon {
  display: grid;
  place-items: center;
  width: 5rem;
  height: 5rem;
  margin-bottom: 0.5rem;
  border-radius: 999px;
  color: var(--primary-700, #1d4ed8);
  background: color-mix(in srgb, var(--primary-200, #bfdbfe) 60%, white);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary-300, #93c5fd) 50%, transparent);
  animation: page-drop-overlay-bob 1.6s ease-in-out infinite;
}

.page-drop-overlay__title {
  font-size: clamp(1.35rem, 2.5vw, 1.85rem);
  font-weight: 800;
  letter-spacing: -0.015em;
  line-height: 1.15;
  color: var(--color-text-primary, #0f172a);
  margin: 0;
}

.page-drop-overlay__subtitle {
  font-size: 0.95rem;
  color: var(--color-text-subtle, #64748b);
  margin: 0;
}

.page-drop-overlay__hint {
  margin-top: 0.9rem;
}

@media (prefers-reduced-motion: reduce) {
  .page-drop-overlay__icon { animation: none; }
}


/* ===== shoot_empty_card.css ===== */
/* ============================================================
 * Photo-shooting overview — empty-gallery card.
 * The smaller sibling of `.images-empty`: same design language
 * (dashed border, breathing glow, polaroid, primary CTA) but
 * scaled down to fit a grid tile next to cover-image cards.
 *
 * Shares its dashed-glow surface, polaroid tile, eyebrow chip and
 * pulsing dot with `images_empty.css` via the additive `.ep-*`
 * primitives in `empty_polaroid_base.css`; only the per-component
 * deltas (sizes, timings, gradients, stretched-link and `--no-cover`
 * variant) live here.
 * ============================================================ */

/* Surface deltas — shared frame + transition come from `.ep-surface`. */
.shoot-empty-card {
  padding: clamp(1.25rem, 4vw, 1.75rem);
  border-radius: 1rem;
  background:
    radial-gradient(110% 65% at 50% 0%, var(--primary-50, #eff6ff) 0%, transparent 60%),
    linear-gradient(180deg, var(--color-bg-main, #ffffff) 0%, var(--color-bg-shade, #f8fafc) 100%);
}

/* Breathing glow — shape + timing here, boilerplate + `ep-breathe`
   animation come from `.ep-surface::before`. */
.shoot-empty-card::before {
  --ep-breathe-duration: 7s;
  --ep-breathe-min: 0.4;
  --ep-breathe-max: 0.8;
  --ep-breathe-scale: 1.03;
  background:
    radial-gradient(55% 45% at 50% 25%, color-mix(in srgb, var(--primary-200, #bfdbfe) 55%, transparent) 0%, transparent 70%);
  opacity: 0.5;
}

.shoot-empty-card:hover {
  border-color: var(--primary-500, #3b82f6);
  background:
    radial-gradient(110% 65% at 50% 0%, var(--primary-100, #dbeafe) 0%, transparent 65%),
    linear-gradient(180deg, var(--color-bg-main, #ffffff) 0%, var(--primary-50, #eff6ff) 100%);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-500, #3b82f6) 18%, transparent);
  cursor: pointer;
}

/* ----- Polaroid stage ----- */

.shoot-empty-card__stage {
  position: relative;
  width: 100%;
  height: 6.5rem;
  margin-bottom: 0.85rem;
  display: grid;
  place-items: center;
}

@media (width >= 48rem) {
  .shoot-empty-card__stage {
    height: 7.5rem;
  }
}

/* Polaroid deltas — shared tile surface comes from `.ep-polaroid`. The
   float rotation is driven through `ep-float` via `--ep-float-rot*`. */
.shoot-empty-card__polaroid {
  position: relative;
  width: 4.5rem;
  height: 5.5rem;
  border-radius: 0.35rem;
  padding: 0.35rem 0.35rem 1.1rem;
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.04),
    0 10px 22px -10px rgba(15, 23, 42, 0.22),
    0 3px 6px -3px rgba(15, 23, 42, 0.1);
  transform: rotate(-4deg);
  --ep-float-rot: -4deg;
  --ep-float-rot-mid: -3deg;
  animation: ep-float 8s ease-in-out infinite;
}

.shoot-empty-card__polaroid::after {
  bottom: 0.35rem;
  width: 1.4rem;
  height: 0.14rem;
}

.shoot-empty-card__polaroid-inner {
  border-radius: 0.18rem;
}

.shoot-empty-card__hint {
  position: absolute;
  top: 0.1rem;
  left: 50%;
  transform: translateX(-50%);
  pointer-events: none;
}

.shoot-empty-card__hint-arrow {
  width: 0.95rem;
  height: 0.95rem;
  color: color-mix(in srgb, var(--primary-600, #2563eb) 80%, transparent);
  --ep-arrow-min: 0.5;
  --ep-arrow-to: 0.3rem;
  animation: ep-arrow-bob 2.2s ease-in-out infinite;
}

/* ----- Copy ----- */

/* Eyebrow deltas — shared chip styling comes from `.ep-eyebrow`. */
.shoot-empty-card__eyebrow {
  gap: 0.4rem;
  font-size: 0.625rem;
  padding: 0.22rem 0.55rem;
  margin-bottom: 0.7rem;
}

/* Dot deltas — shared styling + `ep-dot-pulse` come from `.ep-eyebrow-dot`. */
.shoot-empty-card__eyebrow-dot {
  width: 0.38rem;
  height: 0.38rem;
  --ep-pulse-ring: 5px;
}

.shoot-empty-card__title {
  font-size: clamp(1.05rem, 1.6vw, 1.25rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.45;
  color: var(--color-text, #0f172a);
  text-decoration: none;
  margin: 0 0 0.15rem;
  padding: 0.06em 0.04em 0.22em;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Stretched-link: the title anchor stretches across the whole card so any
 * click on the card opens the shooting. The actions container sits above
 * the overlay so the inline button-to form keeps its own click target. */
.shoot-empty-card__title::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}

.shoot-empty-card:hover .shoot-empty-card__title {
  text-decoration: underline;
}

.shoot-empty-card__description {
  font-size: 0.85rem;
  color: var(--color-text-subtle, #64748b);
  margin: 0;
  line-height: 1.45;
  max-width: 18rem;
}

.shoot-empty-card__meta {
  font-size: 0.72rem;
  color: var(--color-text-subtle, #94a3b8);
  margin: 0.2rem 0 0;
  letter-spacing: 0.01em;
}

.shoot-empty-card__actions {
  margin-top: 0.95rem;
  position: relative;
  z-index: 2;
}

.shoot-empty-card__primary {
  box-shadow:
    0 6px 16px -6px color-mix(in srgb, var(--primary-600, #2563eb) 60%, transparent),
    0 1px 0 0 color-mix(in srgb, var(--primary-700, #1d4ed8) 70%, transparent) inset;
  transition: transform 120ms ease, box-shadow 200ms ease, background 200ms ease;
}

.shoot-empty-card__primary:hover {
  transform: translateY(-1px);
  box-shadow:
    0 10px 22px -8px color-mix(in srgb, var(--primary-600, #2563eb) 60%, transparent),
    0 1px 0 0 color-mix(in srgb, var(--primary-700, #1d4ed8) 70%, transparent) inset;
}

.shoot-empty-card__primary:active {
  transform: translateY(0);
}

/* ----- No-cover variant ----- */
/* Same design language as the empty-gallery card, but recognisably its own
 * state: a calmer dashed frame, the polaroid tilted the other way, and a
 * dashed void inside the polaroid where the missing cover photo would sit. */

.shoot-empty-card--no-cover {
  border-color: color-mix(in srgb, var(--primary-400, #60a5fa) 35%, var(--color-border, #e5e7eb));
}

.shoot-empty-card--no-cover .shoot-empty-card__polaroid {
  transform: rotate(4deg);
  --ep-float-rot: 4deg;
  --ep-float-rot-mid: 3deg;
}

.shoot-empty-card--no-cover .shoot-empty-card__polaroid-inner {
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--color-bg-shade, #f1f5f9) 80%, transparent) 0%,
      color-mix(in srgb, var(--primary-100, #dbeafe) 40%, transparent) 100%);
  outline: 1.5px dashed color-mix(in srgb, var(--color-text-subtle, #6b7280) 45%, transparent);
  outline-offset: -4px;
}

/* ----- Mobile: gerahmte Platzhalter-Karte -----
 * Auf kleinen Screens eine ruhige, aber bewusst *gerahmte* Kachel: ein
 * gestrichelter Rahmen grenzt den Platzhalter klar von den randlosen
 * Foto-Karten ab, der Shooting-Name führt als Hero. Schwere Dekoration
 * (atmender Glow, Animationen) bleibt aus, der redundante „keine Galerie"-
 * Satz entfällt — Rahmen + Status-Pille tragen den Zustand. */
@media (width < 40rem) {
  .shoot-empty-card,
  .shoot-empty-card:hover {
    border: 1.5px solid color-mix(in srgb, var(--primary-400, #60a5fa) 45%, var(--color-border, #e5e7eb));
    box-shadow: none;
    background:
      linear-gradient(180deg,
        var(--color-bg-main, #ffffff) 0%,
        color-mix(in srgb, var(--primary-50, #eff6ff) 55%, var(--color-bg-shade, #f8fafc)) 100%);
    padding: 1.5rem 1.25rem;
    cursor: pointer;
  }

  .shoot-empty-card::before {
    display: none;
  }

  .shoot-empty-card__stage {
    height: 4.75rem;
    margin-bottom: 0.5rem;
  }

  /* Kompaktes Chip statt großem Polaroid, damit die Illustration vollständig
   * in die Bühne passt und nicht in die Status-Pille hineinragt. */
  .shoot-empty-card__polaroid {
    width: 3.5rem;
    height: 4.2rem;
    padding: 0.3rem 0.3rem 0.85rem;
  }

  .shoot-empty-card__polaroid,
  .shoot-empty-card__hint-arrow,
  .shoot-empty-card__eyebrow-dot {
    animation: none;
  }

  .shoot-empty-card__hint {
    display: none;
  }

  /* Status-Pille „Bereit für die Galerie" entfällt auf Mobile — der
   * Shooting-Name übernimmt ihren Platz als oberstes Textelement. Nur für die
   * Empty-Gallery-Variante; die No-Cover-Karte behält ihre „Titelbild fehlt"-
   * Pille. */
  .shoot-empty-card:not(.shoot-empty-card--no-cover) .shoot-empty-card__eyebrow {
    display: none;
  }

  /* Shooting-Name wird zum Hero: größer, fett, und darf umbrechen (statt
   * einzeilig mit Ellipsis), damit auch lange Namen vollständig lesbar sind. */
  .shoot-empty-card__title {
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--color-text, #0f172a);
    white-space: normal;
    overflow: visible;
    padding: 0.12em 0 0.28em;
    margin: 0 0 0.4rem;
  }

  /* „Noch keine Galerie vorhanden" entfällt auf Mobile — nur für die
   * Empty-Gallery-Variante; die No-Cover-Karte behält ihre Beschreibung. */
  .shoot-empty-card:not(.shoot-empty-card--no-cover) .shoot-empty-card__description {
    display: none;
  }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .shoot-empty-card::before,
  .shoot-empty-card__polaroid,
  .shoot-empty-card__hint-arrow,
  .shoot-empty-card__eyebrow-dot {
    animation: none !important;
  }
}

/* Dark mode */
[data-color-scheme="dark"] .shoot-empty-card {
  background:
    radial-gradient(110% 65% at 50% 0%, color-mix(in srgb, var(--primary-900, #1e3a8a) 35%, transparent) 0%, transparent 60%),
    linear-gradient(180deg, color-mix(in srgb, var(--color-bg-main, #0b0f17) 100%, transparent) 0%, color-mix(in srgb, var(--color-bg-shade, #0f172a) 100%, transparent) 100%);
  border-color: color-mix(in srgb, var(--primary-500, #3b82f6) 55%, var(--color-border, #1f2937));
}

[data-color-scheme="dark"] .shoot-empty-card__polaroid {
  background: #0f172a;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.04),
    0 12px 26px -10px rgba(0, 0, 0, 0.55),
    0 3px 6px -3px rgba(0, 0, 0, 0.4);
}

[data-color-scheme="dark"] .shoot-empty-card--no-cover {
  border-color: color-mix(in srgb, var(--primary-500, #3b82f6) 35%, var(--color-border, #1f2937));
}

[data-color-scheme="dark"] .shoot-empty-card--no-cover .shoot-empty-card__polaroid-inner {
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--color-bg-shade, #0f172a) 85%, transparent) 0%,
      color-mix(in srgb, var(--primary-900, #1e3a8a) 45%, transparent) 100%);
  outline-color: color-mix(in srgb, var(--color-text-subtle, #94a3b8) 40%, transparent);
}


/* ===== selection_overlay.css ===== */
/* ==============================
   Selection overlay — shared checkbox + checkmark rules

   Canonical multi-select overlay shared by the image grid
   (.image-display__select), gallery cards (.gallery-card__select) and picker
   tiles (.check-badge). Only the declarations that are identical across all
   three surfaces live here; surface-specific bits (z-index, cursor, padding,
   the selection ring/focus-veil, touch + reduced-motion rules) stay in
   image_display.css, gallery_card.css and picker_tiles.css.

   App CSS is unlayered, so the cascade is import order: this file MUST be
   imported BEFORE the three surface files (see application.css) so their
   per-surface overrides win.
   ============================== */

/* Checkbox circle — resting state. */
:is(.image-display__select, .gallery-card__select, .check-badge) {
  position: absolute;
  top: 8px;
  left: 8px;
  width: 24px;
  height: 24px;
  background-color: rgba(255, 255, 255, 0.9);
  border: 2px solid #888;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.2s, transform 0.15s, border-color 0.15s, background-color 0.15s;
}

/* Reveal the empty circle on hover of the tile/card and while selected. */
:is(
  .image-display:hover .image-display__select,
  .gallery-item--selected .image-display__select,
  .gallery-card--selectable:hover .gallery-card__select,
  .gallery-card--selected .gallery-card__select,
  .tile:hover .check-badge,
  .tile.is-selected .check-badge
) {
  opacity: 1;
}

/* Keyboard access: the image-grid badge is a real button (role="checkbox") —
   reveal it while focused and draw a visible focus ring, otherwise a keyboard
   user tabs onto an invisible control. */
.image-display__select:focus-visible {
  opacity: 1;
  outline: 2px solid var(--color-primary, #4f46e5);
  outline-offset: 2px;
}

/* Circle hover accent on the two pointer-driven component surfaces — picker
   tiles have no circle hover state (the whole tile is the button). */
:is(.image-display__select, .gallery-card__select):hover {
  border-color: var(--color-primary, #4f46e5);
}

/* Bewegungs-Hover nur auf echten Zeigergeräten — auf Touch löst ein Tap sonst
   einen Fake-Hover-Zoom aus (sticky hover). */
@media (hover: hover) and (pointer: fine) {
  :is(.image-display__select, .gallery-card__select):hover {
    transform: scale(1.15);
  }
}

/* Selected circle — brand fill, white ring + shadow, short bump-pop spring. */
:is(
  .gallery-item--selected .image-display__select,
  .gallery-card--selected .gallery-card__select,
  .tile.is-selected .check-badge
) {
  background-color: var(--color-primary, #4f46e5);
  border-color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  animation: bump-pop 0.3s var(--ease-spring);
}

/* Checkmark SVG — resting (hidden, primed to draw via stroke-dashoffset). */
:is(.image-display__select-check, .gallery-card__select-check, .check-badge__check) {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: #fff;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 21;
  stroke-dashoffset: 21;
  opacity: 0;
}

/* The two component surfaces center the SVG by absolute-positioning it inside
   the circle; the picker centers it with the circle's own grid instead. */
:is(.image-display__select-check, .gallery-card__select-check) {
  position: absolute;
  inset: 0;
  margin: auto;
}

/* Selected checkmark — draw the stroke in. */
:is(
  .gallery-item--selected .image-display__select-check,
  .gallery-card--selected .gallery-card__select-check,
  .tile.is-selected .check-badge__check
) {
  opacity: 1;
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.3s var(--ease-out-soft) 0.05s;
}

/* Selected image shrinks inward so a ring of "air" appears between the
   selection frame and the photo. transform is layout-neutral — no reflow. */
:is(
  .gallery-item--selected .image-display__img,
  .gallery-card--selected .card-lift img,
  .tile.is-selected .tile-img img
) {
  transform: scale(0.9);
  border-radius: var(--radius-1, 0.375rem);
}


/* ===== gallery_card.css ===== */
/* ==============================
   Gallery Card — Selection state
   ============================== */

.gallery-card {
  isolation: isolate;
}

/* Checkbox circle — surface-specific overrides only. The shared circle/check/
   hover/selected rules live in selection_overlay.css (imported before this
   file); here only the card-specific button reset (padding) and the raised
   z-index that lifts the checkbox above the card cover. */
.gallery-card__select {
  cursor: pointer;
  z-index: 4;
  padding: 0;
}

/* Selection frame — mirrors the image selection (image_display.css:308-331).
   The ring and the shrink transform live on the SAME clipped element (the
   .card-lift cover wrapper, which carries overflow-hidden + rounded-xl from
   Ui::CardComponent), so the hover-lift (translateY on .card-lift) moves the
   frame and image together — no overflow of the cover past the border.
   Scoped to .card-lift so the table view (<tr>.gallery-card--selected in
   galleries_overview_table.css) is untouched. */
.gallery-card--selected .card-lift::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
  box-shadow: inset 0 0 0 4px var(--color-primary);
  pointer-events: none;
  z-index: 2;
}

/* The selected-image shrink (scale(0.9) on .card-lift img) is shared in
   selection_overlay.css. */

/* Always show checkbox on touch — there is no hover state. */
@media (hover: none) {
  .gallery-card--selectable .gallery-card__select {
    opacity: 1;
  }
}

/* ==============================
   Floating Action Bar — additions
   ============================== */

/* The gem positions the desktop bar at top: 20px — mitten IM Topbar-Inhalt
   (Suchfeld/Breadcrumb, 65px hoch), sodass sich die Texte überlagern. Unter
   dem Header andocken; mobil (max-width: 639px) greift weiterhin das
   Bottom-Sheet-Layout des Gems (top: unset), das hier unberührt bleibt. */
@media (min-width: 640px) {
  .floating-action-bar {
    top: calc(65px + 12px);
  }
}

/* SVG icons baseline-align by default inside inline-flex; force block-level
   so flex's align-items: center actually centers them with the label. */
.floating-action-bar__action svg {
  display: block;
}

/* Subtle green variant for affirmative actions (e.g. "release for voting"). */
.floating-action-bar__action--success {
  color: var(--color-positive);
}

.floating-action-bar__action--success:hover {
  background-color: var(--color-success-bg);
}

/* Subtle red variant for destructive actions — icon + label both red. Mirrors
   the gem's floating_action_bar.css --danger rule (kept in sync, no drift). */
.floating-action-bar__action--danger {
  color: var(--color-negative);
}

.floating-action-bar__action--danger:hover {
  background-color: var(--color-error-bg);
}


/* ===== galleries_overview_table.css ===== */
/* ==========================================================================
   Galleries overview — table ("list") view on photo_shootings/show
   A calm, scannable alternative to the card grid. Rows reuse the
   gallery-card-select contract, so the `gallery-card--selected` class is
   toggled on each <tr> for multi-select.
   ========================================================================== */

.galleries-table-wrap {
  width: 100%;
  overflow-x: auto;
}

.galleries-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

/* ---- Header ---------------------------------------------------------- */

.galleries-table thead th {
  text-align: start;
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.01em;
  color: var(--color-text-subtle);
  padding: 0 1rem 0.625rem;
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.galleries-table__th--num,
.galleries-table__th--date {
  width: 1%;
}

.galleries-table__sort {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  border-radius: 0.375rem;
  transition: color 0.15s;
}

.galleries-table__sort:hover {
  color: var(--color-text);
}

.galleries-table__sort--active {
  color: var(--color-text);
}

/* Chevron sits in a small white badge so the column reads as a sort control. */
.galleries-table__sort-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
  background-color: var(--color-bg);
  border: 1px solid var(--color-border-light);
  border-radius: 0.375rem;
  color: var(--color-text-subtle);
  transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}

.galleries-table__sort:hover .galleries-table__sort-badge {
  border-color: var(--color-border);
  color: var(--color-text);
}

.galleries-table__sort--active .galleries-table__sort-badge {
  color: var(--color-primary-600);
  border-color: var(--color-primary-600);
}

.galleries-table__sort-icon {
  flex-shrink: 0;
}

/* ---- Rows ------------------------------------------------------------ */

.galleries-table__row {
  border-bottom: 1px solid var(--color-border-light);
  transition: background-color 0.15s;
  cursor: pointer;
}

.galleries-table__row:hover {
  background-color: var(--color-border-light);
}

.galleries-table__td {
  padding: 0.75rem 1rem;
  vertical-align: middle;
  color: var(--color-text-subtle);
}

.galleries-table__td--num,
.galleries-table__td--date {
  white-space: nowrap;
}

.galleries-table__td--date,
.galleries-table__count {
  font-variant-numeric: tabular-nums;
}

/* ---- Name cell ------------------------------------------------------- */

.galleries-table__name {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}

/* White badge framing the cover thumbnail. overflow:hidden clips the cover to
   the rounded frame so a portrait cover (taller than the square badge) can't
   spill past the bottom edge into the row. */
.galleries-table__thumb-badge {
  flex-shrink: 0;
  width: 80px;
  height: 80px;
  padding: 5px;
  background-color: var(--color-bg);
  border: 1px solid var(--color-border-light);
  border-radius: 0.875rem;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.06);
  overflow: hidden;
}

/* Fills the badge's content box exactly and crops to a square via object-fit.
   display:block (not a grid item) so its height resolves against the definite
   badge height instead of an auto grid row that grows to the image's aspect. */
.galleries-table__thumb {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 0.5rem;
  object-fit: cover;
  background-color: var(--color-border-light);
}

.galleries-table__thumb--empty {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
}

.galleries-table__title {
  min-width: 0;
  font-weight: 600;
  font-size: 1rem;
  color: var(--color-text);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-underline-offset: 2px;
}

.galleries-table__title:hover {
  text-decoration: underline;
}

/* White pill chip used for the Bilder + Status columns (filter-pill style). */
.galleries-table__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  border-radius: 9999px;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.galleries-table__chip--muted {
  color: var(--color-text-subtle);
}

.galleries-table__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 9999px;
  flex-shrink: 0;
}

.galleries-table__dot--green {
  background-color: var(--color-positive, #16a34a);
}

.galleries-table__dot--muted {
  background-color: var(--color-text-subtle);
}

.galleries-table__date {
  color: var(--color-text-subtle);
}

/* ---- Select checkbox (mirrors .gallery-card__select) ----------------- */

.galleries-table__select {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  margin-inline-end: 0.125rem;
  background-color: var(--color-bg);
  border: 2px solid var(--color-border-dark);
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  position: relative;
  opacity: 0;
  transition: opacity 0.15s, transform 0.15s, border-color 0.15s, background-color 0.15s;
}

.galleries-table__row:hover .galleries-table__select,
.galleries-table__row.gallery-card--selected .galleries-table__select {
  opacity: 1;
}

.galleries-table__select:hover {
  border-color: var(--color-primary-600);
}

/* Bewegungs-Hover nur auf echten Zeigergeräten — verhindert Fake-Hover-Zoom
   beim Tap auf Touch-Geräten. */
@media (hover: hover) and (pointer: fine) {
  .galleries-table__select:hover {
    transform: scale(1.12);
  }
}

.galleries-table__row.gallery-card--selected .galleries-table__select {
  background-color: var(--color-primary-600);
  border-color: var(--color-primary-600);
}

.galleries-table__row.gallery-card--selected .galleries-table__select::after {
  content: '✓';
  color: white;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 12px;
  font-weight: bold;
  line-height: 1;
}

/* ---- Selected row ---------------------------------------------------- */
/* The select controller adds `gallery-card--selected` to the <tr>, which would
   otherwise inherit gallery_card.css's full rounded outline (meant for cards).
   Neutralise that and use a clean filled row + left accent bar instead. */

.galleries-table__row.gallery-card--selected {
  outline: none;
  border-radius: 0;
  background-color: var(--color-primary-50);
  box-shadow: inset 3px 0 0 0 var(--color-primary-600);
}

/* View toggle: force the active segment onto pure white (--color-bg) instead
   of the theme's off-white --color-main-bg, so both segments look consistent. */
.galleries-view-toggle .segmented-control__segment--active {
  background-color: var(--color-bg);
}

/* Touch devices have no hover — keep the checkbox reachable. */
@media (hover: none) {
  .galleries-table__select {
    opacity: 1;
  }
}


/* ===== photo_shootings_overview_table.css ===== */
/* Photo-shootings overview table (redesign "Shootings Tabelle 2a").
   Builds on the shared .galleries-table* styles; these scoped overrides only
   apply to the shootings index table (loaded after galleries_overview_table.css
   so the equal-specificity overrides win). */

/* Compact rows: the name block is a short two-line stack next to a landscape
   thumbnail, so vertically center it. */
.shootings-table__name {
  align-items: center;
}

/* Title + meta line stacked vertically next to the thumbnail. */
.shootings-table__text {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

/* Landscape 60×44 cover thumbnail: plain rounded image, no white badge. */
.shootings-table__thumb {
  width: 60px;
  height: 44px;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: var(--rounded-md);
}

/* Placeholder when a shooting has no usable cover image yet. */
.shootings-table__thumb--empty {
  display: grid;
  place-items: center;
  background: var(--color-shade);
}

/* "Shared with" column: shrink to its avatar stack, keep it on one line. */
.galleries-table__th--shared {
  width: 1%;
  white-space: nowrap;
}

.galleries-table__td--shared {
  white-space: nowrap;
}

/* Checkbox + actions columns shrink to their controls. */
.galleries-table__th--select,
.galleries-table__th--actions {
  width: 1%;
  white-space: nowrap;
}

.galleries-table__td--select,
.galleries-table__td--actions {
  white-space: nowrap;
}

/* Selection checkbox sizing. */
.galleries-table__checkbox {
  width: 1rem;
  height: 1rem;
  cursor: pointer;
  accent-color: var(--color-primary-600, currentColor);
}

/* Date values render as bare tabular numbers (no leading calendar icon). */
.shootings-table__date-value {
  font-variant-numeric: tabular-nums;
}

/* Subtle "N galleries · N photos" meta line under the title. */
.shootings-table__meta {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.125rem;
  font-size: 0.8125rem;
  color: var(--color-text-subtle);
  white-space: nowrap;
}

.shootings-table__meta-sep {
  color: var(--color-text-subtle);
  opacity: 0.6;
}


/* ===== gallery_grid.css ===== */
/* GalleryGridComponent — standalone PhotoSwipe photo grid with per-tile overlay
 * slots. The responsive column layout comes from Tailwind grid utilities on the
 * container (.gg-grid also carries `grid`) or, for the display-layout variants
 * (masonry / grid / rows / list), from the .gg-grid--<layout> rules below.
 * These rules own the tile box, the aspect-ratio reservation, the thumbnail
 * fit and the overlay positioning.
 *
 * Two custom-property knobs drive every layout variant (set by the gg-size /
 * gg-gap controllers or a host wrapper):
 *   --gg-size  minimum column width (masonry/grid) or row-height basis (rows)
 *   --gg-gap   gutter between tiles
 * The --pg-* fallbacks keep the guest preview's fluid grid working unchanged.
 *
 * No nested @imports here — Lightning CSS (the Tailwind v4 / docs build) silently
 * drops them (see css_nested_import_test.rb). */

.gg-grid {
  width: 100%;
}

/* Fluid column model (layout: :fluid): the container drops the Tailwind
 * grid-cols-* utilities and sizes itself from custom properties a wrapper sets,
 * so a host keeps a photographer-configurable tile size. Falls back to sensible
 * defaults when the properties are absent. */
.gg-grid--fluid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--gg-size, var(--pg-grid-size, 200px)), 1fr));
  gap: var(--gg-gap, var(--pg-grid-gap, 0.5rem));
}

.gg-tile {
  position: relative;
  margin: 0;
}

/* The visual box (aspect-ratio reservation, crop, neutral backdrop) sits on the
 * tile itself in the default markup, and moves to the inner .gg-tile__media
 * wrapper when the tile renders a caption below the image — the caption must
 * grow the cell without breaking the image's ratio. :where() keeps the
 * specificity at the plain-class level so host overrides keep winning. */
.gg-tile:where(:not(.gg-tile--with-caption)) {
  overflow: hidden;
  background: var(--color-secondary, #f4f4f5);
  /* Reserve the final box before the thumbnail loads so the grid never jumps.
   * Falls back to a square when the item carried no intrinsic dimensions. */
  aspect-ratio: var(--gg-ar, 1 / 1);
}

.gg-tile--with-caption {
  display: flex;
  flex-direction: column;
}

.gg-tile__media {
  position: relative;
  overflow: hidden;
  background: var(--color-secondary, #f4f4f5);
  aspect-ratio: var(--gg-ar, 1 / 1);
}

.gg-tile--rounded {
  border-radius: 0.5rem;
}

.gg-tile--rounded :where(.gg-tile__media) {
  border-radius: 0.5rem;
}

.gg-tile__link {
  display: block;
  width: 100%;
  height: 100%;
  cursor: pointer;
}

.gg-tile__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 200ms ease;
}

.gg-tile__link:hover .gg-tile__img {
  transform: scale(1.05);
}

/* The overlay layer sits above the thumbnail and, by default, lets pointer
 * events fall through to the trigger link so a tap still opens the lightbox;
 * interactive controls inside the slot re-enable pointer events themselves. */
.gg-tile__overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.gg-tile__overlay > * {
  pointer-events: auto;
}

/* Badge slot — pinned to the tile's top-right corner, above image and overlay. */
.gg-tile__badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 2;
}

/* Caption slot below the image (list layout stretches it, see --list). */
.gg-tile__caption {
  padding: 0.375rem 0.125rem 0;
  font-size: 0.8125rem;
  color: var(--color-text-subtle, #71717a);
}

/* Loading state — a soft pulse fills the reserved box until the host swaps the
 * tile for its completed markup. */
.gg-tile__skeleton {
  position: absolute;
  inset: 0;
  background: var(--color-secondary, #f4f4f5);
  animation: gg-tile-pulse 1.6s ease-in-out infinite;
}

@keyframes gg-tile-pulse {
  50% {
    opacity: 0.55;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gg-tile__skeleton {
    animation: none;
  }
}

/* Error state — centers the host's error slot (message, retry button) inside
 * the reserved box. */
.gg-tile__error {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem;
  text-align: center;
}

/* ==============================
   Display-layout variants
   ------------------------------
   A gg-grid--<layout> modifier on the container selects one of four display
   modes; the gg-layout controller switches between them. All modes reuse the
   same tile markup; only the container layout (and a few per-tile overrides)
   change. The single --gg-size knob means a different thing per layout:
   column width (masonry/grid) or row height (rows). Image bytes always come
   from the host's fixed srcset variants — switching layout or dragging the
   slider never requests a new image size.
   ============================== */

/* Masonry (CSS multicol fallback) — natural aspect ratios, no crop. The
 * gg-masonry controller upgrades this to the evenly-filled grid variant below;
 * without JS the multicol keeps working. */
.gg-grid--masonry {
  display: block;
  column-width: var(--gg-size, 250px);
  column-gap: var(--gg-gap, 16px);
}

.gg-grid--masonry > .gg-tile {
  width: 100%;
  margin-bottom: var(--gg-gap, 16px);
  break-inside: avoid;
}

/* Masonry (JS) — true, evenly-filled masonry via the grid-row-span technique.
 * The gg-masonry controller adds this class ONLY after the first span pass (no
 * FOUC with empty 8px rows) and computes `grid-row-end: span N` per tile. The
 * tiles keep their DOM order, so the gallery reads left→right. --gg-size is a
 * MINIMUM tile width — auto-fill picks the column count and 1fr stretches the
 * tracks flush, so the gutters always equal --gg-gap and the edges always hug
 * the container, mid-drag included. The min(…, 100%) guard keeps a single
 * column from overflowing a container narrower than the slider value. */
.gg-grid--masonry-js {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--gg-size, 250px), 100%), 1fr));
  gap: var(--gg-gap, 16px);
  grid-auto-rows: 8px;
  align-items: start;
  column-width: auto;
}

.gg-grid--masonry-js > .gg-tile {
  width: 100%;
  margin-bottom: 0;
  break-inside: auto;
}

/* Grid — uniform crop raster (deliberate center-crop, 4:3 by default via
 * --gg-crop-ar). Same browser-native fit model as --masonry-js. The !important
 * is required: the crop must overrule the tile's inline aspect-ratio /
 * per-item --gg-ar reservation. */
.gg-grid--grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(var(--gg-size, 250px), 100%), 1fr));
  gap: var(--gg-gap, 16px);
  align-items: start;
}

.gg-grid--grid > .gg-tile:where(:not(.gg-tile--with-caption)),
.gg-grid--grid .gg-tile__media {
  aspect-ratio: var(--gg-crop-ar, 4 / 3) !important;
}

/* Rows — justified rows: equal row height, width follows aspect ratio. Each
 * tile's flex-basis is its natural width at the row height (height ×
 * --gg-ar-num); flex-grow ∝ --gg-ar-num then stretches full rows to the
 * container width while tiles keep their natural width proportions. The
 * ::after spacer absorbs the leftover space of the last row so its tiles stay
 * at natural size instead of being blown up to full width. */
.gg-grid--rows {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gg-gap, 16px);
  align-items: stretch;
}

.gg-grid--rows > .gg-tile {
  height: calc(var(--gg-size, 250px) * 0.72);
  flex: var(--gg-ar-num, 1.5) 1 calc(var(--gg-size, 250px) * 0.72 * var(--gg-ar-num, 1.5));
  max-width: calc(var(--gg-size, 250px) * 0.72 * var(--gg-ar-num, 1.5) * 1.6);
  margin: 0;
  aspect-ratio: auto;
}

.gg-grid--rows::after {
  content: "";
  flex: 999 1 0;
  height: 0;
}

.gg-grid--rows .gg-tile__media {
  height: 100%;
  aspect-ratio: auto !important;
}

/* List — preview thumbnail + always-visible caption row. Rows span the full
 * container width. */
.gg-grid--list {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}

.gg-grid--list > .gg-tile {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 16px;
  border: 1px solid var(--color-border, #e4e4e7);
  padding: 10px 14px;
  aspect-ratio: auto;
  overflow: visible;
  background: transparent;
}

.gg-grid--list .gg-tile__media {
  width: 128px;
  height: 128px;
  flex: 0 0 auto;
  aspect-ratio: 1 / 1 !important;
}

.gg-grid--list .gg-tile__caption {
  display: flex !important;
  flex: 1 1 auto;
  gap: 4px;
  padding: 0;
}

/* Slider — a horizontal scroll-snap strip. Tile height is --gg-size, tile
 * width follows each tile's natural aspect ratio (--gg-ar-num), so a strip
 * mixes portrait and landscape photos at one height like a film roll. The
 * scrollbar is hidden (swipe/trackpad territory); tiles keep the full
 * PhotoSwipe wiring, so a tap still opens the lightbox. */
.gg-grid--slider {
  display: flex;
  gap: var(--gg-gap, 16px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.gg-grid--slider::-webkit-scrollbar {
  display: none;
}

.gg-grid--slider > .gg-tile {
  height: var(--gg-size, 250px);
  width: calc(var(--gg-size, 250px) * var(--gg-ar-num, 1.5));
  flex: 0 0 auto;
  scroll-snap-align: start;
  aspect-ratio: auto;
  margin: 0;
}

.gg-grid--slider .gg-tile__media {
  height: 100%;
  aspect-ratio: auto !important;
}

/* Square — zero-radius tiles (edge-to-edge look); a host pairs this with its
 * own gap/border tweaks. */
.gg-grid--square > .gg-tile,
.gg-grid--square .gg-tile__media {
  border-radius: 0;
}

/* Single-column mobile modifier (server-set from a ?columns param, see
 * GalleryColumnsToggleComponent) — full-width stacked tiles. The concrete
 * template lives in the mobile collapse block below. */

/* Layout switch (FLIP): while gg-layout / gg-size / gg-columns-flip morph the
 * tiles from their old to their new box, the grid carries this class. It
 * suppresses a host's entrance stagger and competing tile transitions so they
 * neither distort the First/Last measurements nor fight the FLIP transform.
 * overflow: clip keeps the object-fit image inside the tile frame during the
 * brief scale distortion.
 *
 * animation-play-state: paused instead of animation: none — swapping
 * animation-name (none → a name) RESTARTS the animation per CSS spec when the
 * class is removed, so every tile would fade in/out again after the FLIP
 * (reads like an image reload). Pausing leaves animation-name untouched:
 * removing the class creates no new animation, finished entrances stay
 * finished. Still-running entrances are finished by the controllers before the
 * First measurement (finishEntrance in lib/flip_core). */
.gg-grid--switching > .gg-tile {
  animation-play-state: paused !important;
  overflow: clip;
  transition: none;
}

.gg-grid--switching .gg-tile__media,
.gg-grid--switching .gg-tile__img {
  transition: none;
}

/* Mobile collapse — the layout toolbar is desktop-only, so below 48rem the
 * masonry/grid/rows variants fold back to a simple auto-fill grid; list stays
 * (it reads fine stacked). */
@media (width < 48rem) {
  .gg-grid--masonry,
  .gg-grid--masonry-js,
  .gg-grid--grid,
  .gg-grid--rows {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    grid-auto-rows: auto;
    column-width: auto;
    gap: 8px;
  }

  .gg-grid--masonry > .gg-tile,
  .gg-grid--masonry-js > .gg-tile,
  .gg-grid--rows > .gg-tile {
    width: auto;
    height: auto;
    margin-bottom: 0;
    flex: initial;
  }

  .gg-grid--grid > .gg-tile:where(:not(.gg-tile--with-caption)),
  .gg-grid--rows > .gg-tile:where(:not(.gg-tile--with-caption)),
  .gg-grid--grid .gg-tile__media,
  .gg-grid--rows .gg-tile__media {
    aspect-ratio: 1 / 1 !important;
  }

  /* Single-column mobile view. Placed AFTER the collapse rules above (same
   * specificity) so it wins and renders every image full-width, stacked. */
  .gg-grid--mobile-1col,
  .gg-grid--mobile-1col.gg-grid--masonry,
  .gg-grid--mobile-1col.gg-grid--masonry-js,
  .gg-grid--mobile-1col.gg-grid--grid,
  .gg-grid--mobile-1col.gg-grid--rows {
    grid-template-columns: 1fr;
  }
}


/* ===== gallery_toolbar.css ===== */
/* GalleryToolbarComponent — the white "view" pill bundling the grid display
 * controls (size slider · gap slider · layout switcher). The layout-switcher
 * segments collapse to icon-only while inactive; the active segment keeps its
 * label and reads in the theme's primary color on a tinted track.
 *
 * No nested @imports here — Lightning CSS (the Tailwind v4 / docs build)
 * silently drops them (see css_nested_import_test.rb). */

.gg-toolbar {
  height: 48px;
  padding: 0 16px;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  background: var(--color-bg, #fff);
}

.gg-toolbar__divider {
  width: 1px;
  height: 24px;
  flex-shrink: 0;
  background: var(--color-border);
}

/* In the layout switcher, inactive segments collapse to icon-only; the active
 * segment keeps its label. Keyed on aria-pressed, which the segmented-control
 * and gg-layout controllers both maintain. */
.gg-toolbar__layout-seg .segmented-control__segment[aria-pressed="false"] span {
  display: none;
}

/* Tint the track so the active segment stands out (the default bg-shade is
 * too close to white). Theme-aware accent wash over the bg. */
.gg-toolbar__layout-seg.segmented-control {
  background: color-mix(in srgb, var(--color-primary) 10%, var(--color-bg, #fff));
}

/* Active segment: a bg fill + accent ring + accent label/icon, so the
 * selection reads clearly on the tinted track (theme-aware). */
.gg-toolbar__layout-seg .segmented-control__segment[aria-pressed="true"] {
  background: var(--color-bg, #fff);
  color: var(--color-primary);
  box-shadow:
    0 0 0 1.5px var(--color-primary),
    0 1px 2px rgba(0, 0, 0, 0.08);
}

/* Normalize the slider so its track centers with the end icons (range inputs
 * ship a default vertical margin in some browsers). */
.gg-toolbar__size .slider {
  display: block;
  margin: 0;
}


/* ===== chat_thread.css ===== */
/* ============================================================
 * Chat thread family (.cl-*)
 * Conversation cards with inline chat threads — the "comments
 * overview" pattern: filter toolbar with chips, bucketed card
 * list, message groups with side-aligned bubbles, per-bubble
 * menus and a footer composer.
 *
 * Components: ChatConversationListComponent, ChatConversationCardComponent,
 * ChatMessageComponent, ChatBubbleComponent, ChatDaySeparatorComponent,
 * ChatComposerComponent, FilterToolbarComponent, FilterChipComponent.
 * Stimulus: chat_card_controller.js (per card), chat_list_controller.js
 * (filter/sort/collapse-all across cards).
 * ============================================================ */

/* ---------- Filter toolbar (sort + filter chips) ---------- */

.cl-list-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  /* Tight top padding tucks the toolbar right under the shared tab nav; the
   * larger bottom padding keeps it clear of the first conversation card. */
  padding: 0.25rem 0 0.75rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.cl-list-toolbar__group {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

.cl-list-toolbar__label {
  font-size: 0.85rem;
  color: #52525b;
  margin-right: 0.25rem;
}

.cl-list-toolbar__divider {
  width: 1px;
  height: 20px;
  background: rgba(0, 0, 0, 0.12);
  margin: 0 0.25rem;
}

.cl-list-toolbar__counter {
  margin-left: auto;
  font-size: 0.85rem;
  color: #71717a;
  white-space: nowrap;
}

/* ---------- Chip ---------- */

.cl-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  border: 1px solid var(--color-border-main, rgba(0, 0, 0, 0.12));
  background: var(--color-bg-white, #fff);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--color-text-primary, #18181b);
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}

.cl-chip:hover {
  background: var(--color-bg-shade, #f4f4f5);
  border-color: var(--primary-300, rgba(0, 0, 0, 0.22));
}

.cl-chip--active {
  background: var(--primary-100);
  border-color: var(--primary-300);
  color: var(--primary-700);
}

.cl-chip--unread.cl-chip--active {
  background: var(--red-100, #fee2e2);
  border-color: var(--red-300, #fca5a5);
  color: var(--red-700, #b91c1c);
}

.cl-chip__avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.62rem;
  font-weight: 700;
  flex-shrink: 0;
}

/* ---------- List + bucket headers ---------- */

.cl-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.cl-list__bucket-label {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: #3f3f46;
  padding: 1rem 0 0;
  margin-bottom: -0.5rem; /* tuck the first row right under the label */
}

/* ---------- Mobile: toolbar stacks, chip rows scroll horizontally ---------- */

@media (width < 40rem) {
  .cl-list-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: 0.45rem;
    padding-inline: 0;
  }

  .cl-list-toolbar__group {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    gap: 0.45rem;
    padding-bottom: 2px;
    min-width: 0;
    max-width: 100%;
  }
  .cl-list-toolbar__group::-webkit-scrollbar { display: none; }

  .cl-list-toolbar__label {
    display: none;
  }

  .cl-list-toolbar__divider {
    display: none;
  }

  .cl-list-toolbar__counter {
    display: none;
  }

  .cl-chip {
    white-space: nowrap;
    flex-shrink: 0;
    padding: 0.35rem 0.8rem;
    font-size: 0.8rem;
  }
}

/* ============================================================
 * Conversation cards (.cl-conv / .cl-msg / .cl-reply)
 * Photographer comments overview — one inline chat thread per
 * image, in the "Kommentar-Thread Final (Dialog)" design:
 * guest bubbles left, your replies right, composer in the footer.
 * Mirrors the claude.ai/design tokens (--primary-*, --slate-*,
 * --color-border, --color-main-bg, --shadow-xs) one-to-one.
 * ============================================================ */

/* ---------- Card list + bucket spacing ---------- */

.cl-list--cards {
  gap: 0;
}

.cl-list--cards .cl-list__bucket {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.cl-list--cards .cl-list__bucket-label {
  font-family: var(--font-grotesk, "Space Grotesk", "Inter", sans-serif);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--slate-400, #94a3b8);
  padding: 1.25rem 0 0;
  margin: 0 0 12px;
}

.cl-list--cards .cl-list__bucket:first-child .cl-list__bucket-label {
  padding-top: 0.25rem;
}

/* ---------- Card shell ---------- */

.cl-conv {
  background: #fff;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 16px;
  box-shadow: var(--shadow-xs, 0 1px 2px 0 #0000000d);
  /* overflow stays visible so the bubble 3-dot menus and the emoji picker can
     spill outside the card; the header thumb and footer round themselves. */
  margin-bottom: 22px;
}

.cl-conv:last-child {
  margin-bottom: 0;
}

/* ---------- Card header (thumb + meta + dot + time) ---------- */

.cl-conv__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--color-border-light, #f1f5f9);
}

.cl-conv__thumb {
  position: relative;
  width: 48px;
  height: 48px;
  border-radius: 10px;
  overflow: hidden;
  flex: none;
  background: var(--slate-100, #f1f5f9);
}

.cl-conv__thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Zoomable thumbnail: a reset <button> that opens the PhotoSwipe viewer. The
   magnifier overlay fades in on hover/focus and the cursor turns to zoom-in. */
.cl-conv__thumb--zoom {
  padding: 0;
  border: 0;
  cursor: zoom-in;
  appearance: none;
  -webkit-appearance: none;
}

.cl-conv__thumb-zoom {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: rgba(15, 23, 42, 0.42);
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.cl-conv__thumb-zoom svg {
  width: 18px;
  height: 18px;
}

.cl-conv__thumb--zoom:hover .cl-conv__thumb-zoom,
.cl-conv__thumb--zoom:focus-visible .cl-conv__thumb-zoom {
  opacity: 1;
}

.cl-conv__thumb--zoom:focus-visible {
  outline: 2px solid var(--primary-400, #a78bfa);
  outline-offset: 2px;
}

/* Gallery badges: small chips under the subtitle linking to each gallery the
   commented image belongs to, plus a "+n" chip for the overflow. */
.cl-conv__galleries {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 5px;
}

.cl-conv__gallery {
  display: inline-block;
  max-width: 140px;
  padding: 1px 8px;
  border-radius: 9999px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--primary-700, #5b21b6);
  background: var(--primary-100, #ede9fe);
  border: 1px solid var(--primary-200, #ddd6fe);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.12s ease;
}

a.cl-conv__gallery:hover {
  background: var(--primary-200, #ddd6fe);
}

.cl-conv__gallery--more {
  color: var(--slate-500, #64748b);
  background: var(--slate-100, #f1f5f9);
  border-color: var(--slate-200, #e2e8f0);
}

.cl-conv__thumb-ph {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--slate-500, #64748b);
  text-align: center;
  padding: 2px;
}

.cl-conv__meta {
  flex: 1;
  min-width: 0;
}

.cl-conv__title {
  font-family: var(--font-grotesk, "Space Grotesk", "Inter", sans-serif);
  font-size: 15px;
  font-weight: 600;
  color: var(--slate-900, #0f172a);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cl-conv__sub {
  font-size: 12.5px;
  color: var(--slate-400, #94a3b8);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cl-conv__dot {
  width: 8px;
  height: 8px;
  border-radius: 9999px;
  background: var(--primary-600, #6d28d9);
  flex: none;
}

.cl-conv__time {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--slate-400, #94a3b8);
  flex: none;
  white-space: nowrap;
}

/* ---------- Thread body ---------- */

.cl-conv__thread {
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding: 18px 20px;
  background: var(--color-main-bg, #faf9fc);
}

/* Day separator inside a multi-day thread */
.cl-conv__day {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px 0;
}

.cl-conv__day-pill {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate-400, #94a3b8);
}

/* ---------- Message group ---------- */

.cl-msg {
  display: flex;
  gap: 11px;
  align-items: flex-start;
}

.cl-msg--self {
  flex-direction: row-reverse;
}

.cl-msg__avatar {
  width: 30px;
  height: 30px;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  flex: none;
  object-fit: cover;
}

.cl-msg__col {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  max-width: 72%;
  min-width: 0;
}

.cl-msg--self .cl-msg__col {
  align-items: flex-end;
}

.cl-msg__head {
  font-size: 12px;
}

.cl-msg__name {
  font-weight: 600;
  color: var(--slate-500, #64748b);
}

.cl-msg__name--self {
  color: var(--primary-700, #5b21b6);
}

/* Small role tag after the author name (e.g. "Fotograf"). */
.cl-msg__tag {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 9999px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--primary-700, #5b21b6);
  background: var(--primary-100, #ede9fe);
  border: 1px solid var(--primary-200, #ddd6fe);
  vertical-align: 1px;
}

.cl-msg__time {
  color: var(--slate-400, #94a3b8);
  font-weight: 400;
  margin-left: 7px;
}

/* ---------- Bubble ---------- */

/* Bubble + 3-dot menu sit side by side; the trigger always lands on the right
 * of the bubble — for guest bubbles (left-aligned) and own replies
 * (right-aligned) alike — so the affordance is in one predictable spot. */
.cl-msg__bubble-row {
  display: flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  min-width: 0;
}

.cl-msg__bubble {
  position: relative;
  padding: 10px 14px;
  border-radius: 15px;
  font-size: 14px;
  line-height: 1.45;
  max-width: 100%;
  min-width: 0;
}

.cl-msg__bubble--other {
  background: #fff;
  border: 1px solid var(--color-border, #e2e8f0);
  color: var(--slate-800, #1e293b);
}

.cl-msg__bubble--self {
  background: var(--primary-600, #6d28d9);
  color: #fff;
}

.cl-msg__bubble--other.cl-msg__bubble--first {
  border-radius: 3px 15px 15px 15px;
}

.cl-msg__bubble--self.cl-msg__bubble--first {
  border-radius: 15px 3px 15px 15px;
}

.cl-msg__body {
  display: block;
  white-space: pre-wrap;
  word-break: break-word;
}

.cl-msg__edited {
  display: inline-block;
  margin-top: 3px;
  font-size: 10.5px;
  font-style: italic;
  opacity: 0.7;
}

/* ---------- Reactions under a bubble ---------- */

/* The shared gc-react-chips list (reaction_menu.css) as a bubble-row sibling
 * inside the message column: WhatsApp-style, the chips overlap the bubble's
 * bottom edge slightly. Column alignment (start/end) comes from
 * .cl-msg__col, so own messages align their chips right automatically. */
.cl-msg__reactions {
  margin-top: -6px;
  padding-inline: 6px;
  position: relative;
  z-index: 1;
}

.cl-msg__reactions .gc-react-chip {
  box-shadow: 0 1px 3px oklch(0 0 0 / 0.1);
}

/* Long-press opens the reaction menu on touch — suppress the native
 * text-selection callout on bubbles of reaction-enabled cards so the gesture
 * feels like WhatsApp. Desktop text selection stays untouched. */
@media (pointer: coarse) {
  .cl-conv:has(.gc-react-menu) .cl-msg__bubble {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
  }
}

/* ---------- Reply quote inside a bubble ---------- */

.cl-msg__quote {
  display: block;
  border-left: 2px solid currentColor;
  padding-left: 8px;
  margin-bottom: 6px;
  opacity: 0.85;
}

.cl-msg__bubble--other .cl-msg__quote {
  border-left-color: var(--primary-400, #a78bfa);
}

.cl-msg__bubble--self .cl-msg__quote {
  border-left-color: rgba(255, 255, 255, 0.6);
}

.cl-msg__quote-author {
  display: block;
  font-size: 11px;
  font-weight: 700;
  margin-bottom: 1px;
}

.cl-msg__quote-body {
  display: block;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 30ch;
}

/* ---------- Per-bubble 3-dot menu ---------- */

/* The menu is a flex sibling of the bubble (not absolutely placed over it), so
 * the trigger never overlaps the text. The dropdown list stays absolute,
 * anchored to this relative wrapper. */
.cl-msg__menu {
  position: relative;
  flex: none;
  align-self: center;
}

.cl-msg__more {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 8px;
  background: transparent;
  /* Always fully visible in a clear slate tone so the actions affordance reads
   * at a glance, no hover required. */
  color: var(--slate-600, #475569);
  cursor: pointer;
  opacity: 1;
  transition: background 120ms ease, color 120ms ease;
}

.cl-msg__more svg {
  width: 19px;
  height: 19px;
}

.cl-msg__more:hover,
.cl-msg__menu.is-open .cl-msg__more {
  background: var(--slate-100, #f1f5f9);
  color: var(--slate-800, #1e293b);
}

.cl-msg__menu-list {
  position: absolute;
  top: 28px;
  right: 0;
  display: none;
  min-width: 168px;
  padding: 5px;
  background: #fff;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 10px;
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.25);
  z-index: 50;
}

.cl-msg__menu.is-open .cl-msg__menu-list {
  display: block;
}

/* The trigger now sits on the right of every bubble; the list always opens
 * leftward (anchored to the trigger's right edge) so it stays in the viewport
 * for both guest and own replies. */

.cl-msg__menu-item {
  appearance: none;
  display: flex;
  align-items: center;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--slate-700, #334155);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}

.cl-msg__menu-item:hover {
  background: var(--slate-100, #f1f5f9);
  color: var(--slate-900, #0f172a);
}

.cl-msg__menu-item--danger {
  color: var(--red-600, #dc2626);
}

.cl-msg__menu-item--danger:hover {
  background: var(--red-50, #fef2f2);
  color: var(--red-700, #b91c1c);
}

/* ---------- Bubble delete fade-out + collapse ---------- */

/* The chat-card controller sets an explicit start height, forces a reflow and
 * adds .is-removing — the row (or, for a group's last bubble, the whole
 * message group incl. avatar and name) fades and shrinks away, then the node
 * is dropped on transitionend. Skipped under prefers-reduced-motion. */
.cl-msg__bubble-row.is-removing,
.cl-msg.is-removing {
  opacity: 0;
  transform: scale(0.96);
  height: 0 !important;
  min-height: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  overflow: hidden;
  transition:
    opacity 240ms ease,
    transform 240ms ease,
    height 240ms var(--ease-crisp, cubic-bezier(0.2, 0.6, 0.2, 1)),
    margin 240ms var(--ease-crisp, cubic-bezier(0.2, 0.6, 0.2, 1)),
    padding 240ms var(--ease-crisp, cubic-bezier(0.2, 0.6, 0.2, 1));
  pointer-events: none;
}

/* ---------- Appended-bubble entrance ---------- */

/* The chat-card controller tags a freshly posted message group with
 * .cl-msg--entering: it rises in with a gentle toast-style overshoot while
 * the thread smooth-scrolls it into view. The class is removed on
 * animationend (and never added under prefers-reduced-motion). */
@keyframes cl-msg-in {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.96);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.cl-msg--entering {
  animation: cl-msg-in 280ms var(--ease-toast, cubic-bezier(0.18, 1.25, 0.4, 1)) both;
}

.cl-msg--self.cl-msg--entering {
  transform-origin: bottom right;
}

.cl-msg--other.cl-msg--entering {
  transform-origin: bottom left;
}

@media (prefers-reduced-motion: reduce) {
  .cl-msg--entering {
    animation: none;
  }
}

/* ---------- Composer (footer) ---------- */

.cl-reply {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 13px 18px;
  border-top: 1px solid var(--color-border-light, #f1f5f9);
  background: #fff;
  /* round the footer corners — the card itself no longer clips (overflow). */
  border-radius: 0 0 15px 15px;
}

/* Context bar shown when replying-with-quote or editing. */
.cl-reply__bar {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 640px;
  padding: 7px 10px 7px 12px;
  border-left: 3px solid var(--primary-400, #a78bfa);
  border-radius: 8px;
  background: var(--slate-50, #f8fafc);
}

.cl-reply__bar[hidden] {
  display: none;
}

.cl-reply__bar-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.cl-reply__bar-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--primary-700, #5b21b6);
}

.cl-reply__bar-snippet {
  font-size: 12px;
  color: var(--slate-500, #64748b);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cl-reply__bar-cancel {
  appearance: none;
  flex: none;
  border: none;
  background: transparent;
  color: var(--slate-500, #64748b);
  font-size: 18px;
  line-height: 1;
  padding: 0 2px;
  cursor: pointer;
  border-radius: 4px;
}

.cl-reply__bar-cancel:hover {
  color: var(--slate-800, #1e293b);
}

/* Input row — width-capped so the reply field doesn't stretch the full card. */
.cl-reply__row {
  display: flex;
  align-items: flex-end;
  gap: 11px;
  max-width: 640px;
}

.cl-reply__avatar {
  width: 28px;
  height: 28px;
  border-radius: 9999px;
  flex: none;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  overflow: hidden;
  object-fit: cover;
}

/* Emoji picker (gui_components) — align its button to the input baseline. */
.cl-reply__emoji {
  flex: none;
  margin-bottom: 4px;
}

.cl-reply__input {
  flex: 1;
  min-width: 0;
  resize: none;
  /* auto (not hidden) so a long reply scrolls to keep the caret/end visible
     once the textarea hits its max-height. */
  overflow-y: auto;
  min-height: 40px;
  max-height: 140px;
  padding: 9px 14px;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 10px;
  background: var(--slate-50, #f8fafc);
  color: var(--slate-800, #1e293b);
  font-family: inherit;
  /* >=16px keeps iOS Safari from zooming the page on focus. */
  font-size: 16px;
  line-height: 1.4;
  transition: border-color 160ms ease, box-shadow 160ms ease, background 160ms ease;
}

.cl-reply__input::placeholder {
  color: var(--slate-400, #94a3b8);
}

.cl-reply__input:focus {
  outline: none;
  border-color: var(--primary-400, #a78bfa);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-600, #6d28d9) 15%, transparent);
  background: #fff;
}

.cl-reply__send {
  appearance: none;
  width: 40px;
  height: 40px;
  flex: none;
  border: none;
  border-radius: 10px;
  background: var(--primary-600, #6d28d9);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 160ms ease, opacity 160ms ease, transform 120ms ease;
}

.cl-reply__send svg {
  width: 18px;
  height: 18px;
}

.cl-reply__send:hover {
  background: var(--primary-700, #5b21b6);
}

.cl-reply__send:active {
  transform: scale(0.94);
}

.cl-reply__send:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Sending state: replace the paper-plane with a spinner (button stays
 * disabled so it can't be double-fired). */
.cl-reply__send.is-sending {
  opacity: 1;
}

.cl-reply__send.is-sending svg {
  display: none;
}

.cl-reply__send.is-sending::after {
  content: "";
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  animation: cl-send-spin 0.6s linear infinite;
}

@keyframes cl-send-spin {
  to { transform: rotate(360deg); }
}

/* ---------- Mobile tightening ---------- */

@media (width < 40rem) {
  .cl-conv__thread {
    padding: 14px 14px;
    gap: 13px;
  }

  .cl-conv__head {
    padding: 12px 14px;
  }

  .cl-msg__col {
    max-width: 82%;
  }

  .cl-reply {
    padding: 11px 14px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cl-reply__input,
  .cl-reply__send {
    transition: none;
  }

  .cl-reply__send.is-sending::after {
    animation: none;
  }

  .cl-msg__bubble-row.is-removing,
  .cl-msg.is-removing {
    transition: none;
  }
}

/* ============================================================
 * Two-column page: card column + filter rail
 * Mobile-first the rail renders as the existing horizontal chip
 * bar ABOVE the list (order:-1). From 64rem it becomes a sticky
 * vertical rail to the right of a width-capped card column.
 * Only new comments-scoped classes (.cl-page*) are introduced;
 * the shared .cl-list-toolbar / .cl-chip base styles (also used by
 * the likes page) are left untouched and only overridden when
 * nested inside .cl-page__rail.
 * ============================================================ */

.cl-page {
  display: flex;
  flex-direction: column;
}

.cl-page__main {
  min-width: 0;
}

/* Filters sit above the list on narrow viewports (today's behaviour). */
.cl-page__rail {
  order: -1;
}

@media (width >= 64rem) {
  .cl-page {
    display: grid;
    grid-template-columns: minmax(0, 44rem) 16rem;
    gap: 1.5rem;
    align-items: start;
  }

  .cl-page__rail {
    order: 0;
    align-self: start;
    /* The scroll container is #main (below the sticky topbar), so top is
     * measured from #main's top — a small offset gives breathing room without
     * double-counting the 56px bar. max-height keeps a long chip list from
     * running off-screen; the rail then scrolls itself. */
    position: sticky;
    top: 0.5rem;
    max-height: calc(100dvh - 56px - 0.5rem);
    overflow-y: auto;
  }

  /* Stack the toolbar groups vertically inside the rail. */
  .cl-page__rail .cl-list-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: 1rem;
    padding: 0;
    margin: 0;
    border-bottom: none;
  }

  .cl-page__rail .cl-list-toolbar__group {
    flex-direction: column;
    align-items: stretch;
    gap: 0.4rem;
  }

  .cl-page__rail .cl-list-toolbar__divider {
    display: none;
  }

  .cl-page__rail .cl-list-toolbar__label {
    margin: 0;
    font-weight: 600;
    color: var(--slate-500, #64748b);
  }

  .cl-page__rail .cl-chip {
    width: 100%;
    justify-content: flex-start;
  }

  .cl-page__rail .cl-list-toolbar__counter {
    margin-left: 0;
  }
}

/* ---------- Card collapse ---------- */

/* The whole header is the click surface for collapsing the card. */
.cl-conv__head {
  cursor: pointer;
}

.cl-conv__collapse {
  appearance: none;
  flex: none;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--slate-400, #94a3b8);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}

.cl-conv__collapse:hover {
  background: var(--slate-100, #f1f5f9);
  color: var(--slate-700, #334155);
}

.cl-conv__collapse svg {
  width: 18px;
  height: 18px;
  transition: transform 180ms ease;
}

.cl-conv--collapsed .cl-conv__collapse svg {
  transform: rotate(-90deg);
}

/* Collapsed: only the header stays; thread + composer fold away. */
.cl-conv--collapsed .cl-conv__thread,
.cl-conv--collapsed .cl-reply {
  display: none;
}

.cl-conv--collapsed .cl-conv__head {
  border-bottom: none;
}

@media (prefers-reduced-motion: reduce) {
  .cl-conv__collapse svg {
    transition: none;
  }
}

/* ============================================================
 * Dark scheme
 * The semantic tokens (--color-main-bg, --color-border,
 * --color-border-light) flip on their own via base.css; the rules
 * below re-map every hardcoded light value (white surfaces, slate
 * text, primary-100 tints) onto the dark brand-neutral ramp.
 * ============================================================ */

/* ---------- Toolbar + chips ---------- */

[data-color-scheme="dark"] .cl-list-toolbar {
  border-bottom-color: rgba(255, 255, 255, 0.1);
}

[data-color-scheme="dark"] .cl-list-toolbar__label,
[data-color-scheme="dark"] .cl-list-toolbar__counter {
  color: var(--color-text-subtle, #94a3b8);
}

[data-color-scheme="dark"] .cl-list-toolbar__divider {
  background: rgba(255, 255, 255, 0.16);
}

[data-color-scheme="dark"] .cl-chip {
  background: var(--brand-neutral-900, #1c1c21);
  border-color: var(--color-border, #33333c);
  color: var(--color-text, #f4f4f5);
}

[data-color-scheme="dark"] .cl-chip:hover {
  background: var(--brand-neutral-800, #27272e);
  border-color: var(--color-border-dark, #4b4b55);
}

[data-color-scheme="dark"] .cl-chip--active {
  background: var(--primary-950);
  border-color: var(--primary-800);
  color: var(--primary-300);
}

[data-color-scheme="dark"] .cl-chip--unread.cl-chip--active {
  background: var(--red-950, #450a0a);
  border-color: var(--red-800, #991b1b);
  color: var(--red-300, #fca5a5);
}

/* ---------- Bucket labels ---------- */

[data-color-scheme="dark"] .cl-list__bucket-label {
  color: var(--color-text-subtle, #94a3b8);
}

/* ---------- Card shell + header ---------- */

[data-color-scheme="dark"] .cl-conv {
  background: var(--brand-neutral-950, #101014);
}

[data-color-scheme="dark"] .cl-conv__title {
  color: var(--color-text, #f4f4f5);
}

[data-color-scheme="dark"] .cl-conv__thumb {
  background: var(--brand-neutral-800, #27272e);
}

[data-color-scheme="dark"] .cl-conv__thumb-ph {
  color: var(--brand-neutral-400, #9494a1);
}

[data-color-scheme="dark"] .cl-conv__gallery {
  color: var(--primary-300);
  background: var(--primary-950);
  border-color: var(--primary-800);
}

[data-color-scheme="dark"] a.cl-conv__gallery:hover {
  background: var(--primary-900);
}

[data-color-scheme="dark"] .cl-conv__gallery--more {
  color: var(--brand-neutral-400, #9494a1);
  background: var(--brand-neutral-800, #27272e);
  border-color: var(--brand-neutral-700, #38383f);
}

[data-color-scheme="dark"] .cl-conv__collapse:hover {
  background: var(--brand-neutral-800, #27272e);
  color: var(--brand-neutral-200, #d4d4dc);
}

/* ---------- Messages + bubbles ---------- */

[data-color-scheme="dark"] .cl-msg__name {
  color: var(--brand-neutral-400, #9494a1);
}

[data-color-scheme="dark"] .cl-msg__tag {
  color: var(--primary-300);
  background: var(--primary-950);
  border-color: var(--primary-800);
}

[data-color-scheme="dark"] .cl-msg__name--self {
  color: var(--primary-300);
}

[data-color-scheme="dark"] .cl-msg__bubble--other {
  background: var(--brand-neutral-900, #1c1c21);
  border-color: var(--color-border, #33333c);
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-msg__more {
  color: var(--brand-neutral-400, #9494a1);
}

[data-color-scheme="dark"] .cl-msg__more:hover,
[data-color-scheme="dark"] .cl-msg__menu.is-open .cl-msg__more {
  background: var(--brand-neutral-800, #27272e);
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-msg__menu-list {
  background: var(--brand-neutral-900, #1c1c21);
  border-color: var(--color-border, #33333c);
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.7);
}

[data-color-scheme="dark"] .cl-msg__menu-item {
  color: var(--brand-neutral-200, #d4d4dc);
}

[data-color-scheme="dark"] .cl-msg__menu-item:hover {
  background: var(--brand-neutral-800, #27272e);
  color: white;
}

[data-color-scheme="dark"] .cl-msg__menu-item--danger {
  color: var(--red-400, #f87171);
}

[data-color-scheme="dark"] .cl-msg__menu-item--danger:hover {
  background: var(--red-950, #450a0a);
  color: var(--red-300, #fca5a5);
}

/* ---------- Composer ---------- */

[data-color-scheme="dark"] .cl-reply {
  background: var(--brand-neutral-950, #101014);
}

[data-color-scheme="dark"] .cl-reply__bar {
  background: var(--brand-neutral-900, #1c1c21);
  border-left-color: var(--primary-500);
}

[data-color-scheme="dark"] .cl-reply__bar-title {
  color: var(--primary-300);
}

[data-color-scheme="dark"] .cl-reply__bar-snippet,
[data-color-scheme="dark"] .cl-reply__bar-cancel {
  color: var(--brand-neutral-400, #9494a1);
}

[data-color-scheme="dark"] .cl-reply__bar-cancel:hover {
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-reply__input {
  background: var(--brand-neutral-900, #1c1c21);
  border-color: var(--color-border, #33333c);
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-reply__input::placeholder {
  color: var(--brand-neutral-500, #6e6e7a);
}

[data-color-scheme="dark"] .cl-reply__input:focus {
  background: var(--brand-neutral-950, #101014);
  border-color: var(--primary-500);
}

/* ---------- Rail ---------- */

@media (width >= 64rem) {
  [data-color-scheme="dark"] .cl-page__rail .cl-list-toolbar__label {
    color: var(--color-text-subtle, #94a3b8);
  }
}

/* ============================================================
 * Chat Prototyp additions (v1.168): composer variants V1–V3,
 * voice-recording row, attachment sheet, media bubbles (audio /
 * image / document) and the typing indicator.
 * Animations mirror the claude.ai/design prototype (pop-in,
 * sheet-in, level bars, pulsing record dot) using the shared
 * easings from animations.css.
 * ============================================================ */

@keyframes cl-pop-in {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes cl-sheet-in {
  from { transform: translateY(100%); }
  to   { transform: none; }
}

@keyframes cl-rec-bar {
  0%, 100% { transform: scaleY(0.35); }
  50%      { transform: scaleY(1); }
}

@keyframes cl-rec-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.3; }
}

@keyframes cl-dot-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
  30%           { transform: translateY(-4px); opacity: 1; }
}

/* ---------- Composer shell for the variants ---------- */

/* Positioning context for the attach sheet popover (desktop). */
.cl-reply {
  position: relative;
}

/* Round ghost action button (attach, shortcuts, mic). */
.cl-reply__action,
.cl-reply__mic {
  appearance: none;
  width: 40px;
  height: 40px;
  flex: none;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: var(--slate-500, #64748b);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, transform 120ms ease;
}

.cl-reply__action svg,
.cl-reply__mic svg {
  width: 21px;
  height: 21px;
}

.cl-reply__action:hover,
.cl-reply__mic:hover {
  background: var(--primary-100, #ede9fe);
  color: var(--primary-700, #5b21b6);
}

.cl-reply__action:active,
.cl-reply__mic:active {
  transform: scale(0.94);
}

.cl-reply__action--sm {
  width: 32px;
  height: 32px;
}

.cl-reply__action--sm svg {
  width: 17px;
  height: 17px;
}

.cl-reply__action--chip {
  width: 28px;
  height: 28px;
  background: var(--slate-100, #f1f5f9);
  color: var(--slate-700, #334155);
}

.cl-reply__action--chip svg {
  width: 15px;
  height: 15px;
}

/* Round send button (classic / card / compact variants). */
.cl-reply__send--round {
  width: 42px;
  height: 42px;
  border-radius: 9999px;
}

.cl-reply__send--sm {
  width: 32px;
  height: 32px;
}

.cl-reply__send--sm svg {
  width: 15px;
  height: 15px;
}

/* ---------- V1 · classic (WhatsApp): pill input + mic ↔ send morph ---------- */

.cl-reply--classic .cl-reply__row {
  align-items: flex-end;
  gap: 7px;
  max-width: none;
}

.cl-reply__input--pill {
  border-radius: 999px;
  background: var(--color-bg-white, #fff);
  padding: 8px 16px;
}

/* Filled primary mic (idle state of the morph pair). */
.cl-reply__mic--primary {
  width: 42px;
  height: 42px;
  background: var(--primary-600, #6d28d9);
  color: #fff;
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.12));
}

.cl-reply__mic--primary:hover {
  background: var(--primary-700, #5b21b6);
  color: #fff;
}

/* The morph: with no draft the mic shows; as soon as text (or a pending
 * attachment) exists the chat-composer controller sets .is-typing and the
 * send button pops in where the mic was. */
.cl-reply--classic .cl-reply__send--morph {
  display: none;
}

.cl-reply--classic.is-typing .cl-reply__send--morph {
  display: flex;
  animation: cl-pop-in 180ms var(--ease-toast, cubic-bezier(0.18, 1.25, 0.4, 1)) both;
}

.cl-reply--classic.is-typing .cl-reply__mic--primary {
  display: none;
}

/* ---------- V2 · card (AI-input): textarea on top, action bar below ---------- */

.cl-reply__card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 14px;
  background: var(--color-bg-white, #fff);
  box-shadow: var(--shadow-xs, 0 1px 2px 0 #0000000d);
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.cl-reply__card:focus-within {
  border-color: var(--primary-400, #a78bfa);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-600, #6d28d9) 15%, transparent);
}

.cl-reply__input--bare,
.cl-reply__input--bare:focus {
  border: none;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  padding: 2px 4px;
  min-height: 28px;
}

.cl-reply__actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

.cl-reply__spacer {
  flex: 1;
}

/* ---------- V3 · compact: everything in one pill ---------- */

.cl-reply__pill {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 44px;
  padding: 2px 5px 2px 6px;
  border: 1px solid var(--color-border, #e2e8f0);
  border-radius: 999px;
  background: var(--color-bg-white, #fff);
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.cl-reply__pill:focus-within {
  border-color: var(--primary-400, #a78bfa);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-600, #6d28d9) 15%, transparent);
}

.cl-reply__pill .cl-reply__input--bare {
  min-height: 24px;
  max-height: 24px;
  overflow: hidden;
  white-space: nowrap;
  resize: none;
}

/* ---------- Voice-recording row ---------- */

.cl-reply__rec {
  display: none;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 4px;
}

.cl-reply.is-recording .cl-reply__rec {
  display: flex;
  animation: cl-msg-in 200ms var(--ease-toast, cubic-bezier(0.18, 1.25, 0.4, 1)) both;
}

.cl-reply.is-recording .cl-reply__row,
.cl-reply.is-recording .cl-reply__card,
.cl-reply.is-recording .cl-reply__pill,
.cl-reply.is-recording .cl-reply__pending {
  display: none;
}

.cl-reply__rec-cancel {
  appearance: none;
  width: 38px;
  height: 38px;
  flex: none;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: var(--red-600, #dc2626);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 140ms ease;
}

.cl-reply__rec-cancel:hover {
  background: var(--red-50, #fef2f2);
}

.cl-reply__rec-cancel svg {
  width: 18px;
  height: 18px;
}

.cl-reply__rec-dot {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 9999px;
  background: var(--red-600, #dc2626);
  animation: cl-rec-pulse 1s ease-in-out infinite;
}

.cl-reply__rec-time {
  font-size: 14px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 36px;
  color: var(--slate-800, #1e293b);
}

.cl-reply__rec-bars {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 26px;
}

.cl-reply__rec-bars span {
  width: 4px;
  border-radius: 2px;
  background: var(--primary-600, #6d28d9);
  animation: cl-rec-bar 800ms ease-in-out infinite;
}

.cl-reply__rec-bars span:nth-child(1) { height: 20px; }
.cl-reply__rec-bars span:nth-child(2) { height: 24px; animation-delay: 120ms; }
.cl-reply__rec-bars span:nth-child(3) { height: 16px; animation-delay: 240ms; }
.cl-reply__rec-bars span:nth-child(4) { height: 22px; animation-delay: 360ms; }
.cl-reply__rec-bars span:nth-child(5) { height: 14px; animation-delay: 480ms; }

.cl-reply__rec-send {
  appearance: none;
  width: 42px;
  height: 42px;
  flex: none;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: var(--primary-600, #6d28d9);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.12));
  transition: background 140ms ease, transform 120ms ease;
}

.cl-reply__rec-send:hover {
  background: var(--primary-700, #5b21b6);
}

.cl-reply__rec-send:active {
  transform: scale(0.95);
}

.cl-reply__rec-send svg {
  width: 18px;
  height: 18px;
}

/* ---------- Pending-attachment chip ---------- */

.cl-reply__pending {
  display: flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  padding: 6px 10px;
  border-radius: 10px;
  background: var(--primary-100, #ede9fe);
  font-size: 12.5px;
  animation: cl-pop-in 200ms var(--ease-toast, cubic-bezier(0.18, 1.25, 0.4, 1)) both;
}

.cl-reply__pending[hidden] {
  display: none;
}

.cl-reply__pending-icon {
  color: var(--primary-700, #5b21b6);
}

.cl-reply__pending-icon svg,
.cl-reply__pending-icon {
  width: 15px;
  height: 15px;
}

.cl-reply__pending-name {
  font-weight: 600;
  color: var(--slate-800, #1e293b);
}

.cl-reply__pending-remove {
  appearance: none;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: var(--slate-500, #64748b);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.cl-reply__pending-remove svg {
  width: 12px;
  height: 12px;
}

.cl-reply__pending-remove:hover {
  color: var(--slate-800, #1e293b);
}

/* ---------- Attachment sheet ---------- */

/* Desktop: anchored popover card above the composer. Mobile (<40rem):
 * fixed bottom sheet with a dimmed backdrop, WhatsApp-style. */
.cl-reply__backdrop {
  display: none;
}

.cl-reply__sheet {
  display: none;
  background: var(--color-bg-white, #fff);
  box-shadow: var(--shadow-xl, 0 20px 40px -12px rgba(0, 0, 0, 0.3));
}

.cl-reply.is-attach-open .cl-reply__sheet {
  display: block;
}

.cl-reply__sheet-handle {
  display: none;
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--slate-300, #cbd5e1);
  margin: 0 auto 14px;
}

.cl-reply__sheet-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.cl-reply__sheet-item {
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 10px 8px;
  border: none;
  border-radius: 14px;
  background: transparent;
  cursor: pointer;
  font-family: inherit;
  transition: background 140ms ease;
}

.cl-reply__sheet-item:hover {
  background: var(--slate-100, #f1f5f9);
}

.cl-reply__sheet-icon {
  width: 54px;
  height: 54px;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}

.cl-reply__sheet-icon svg {
  width: 23px;
  height: 23px;
}

.cl-reply__sheet-icon--primary   { background: var(--primary-600, #6d28d9); }
.cl-reply__sheet-icon--secondary { background: var(--teal-600, #0d9488); }
.cl-reply__sheet-icon--accent    { background: var(--amber-500, #f59e0b); }

.cl-reply__sheet-label {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--slate-800, #1e293b);
}

@media (width >= 40rem) {
  .cl-reply.is-attach-open .cl-reply__sheet {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 12px;
    min-width: 300px;
    padding: 12px;
    border: 1px solid var(--color-border, #e2e8f0);
    border-radius: 16px;
    z-index: 40;
    animation: cl-pop-in 180ms var(--ease-toast, cubic-bezier(0.18, 1.25, 0.4, 1)) both;
    transform-origin: bottom left;
  }
}

@media (width < 40rem) {
  .cl-reply.is-attach-open .cl-reply__backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(10, 16, 28, 0.32);
    z-index: 60;
    animation: fade-in 200ms ease both;
  }

  .cl-reply.is-attach-open .cl-reply__sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 10px 16px calc(20px + env(safe-area-inset-bottom, 0px));
    border-radius: 20px 20px 0 0;
    z-index: 61;
    animation: cl-sheet-in 280ms var(--ease-toast, cubic-bezier(0.18, 1.25, 0.4, 1)) both;
  }

  .cl-reply.is-attach-open .cl-reply__sheet-handle {
    display: block;
  }
}

/* ---------- Audio bubble ---------- */

.cl-msg__bubble--audio {
  padding: 8px 12px;
}

.cl-audio {
  display: flex;
  align-items: center;
  gap: 9px;
}

.cl-audio__play {
  appearance: none;
  width: 34px;
  height: 34px;
  flex: none;
  padding: 0;
  border: none;
  border-radius: 9999px;
  background: var(--primary-600, #6d28d9);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 120ms ease, background 140ms ease;
}

.cl-audio__play:active {
  transform: scale(0.94);
}

.cl-msg__bubble--self .cl-audio__play {
  background: #fff;
  color: var(--primary-600, #6d28d9);
}

.cl-audio__icon {
  display: flex;
}

.cl-audio__icon[hidden] {
  display: none;
}

.cl-audio__icon svg {
  width: 14px;
  height: 14px;
}

.cl-audio__wave {
  display: flex;
  align-items: center;
  gap: 2px;
  height: 26px;
}

.cl-audio__bar {
  width: 3px;
  border-radius: 2px;
  background: var(--primary-200, #ddd6fe);
  transition: background 120ms ease;
}

.cl-audio__bar.is-played {
  background: var(--primary-600, #6d28d9);
}

.cl-msg__bubble--self .cl-audio__bar {
  background: rgba(255, 255, 255, 0.4);
}

.cl-msg__bubble--self .cl-audio__bar.is-played {
  background: #fff;
}

.cl-audio__time {
  font-size: 11px;
  opacity: 0.85;
  min-width: 30px;
  font-variant-numeric: tabular-nums;
}

/* ---------- Image bubble ---------- */

.cl-msg__bubble--media {
  padding: 4px;
  overflow: hidden;
}

.cl-media__img {
  display: block;
  max-width: 240px;
  height: auto;
  border-radius: 12px;
}

.cl-media__placeholder {
  width: 226px;
  height: 152px;
  max-width: 100%;
  border-radius: 12px;
  background: linear-gradient(135deg, var(--primary-200, #ddd6fe), var(--primary-100, #ede9fe));
  display: flex;
  align-items: center;
  justify-content: center;
}

.cl-media__placeholder-badge {
  width: 46px;
  height: 46px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--primary-700, #5b21b6);
}

.cl-media__placeholder-badge svg {
  width: 22px;
  height: 22px;
}

.cl-media__caption {
  font-size: 14px;
  line-height: 1.4;
  padding: 6px 8px 4px;
}

/* ---------- Document bubble ---------- */

.cl-msg__bubble--file {
  padding: 8px 12px;
}

.cl-file {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 200px;
  padding: 2px;
  text-decoration: none;
  color: inherit;
}

.cl-file__tile {
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 10px;
  background: var(--primary-100, #ede9fe);
  color: var(--primary-700, #5b21b6);
  display: flex;
  align-items: center;
  justify-content: center;
}

.cl-msg__bubble--self .cl-file__tile {
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}

.cl-file__tile svg {
  width: 19px;
  height: 19px;
}

.cl-file__meta {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.cl-file__name {
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 170px;
}

.cl-file__sub {
  font-size: 11.5px;
  opacity: 0.7;
}

/* ---------- Typing indicator ---------- */

.cl-typing__bubble {
  display: flex;
  gap: 4px;
  padding: 12px 14px;
}

.cl-typing__dot {
  width: 7px;
  height: 7px;
  border-radius: 9999px;
  background: var(--slate-400, #94a3b8);
  animation: cl-dot-bounce 1.2s infinite;
}

.cl-typing__dot:nth-child(2) { animation-delay: 150ms; }
.cl-typing__dot:nth-child(3) { animation-delay: 300ms; }

/* ---------- Mobile tightening for the new pieces ---------- */

@media (width < 40rem) {
  .cl-reply--classic .cl-reply__row {
    gap: 5px;
  }

  .cl-media__img,
  .cl-media__placeholder {
    max-width: 200px;
  }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .cl-reply--classic.is-typing .cl-reply__send--morph,
  .cl-reply.is-recording .cl-reply__rec,
  .cl-reply__pending,
  .cl-reply.is-attach-open .cl-reply__sheet,
  .cl-reply.is-attach-open .cl-reply__backdrop {
    animation: none;
  }

  .cl-reply__rec-dot,
  .cl-reply__rec-bars span,
  .cl-typing__dot {
    animation: none;
  }
}

/* ---------- Dark scheme for the new pieces ---------- */

[data-color-scheme="dark"] .cl-reply__input--pill,
[data-color-scheme="dark"] .cl-reply__card,
[data-color-scheme="dark"] .cl-reply__pill {
  background: var(--brand-neutral-900, #1c1c21);
  border-color: var(--color-border, #33333c);
}

[data-color-scheme="dark"] .cl-reply__input--bare,
[data-color-scheme="dark"] .cl-reply__input--bare:focus {
  background: transparent;
}

[data-color-scheme="dark"] .cl-reply__action--chip {
  background: var(--brand-neutral-800, #27272e);
  color: var(--brand-neutral-200, #d4d4dc);
}

[data-color-scheme="dark"] .cl-reply__action:hover,
[data-color-scheme="dark"] .cl-reply__mic:hover {
  background: var(--primary-950);
  color: var(--primary-300);
}

[data-color-scheme="dark"] .cl-reply__mic--primary,
[data-color-scheme="dark"] .cl-reply__mic--primary:hover {
  background: var(--primary-600, #6d28d9);
  color: #fff;
}

[data-color-scheme="dark"] .cl-reply__rec-time {
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-reply__rec-cancel:hover {
  background: var(--red-950, #450a0a);
}

[data-color-scheme="dark"] .cl-reply__pending {
  background: var(--primary-950);
}

[data-color-scheme="dark"] .cl-reply__pending-name {
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-reply__sheet {
  background: var(--brand-neutral-900, #1c1c21);
  border-color: var(--color-border, #33333c);
}

[data-color-scheme="dark"] .cl-reply__sheet-item:hover {
  background: var(--brand-neutral-800, #27272e);
}

[data-color-scheme="dark"] .cl-reply__sheet-label {
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-reply__sheet-handle {
  background: var(--brand-neutral-600, #52525f);
}

[data-color-scheme="dark"] .cl-media__caption {
  color: var(--brand-neutral-100, #e8e8ee);
}

[data-color-scheme="dark"] .cl-typing__dot {
  background: var(--brand-neutral-400, #9494a1);
}


/* ===== reaction_menu.css ===== */
/* WhatsApp-style reactions, shared by the comment panel and the chat-thread
 * family: the ReactionMenuComponent popover (quick bar + smiley-plus into the
 * full emoji picker + optional host actions) and the reaction chips rendered
 * under a comment/bubble. Behaviour lives in lib/reaction_core.js. */

/* ---------- The shared reaction context menu ---------- */

/* A Top-Layer popover positioned next to the pressed item by Floating UI. */
.gc-react-menu {
  position: fixed;
  margin: 0;
  padding: 0.375rem;
  width: max-content;
  max-width: min(20rem, calc(100vw - 1rem));
  background: var(--color-surface, oklch(1 0 0));
  border: 1px solid var(--color-border, oklch(0.9 0.01 260));
  border-radius: 1rem;
  box-shadow: 0 10px 30px oklch(0 0 0 / 0.16);
  overflow: visible;
}

.gc-react-bar {
  display: flex;
  align-items: center;
  gap: 0.125rem;
}

/* When host actions follow the bar, separate them. */
.gc-react-bar:not(:last-child) {
  padding-bottom: 0.375rem;
  border-bottom: 1px solid var(--color-border, oklch(0.92 0.01 260));
}

.gc-react-quick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.125rem;
  height: 2.125rem;
  padding: 0;
  font-size: 1.25rem;
  line-height: 1;
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  transition: transform 100ms ease, background-color 100ms ease;
}

.gc-react-quick:hover {
  background: var(--color-secondary, oklch(0.95 0.005 260));
  transform: scale(1.15);
}

.gc-react-quick.is-selected {
  background: color-mix(in oklab, var(--color-primary, oklch(0.62 0.19 265)) 15%, transparent);
}

.gc-react-more {
  border-radius: 999px;
}

/* ---------- Reaction chips under an item ---------- */

.gc-react-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin: 0.15rem 0 0;
  padding: 0;
  list-style: none;
}

.gc-react-chips:empty {
  display: none;
}

.gc-react-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.5rem;
  font: inherit;
  font-size: 0.8125rem;
  line-height: 1.3;
  color: var(--color-primary-text, inherit);
  background: var(--color-surface, oklch(0.97 0.005 260));
  border: 1px solid var(--color-border, oklch(0.9 0.01 260));
  border-radius: 999px;
  cursor: pointer;
}

.gc-react-chip.is-mine {
  border-color: var(--color-primary, oklch(0.62 0.19 265));
  background: color-mix(in oklab, var(--color-primary, oklch(0.62 0.19 265)) 12%, transparent);
}

.gc-react-chip-count {
  font-size: 0.75rem;
  color: var(--color-subtle, oklch(0.55 0.02 260));
}

.gc-react-chip.is-mine .gc-react-chip-count {
  color: inherit;
}


/* ===== image_display.css ===== */
/* ==============================
   Gallery Grid — App-Anteile
   --------------------------------------
   Die Grid-Layout-Varianten (masonry/grid/rows/list/square/mobile-1col/
   switching) liegen seit gui_components v1.149.0 im Gem
   (gallery_grid.css, Klassen .gg-grid--*). Hier bleiben nur die
   App-spezifischen Regeln rund um die .image-display-Kachel: Entrance-
   Stagger, Auswahl-Zustände, Bildinfos-Toggle und die List-Caption.
   ============================== */

/* Entrance: tiles rise in with a short stagger. Only the first dozen get an
   individual delay (roughly the above-the-fold rows); everything after
   enters together so a large gallery never feels artificially slowed down.
   Manual-sort tiles are excluded — SortableJS re-inserts nodes on reorder,
   which would replay the animation (and hide late-delay tiles) on drop. */
.gg-grid .image-display:not(.image-display--sortable) {
  animation: tile-enter 0.3s var(--ease-out-soft) backwards;
}
.gg-grid .image-display:nth-child(2)  { animation-delay: 40ms; }
.gg-grid .image-display:nth-child(3)  { animation-delay: 80ms; }
.gg-grid .image-display:nth-child(4)  { animation-delay: 120ms; }
.gg-grid .image-display:nth-child(5)  { animation-delay: 160ms; }
.gg-grid .image-display:nth-child(6)  { animation-delay: 200ms; }
.gg-grid .image-display:nth-child(7)  { animation-delay: 240ms; }
.gg-grid .image-display:nth-child(8)  { animation-delay: 280ms; }
.gg-grid .image-display:nth-child(9)  { animation-delay: 320ms; }
.gg-grid .image-display:nth-child(10) { animation-delay: 360ms; }
.gg-grid .image-display:nth-child(11) { animation-delay: 400ms; }
.gg-grid .image-display:nth-child(12) { animation-delay: 440ms; }
.gg-grid .image-display:nth-child(n + 13) { animation-delay: 480ms; }

@media (prefers-reduced-motion: reduce) {
  .gg-grid .image-display {
    animation: none;
  }
}

/* Revisits überspringen die Entrance: application.js markiert den
   eintreffenden <body> mit tile-entrance-done, wenn die Ziel-URL in dieser
   Session schon besucht wurde — die Bilder sind dann sofort aus dem Cache
   sichtbar und ein erneuter Stagger läse sich als Reload-Flackern.
   duration/delay 0s statt animation:none — ein Wechsel des animation-name
   (none → tile-enter) würde die Animation beim Entfernen der Klasse NEU
   starten (siehe .gg-grid--switching im Gem / #674); mit 0s springt sie
   sofort in den Endzustand. Die Klasse bleibt für die Lebensdauer der Seite
   gesetzt, es gibt also keinen Entfernen-Moment. */
body.tile-entrance-done .gg-grid .image-display:not(.image-display--sortable) {
  animation-duration: 0s;
  animation-delay: 0s;
}

/* Per Turbo-Stream eingefügte/ersetzte Kacheln (Upload-Broadcast: Grid-Append
   und pending→processing→completed-Replace) sollen die tile-enter-Entrance
   NICHT erneut abspielen — application.js markiert sie im
   turbo:before-stream-render mit .image-display--stream. Wie beim
   tile-entrance-done-Block nur duration/delay nullen, NIE animation-name
   wechseln (#674), damit kein Replay entsteht. Erstaufbau (kein Stream) bleibt
   unberührt und spielt die Entrance normal ab. */
.gg-grid .image-display--stream {
  animation-duration: 0s;
  animation-delay: 0s;
}

/* Layout-Wechsel (FLIP): Die Unterdrückung von Entrance/Transitions auf der
   Kachel (.gg-tile) und dem Media-Wrapper (.gg-tile__media) liegt im Gem
   (.gg-grid--switching). Hier nur das App-eigene Bild-Element, das keine
   Gem-Klasse trägt. */
.gg-grid--switching .image-display__img {
  transition: none;
}

/* Die Layout-Varianten selbst (masonry/masonry-js/grid/rows/list) liegen im
   Gem. Hier nur die App-Kopplungen an den Bildinfos-Toggle und die Caption. */

/* Reihen + Bildinfos on: keep the media at the justified row height (so widths
   still follow flex-basis ∝ --gg-ar-num and rows stay justified) and let the
   tile grow to fit the caption below it. flex-start stops a taller (wrapped)
   caption from stretching its row-mates. Desktop-only — on mobile rows
   collapse to a grid (see the Gem's mobile block), where the caption already
   fits naturally. */
@media (width >= 48rem) {
  .gg-grid--rows.images-grid--with-info {
    align-items: flex-start;
  }
  .gg-grid--rows.images-grid--with-info .image-display {
    height: auto;
  }
  .gg-grid--rows.images-grid--with-info .image-display__media {
    height: calc(var(--gg-size, 250px) * 0.72);
  }
}

/* Liste — die App-Caption (normalerweise vom Bildinfos-Toggle gesteuert)
   wird zur immer sichtbaren Metadaten-Zeile; Container/Kachel/Media kommen
   aus dem Gem (.gg-grid--list). */
.gg-grid--list .image-display__caption {
  display: flex !important;
  flex: 1 1 auto;
  gap: 4px;
  padding: 0;
}
/* Filenames in the list read as content, not code — use the app sans face
   (the grid/masonry caption keeps its monospace treatment). */
.gg-grid--list .image-display__caption-name {
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
}

/* ==============================
   View Cluster (slider + layout segmented control)
   ============================== */
.view-cluster {
  height: 48px;
  padding: 0 16px;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  background: var(--color-bg, #fff);
}
.view-cluster__divider {
  width: 1px;
  height: 24px;
  flex-shrink: 0;
  background: var(--color-border);
}
/* In the layout switcher, inactive segments collapse to icon-only;
   the active segment keeps its label. Keyed on aria-pressed, which the
   segmented-control + gallery-layout controllers both maintain. */
.view-layout-seg .segmented-control__segment[aria-pressed="false"] span {
  display: none;
}
/* Tint the track so the white active segment stands out (the default
   bg-shade is too close to white). Theme-aware accent wash over the bg. */
.view-layout-seg.segmented-control {
  background: color-mix(in srgb, var(--color-primary) 10%, var(--color-bg, #fff));
}
/* Active segment: a white fill + accent ring + accent label/icon, so the
   selection reads clearly on the tinted track (theme-aware). */
.view-layout-seg .segmented-control__segment[aria-pressed="true"] {
  background: var(--color-bg, #fff);
  color: var(--color-primary);
  box-shadow:
    0 0 0 1.5px var(--color-primary),
    0 1px 2px rgba(0, 0, 0, 0.08);
}

/* Normalize the slider so its track centers with the end icons (range
   inputs ship a default vertical margin in some browsers). */
.view-cluster__size .slider {
  display: block;
  margin: 0;
}

/* ==============================
   Image Display Component

   Outer .image-display is a flex column that holds the media tile
   (.image-display__media) and an optional caption block below.
   The aspect-ratio + overflow:hidden + position:relative all live
   on the inner media wrapper so the caption can grow the cell
   without breaking the image's aspect ratio.
   ============================== */
.image-display {
  cursor: pointer;
  border: 3px solid transparent;
  border-radius: var(--radius-2, 0.5rem);
  transition: border-color 0.2s;
}

/* Long-press on a grid tile enters mobile selection mode
   (gallery_select_controller). Suppress the iOS image callout and text/image
   selection so a hold engages selection instead of offering "Save Image".
   Scoped to grid tiles — not global — so normal long-press-to-save works
   everywhere else. */
.gg-grid .image-display {
  -webkit-touch-callout: none;
}

.gg-grid .image-display__img {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

.image-display__media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-2, 0.5rem);
  background-color: var(--color-secondary);
  transition: transform 0.25s var(--ease-spring), border-radius 0.2s ease;
}

/* Zero-radius square tiles: the radius reset itself lives in the Gem
   (.gg-grid--square). Drop the 3px transparent border so the visible gap
   between two images equals --gg-gap exactly — the border would otherwise add
   ~6px between tiles (3px per side), making the smallest gap look like ~7px
   instead of 1px. The selection ring moves to an inset outline (painted over
   the image, no layout cost) so removing the border doesn't lose the
   selected-state indicator. */
.gg-grid--square .image-display {
  border-width: 0;
}
/* Auswahl-Ring (Fokus-Variante): früher eine Outline auf __media, die in
   manchen Engines vom Bild-Layer überlagert wurde und weißlich/undeutlich
   wirkte. Jetzt als Inset-box-shadow auf dem __media-::after gezeichnet, das
   garantiert ÜBER den Bild-Layern liegt (z-index 2) — der Ring erscheint klar
   in der Theme-Primärfarbe, plus ein dezenter Primär-Tint, damit die Auswahl
   auch auf sehr hellen/bunten Motiven eindeutig bleibt. Das ::after ist auf
   gewählten Kacheln sonst ungenutzt (der Schleier greift nur auf NICHT
   gewählten Kacheln). border-radius: inherit und pointer-events: none stammen
   aus der Basisregel; layout-neutral, kein Layout-Shift. */
.gallery-item--selected .image-display__media::after {
  background: color-mix(in srgb, var(--color-primary, #4f46e5) 10%, transparent);
  box-shadow: inset 0 0 0 4px var(--color-primary, #4f46e5);
  opacity: 1;
  z-index: 2;
}

/* Beide Auswahl-Seiten nutzen gg-grid--square (border-radius: 0 auf dem
   Medienbereich). Damit der gerundete Auswahl-Rahmen die Null-Rundung
   überstimmt, wird die Rundung für gewählte Media im Square-Layout mit höherer
   Spezifität gesetzt. */
.gg-grid--square .gallery-item--selected .image-display__media {
  border-radius: var(--radius-2, 0.5rem);
}

/* Shrink beim Anwählen (das Bild schrumpft INNERHALB des Medienbereichs) liegt
   jetzt gemeinsam in selection_overlay.css. */

/* ==============================
   Select Button (checkbox circle) — surface-specific overrides only.
   Kreis-, Häkchen-, Hover- und Shrink-Basis liegen in selection_overlay.css
   (vor dieser Datei importiert); hier nur cursor + die z-index-Stufung, mit der
   die Checkbox über dem Fokus-Schleier (::after, z-index 1) liegt. */
.image-display__select {
  cursor: pointer;
  z-index: 2;
}

/* Ausgewählte Checkbox liegt eine Stufe höher, damit der bump-pop-Kreis den
   Schleier klar überlagert. */
.gallery-item--selected .image-display__select {
  z-index: 3;
}

/* ==============================
   Fokus-Variante — aktive Auswahl hebt gewählte Kacheln hervor

   Sobald mindestens eine Kachel gewählt ist, treten alle NICHT gewählten
   Kacheln dezent zurück: ein leichter Schleier in Hintergrundfarbe (hell =
   leichter Weiß-, dunkel = leichter Dunkel-Schleier). Die Fotos bleiben
   farbig und gut erkennbar; der Fokus liegt auf dem grünen Auswahl-Rahmen.
   Reines CSS über :has() — kein Markup/JS. Gilt Desktop und Mobile.
   ============================== */

/* Immer vorhandener (unsichtbarer) Schleier — so animiert das Ein-/Ausblenden.
   border-radius: inherit übernimmt die runden Ecken (Downloads-Tab) bzw. 0 (Quadrat). */
.image-display__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--color-bg, #fff) 35%, transparent);
  border-radius: inherit;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
  z-index: 1;
}

/* Bei aktiver Auswahl wird über nicht gewählten Kacheln der dezente Schleier
   sichtbar — die einzige Zurücknahme. Die Fotos bleiben farbig (keine
   Graustufen, keine reduzierte Deckkraft). */
.gg-grid:has(.gallery-item--selected)
  .image-display:not(.gallery-item--selected) .image-display__media::after {
  opacity: 1;
}

/* Während Marquee-Drag keine Übergänge — sonst ruckelt das Ein-/Ausblenden
   bei vielen Kacheln pro Frame (body.gallery-selecting setzt der Controller). */
body.gallery-selecting .image-display__img,
body.gallery-selecting .image-display__media,
body.gallery-selecting .image-display__media::after {
  transition: none;
}

/* Reduced-Motion respektieren: kein Ein-/Ausblende-Übergang des Fokus-Effekts
   und kein Shrink-Übergang — der Auswahlzustand schaltet ohne Animation um. */
@media (prefers-reduced-motion: reduce) {
  .image-display__img,
  .image-display__media,
  .image-display__media::after {
    transition: none;
  }
}

/* ==============================
   Cover Badge (top-right star)
   ============================== */
.image-display__cover-badge-wrapper {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
}

.image-display__cover-badge {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 9999px;
  background-color: var(--color-primary-600, #4f46e5);
  color: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
  cursor: help;
}

/* ==============================
   Skeleton + Local Preview (processing state)
   ============================== */

/* Base fill behind the spinner while a fresh upload converts. Formerly a
   blurred <img> of the raw original (a full ~20 MB download per tile); now
   a pure-CSS skeleton so no original-blob request is made. The sharp local
   preview (.image-display__preview) is layered on top client-side from the
   just-uploaded File via URL.createObjectURL. */
.image-display__blur {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

.image-display__skeleton {
  background: linear-gradient(
    100deg,
    var(--color-secondary) 30%,
    color-mix(in srgb, var(--color-secondary) 55%, #fff) 50%,
    var(--color-secondary) 70%
  );
  background-size: 200% 100%;
  animation: image-display-shimmer 1.4s ease-in-out infinite;
}

@keyframes image-display-shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .image-display__skeleton {
    animation: none;
  }
}

/* Local, network-free preview injected from the uploaded File. Sits above
   the skeleton but below the spinner overlay. */
.image-display__preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
}

/* Crossfade handoff (upload → converted thumbnail). Once a freshly uploaded
   tile reaches completed, the upload controller lifts the local preview above
   the LazyImage wrapper (z-index: 2) and fades it out only after the converted
   thumbnail has loaded, so the spinner never flashes between the two images. */
.image-display--handoff .image-display__preview {
  z-index: 2;
  transition: opacity 0.25s ease;
}

@media (prefers-reduced-motion: reduce) {
  .image-display--handoff .image-display__preview {
    transition: none;
  }
}

/* ==============================
   Upload Placeholder Card
   ============================== */
.image-display--placeholder {
  aspect-ratio: 1 / 1;
  pointer-events: none;
  user-select: none;
}

/* The placeholder wraps a real .image-display__media host (matching the
   server tile) so the local preview injects into the same node. The media
   fills the tile whose aspect-ratio the upload controller locks to the
   decoded photo dimensions. */
.image-display--placeholder .image-display__media {
  width: 100%;
  height: 100%;
}

/* ==============================
   Upload Progress Bar (bottom edge)
   Slim determinate bar fed the real per-file percent by the upload
   controller via the --upload-progress custom property.
   ============================== */
.image-display__upload-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background-color: rgba(0, 0, 0, 0.15);
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}

.image-display__upload-bar::before {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  background: var(--color-primary);
  transform: translateX(calc(var(--upload-progress, 0%) - 100%));
  transition: transform 0.2s linear;
}

@media (prefers-reduced-motion: reduce) {
  .image-display__upload-bar::before {
    transition: none;
  }
}

/* ==============================
   Status Chip (bottom-left pill)
   Replaces the full dark scrim for processing/uploading tiles: a compact
   white pill that stays legible on light AND dark photos. Reuses the
   download/menu button chrome for visual consistency.
   ============================== */
.image-display__status-chip {
  position: absolute;
  bottom: 8px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background-color: rgba(255, 255, 255, 0.95);
  color: var(--color-primary, #111);
  border-radius: var(--radius-2, 0.5rem);
  font-size: 0.8125rem;
  font-weight: 500;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  z-index: 2;
  pointer-events: none;
}

/* ==============================
   Status Overlay (error only)
   ============================== */
.image-display__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--size-3, 0.75rem);
  z-index: 2;
}

.image-display__overlay--error {
  background-color: var(--color-secondary);
}

/* ==============================
   Lightbox Link (wraps the image)
   ============================== */
.image-display__lightbox-link {
  display: block;
  width: 100%;
  height: 100%;
}

/* ==============================
   Completed Image
   ============================== */
.image-display__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.2s ease, filter 0.18s ease, opacity 0.18s ease;
}

/* Hover-Zoom nur für nicht gewählte Kacheln — bei gewählten Kacheln bleibt der
   Shrink (scale 0.9) auf dem Bild bestehen, statt gegen den Hover-Zoom zu
   kämpfen. */
@media (hover: hover) and (pointer: fine) {
  .image-display:not(.gallery-item--selected):hover .image-display__img {
    transform: scale(1.03);
  }
}

/* ==============================
   Menu Button (bottom-right three-dot)
   ============================== */
.image-display__menu {
  position: absolute;
  bottom: 8px;
  right: 8px;
  opacity: 0;
  transition: opacity 0.2s;
  z-index: 2;
}

.image-display:hover .image-display__menu,
.image-display__menu:has(.popover:popover-open) {
  opacity: 1;
}

.image-display__menu-btn {
  background-color: rgba(255, 255, 255, 0.95);
  color: var(--color-primary, #111);
  border-radius: var(--radius-2, 0.5rem);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  padding: 6px;
}

.image-display__menu-btn:hover {
  background-color: #fff;
}

/* ==============================
   Download Button (bottom-left pill)
   ============================== */
.image-display__download {
  position: absolute;
  bottom: 8px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background-color: rgba(255, 255, 255, 0.95);
  color: var(--color-primary, #111);
  border-radius: var(--radius-2, 0.5rem);
  font-size: 0.8125rem;
  font-weight: 500;
  text-decoration: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  opacity: 0;
  transition: opacity 0.2s, transform 0.15s, background-color 0.15s;
  z-index: 2;
}

.image-display:hover .image-display__download {
  opacity: 1;
}

.image-display__download:hover {
  background-color: #fff;
}

@media (hover: hover) and (pointer: fine) {
  .image-display__download:hover {
    transform: scale(1.03);
  }
}

/* ==============================
   Gallery Membership Chips (top-right stack)
   Pinned to the top-right so they never overlay the select checkbox
   (top-left), the download button (bottom-left), or the menu
   (bottom-right). Stack vertically when the image is in multiple
   galleries; pushed down when the cover-badge star occupies the
   top-right corner.
   ============================== */
.image-display__gallery-chips {
  position: absolute;
  top: 8px;
  right: 8px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  max-width: calc(100% - 56px);
  z-index: 2;
  pointer-events: none;
}

.image-display__cover-badge-wrapper ~ .image-display__gallery-chips {
  top: 44px;
}

.image-display__gallery-chip {
  pointer-events: auto;
}

/* ==============================
   Marquee Selection Rectangle
   ============================== */
.gallery-select-rectangle {
  position: absolute;
  border: 2px solid var(--color-accent, #0078d4);
  background-color: rgba(0, 120, 212, 0.1);
  pointer-events: none;
  z-index: 999;
  display: none;
}

/* Prevent text selection during marquee drag */
body.gallery-selecting {
  user-select: none;
  -webkit-user-select: none;
}

/* Responsive */
@media (width < 48rem) {
  /* Hide the desktop page-sub-header in gallery views — the mobile header
     already carries title, status, and actions via set_gallery_mobile_header. */
  #page-sub-header:has(.gallery-page-header),
  #page-sub-header:has(.shooting-page-header) {
    display: none;
  }

  /* Der mobile Layout-Kollaps (masonry/grid/rows → 2-Spalten-Grid) und der
     gg-grid--mobile-1col-Modifier liegen im Gem (gallery_grid.css). */

  /* Hide checkbox on mobile (shown only in fullscreen selection mode) */
  .image-display__select {
    display: none;
  }

  /* ==============================
     Fullscreen selection mode
     ============================== */

  /* Hide layout chrome */
  body.gallery-fullscreen-select #header,
  body.gallery-fullscreen-select #page-sub-header {
    display: none !important;
  }

  body.gallery-fullscreen-select > nav[aria-label="Main navigation"] {
    display: none !important;
  }

  body.gallery-fullscreen-select .photo-shooting-tabs {
    display: none !important;
  }

  body.gallery-fullscreen-select #gallery-sort-header,
  body.gallery-fullscreen-select #mobile-select-bar,
  body.gallery-fullscreen-select #mobile-tool-row,
  body.gallery-fullscreen-select .images-upload-fab,
  body.gallery-fullscreen-select #cover-banner {
    display: none !important;
  }

  /* Slim "Alle Bilder auswählen" bar above the grid — selection mode only */
  #mobile-select-all-bar {
    display: none;
  }

  body.gallery-fullscreen-select #mobile-select-all-bar {
    display: block !important;
    padding: 0.5rem 1rem;
    background-color: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
  }

  /* Remove main padding for edge-to-edge grid */
  body.gallery-fullscreen-select #main {
    padding: 0;
  }

  /* Slide-up the entire main area */
  body.gallery-fullscreen-select #main {
    animation: selectionSlideUp 0.35s cubic-bezier(0.2, 0.9, 0.3, 1) both;
  }

  /* 3-column square grid. minmax(0, 1fr) statt 1fr: mit eingeschalteten
     Bildinfos-Captions wächst ein 1fr-Track sonst auf die min-content-Breite
     der Dateinamen und das Grid läuft horizontal über (scrollWidth 477px bei
     390px Viewport, dritte Spalte abgeschnitten). */
  body.gallery-fullscreen-select .gg-grid {
    display: grid;
    column-width: auto;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1px;
    margin-top: 0;
    /* Clear the non-modal action sheet fixed at the bottom so the last row can
       scroll above it and stay tappable. The sheet's live height is published
       as --floating-action-bar-height by the gallery-select controller, so the
       padding tracks the sheet whether it shows two or four labelled actions
       (offsetHeight already includes the bar's safe-area padding). */
    padding-bottom: calc(var(--floating-action-bar-height, 6rem) + 1rem);
  }

  body.gallery-fullscreen-select .image-display {
    border-radius: 0;
    /* Anchor the .gallery-item--selected::after selection overlay to the tile.
       Without a positioning context its inset:0 escapes to #main (which keeps a
       transform from selectionSlideUp), so every selected tile paints a
       full-width blue layer and they stack into an ever-darker wash. */
    position: relative;
  }

  body.gallery-fullscreen-select .image-display__media {
    aspect-ratio: 1 / 1 !important;
    border-radius: 0;
  }

  /* Auswahl-Rahmen (auf __media) auch mobil runden — höhere Spezifität
     überstimmt border-radius:0 des Medienbereichs. */
  body.gallery-fullscreen-select .gallery-item--selected .image-display__media {
    border-radius: var(--radius-1, 0.375rem);
  }

  /* Shrink im engen 3-Spalten-Grid (1px-Gaps) dezenter halten und das Bild
     runden — das Bild schrumpft innerhalb des Medienbereichs, der Rahmen bleibt
     an der Bildkante. */
  body.gallery-fullscreen-select .gallery-item--selected .image-display__img {
    transform: scale(0.92);
    border-radius: var(--radius-1, 0.375rem);
  }

  /* Show checkbox only on selected items */
  body.gallery-fullscreen-select .gallery-item--selected .image-display__select {
    display: block !important;
    opacity: 1;
    top: 4px;
    left: 4px;
    width: 20px;
    height: 20px;
  }

  body.gallery-fullscreen-select .gallery-item--selected .image-display__select-check {
    width: 11px;
    height: 11px;
  }

  /* Hide cover badge in selection mode so the checkbox is unambiguous */
  body.gallery-fullscreen-select .image-display__cover-badge-wrapper {
    display: none;
  }

  /* Selection mode: taps only toggle selection — never reveal the
     hover-emulated download pill or three-dot menu (covers tiles that
     were re-rendered via Turbo Streams without mobile_compact). */
  body.gallery-fullscreen-select .image-display__download,
  body.gallery-fullscreen-select .image-display__menu {
    display: none !important;
  }

  /* Markentint auf ausgewählten Kacheln (Fokus-Variante, mobil). Liegt auf dem
     geschrumpften, gerundeten Media-Element (::before) statt der vollen eckigen
     Kachel — so bleibt der Tint deckungsgleich mit dem Bild (border-radius:
     inherit) und füllt nicht die ganze Kachel. Der Marquee-::after-Schleier
     bleibt frei für nicht gewählte Kacheln. */
  body.gallery-fullscreen-select .gallery-item--selected .image-display__media::before {
    content: '';
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--color-primary, #4f46e5) 15%, transparent);
    border-radius: inherit;
    pointer-events: none;
    z-index: 1;
  }

  /* Selection mode header bar */
  .gallery-select-header {
    display: none;
  }

  body.gallery-fullscreen-select .gallery-select-header {
    display: flex !important;
    position: sticky;
    top: 0;
    z-index: 20;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    padding-top: max(0.75rem, env(safe-area-inset-top));
    background-color: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
  }
}

/* Smaller gallery-membership badges on mobile — the photo wins, the label
   stays readable (design handoff: Mobile Optimierung, screen 3). */
@media (width < 48rem) {
  .image-display__gallery-chip {
    font-size: 0.625rem;
    padding: 1px 0.375rem;
    max-width: 7rem;
  }
}


/* ===== picker_tiles.css ===== */
/* ==============================
   Picker-Tile-Auswahl — kanonischer Stil

   Spiegelt das app-weite Auswahl-Design (image_display.css, gallery_card.css)
   auf das Klassen-Contract der Dialog-Picker (.tile / .tile-img / .check-badge /
   .is-selected): 4px-Inset-Ring in var(--color-primary), 10 % Primär-Tint,
   Bild-Shrink auf scale(0.9) und ein Checkmark-Kreis oben links, dessen Haken
   sich per stroke-dashoffset nachzeichnet.

   Liegt app-seitig (nicht im Gem), weil bump-pop und die --ease-*-Tokens in der
   App-animations.css leben. Der alte Gem-Stil (Outline + Badge unten rechts)
   wurde aus dialog.css entfernt, damit hier nichts dagegen kaskadiert.
   ============================== */

/* Immer vorhandenes Overlay auf der Kachel — so animiert der Ring ein/aus,
   ohne Layout-Shift. border-radius: inherit übernimmt die runden Ecken der
   .tile-img (rounded-xl). */
.tile .tile-img::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
  box-shadow: inset 0 0 0 4px var(--color-primary);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
  z-index: 2;
}

.tile.is-selected .tile-img::after {
  opacity: 1;
}

/* Bild zieht sich leicht rein, sodass zwischen Ring und Foto umlaufend Luft
   entsteht. transform ist layout-neutral — kein Reflow der Nachbarkacheln.
   LazyImage packt das img in einen eigenen Wrapper, daher greift der
   Descendant-Selektor .tile-img img (nicht > img). */
.tile .tile-img img {
  transition: transform 0.18s ease;
}

/* Der Shrink beim Anwählen (scale(0.9) auf .tile-img img) liegt gemeinsam in
   selection_overlay.css. */

/* ==============================
   Checkmark-Kreis (oben links) — surface-specific overrides only.
   Kreis-, Häkchen-, Hover- und Selected-Basis liegen in selection_overlay.css
   (vor dieser Datei importiert). Der Picker zentriert den Haken über das
   eigene Grid des Kreises (nicht per absolute/inset wie die Komponenten) und
   stapelt ihn auf z-index 3.
   ============================== */
.check-badge {
  display: grid;
  place-items: center;
  z-index: 3;
}


/* ===== image_info_caption.css ===== */
/* ==============================
   Image Info — Caption + Toggle Pill
   ==============================
   Caption row revealed by the "Bildinfos" toggle in the toolbar.
   Sits below the image media area; the i-icon button on the right
   opens the image-info-drawer (same backend as the lightbox info
   panel, see app/controllers/images_controller.rb#meta).
   The grid container gets the .images-grid--with-info modifier
   when the toggle is on, which reveals the captions.
*/

.image-display__caption {
  display: none;
  flex-direction: column;
  gap: 2px;
  padding: 8px 4px 0;
  font-size: 0.8125rem;
  color: var(--color-text);
  line-height: 1.35;
  min-width: 0;
}

.images-grid--with-info .image-display__caption {
  display: flex;
}

.image-display__caption-top {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.image-display__caption-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  font-family: inherit;
  font-size: 0.8125rem;
}

.image-display__caption-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--color-text-subtle, #6b7280);
  font-size: 0.75rem;
  flex-wrap: wrap;
}

.image-display__caption-sep {
  color: var(--color-border, #d1d5db);
}

.image-display__info-button {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 9999px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-subtle, #6b7280);
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.image-display__info-button:hover {
  background-color: var(--color-shade, rgba(0, 0, 0, 0.04));
  color: var(--color-text, #111827);
  border-color: var(--color-border, #d1d5db);
}

/* ==============================
   Bildinfos Toggle Pill (toolbar)
   ============================== */
.bildinfos-toggle-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.625rem 0.375rem 0.75rem;
  border-radius: 9999px;
  border: 1px solid var(--color-border, #d1d5db);
  background: var(--color-bg, #ffffff);
  color: var(--color-text-subtle, #4b5563);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.bildinfos-toggle-pill:hover {
  background-color: var(--color-shade, rgba(0, 0, 0, 0.04));
}

.bildinfos-toggle-pill[aria-pressed="true"] {
  background-color: color-mix(in srgb, var(--primary-600, #4f46e5) 8%, transparent);
  border-color: color-mix(in srgb, var(--primary-600, #4f46e5) 40%, transparent);
  color: var(--color-primary-700, #4338ca);
}

/* Row variant used inside the mobile filter sheet: full-width plain row
   instead of the bordered toolbar pill; the switch styling is shared. */
.bildinfos-toggle-pill.bildinfos-toggle-pill--row,
.bildinfos-toggle-pill.bildinfos-toggle-pill--row:hover,
.bildinfos-toggle-pill.bildinfos-toggle-pill--row[aria-pressed="true"] {
  display: flex;
  width: 100%;
  border: none;
  background: transparent;
  border-radius: 0;
  padding: 0.75rem 0.125rem;
  gap: 0.75rem;
}

.bildinfos-toggle-pill__switch {
  position: relative;
  width: 28px;
  height: 16px;
  border-radius: 9999px;
  background-color: var(--color-border, #d1d5db);
  transition: background-color 0.2s;
  flex-shrink: 0;
}

.bildinfos-toggle-pill[aria-pressed="true"] .bildinfos-toggle-pill__switch {
  background-color: var(--color-primary-600, #4f46e5);
}

.bildinfos-toggle-pill__switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 9999px;
  background-color: #ffffff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: transform 0.2s;
}

.bildinfos-toggle-pill[aria-pressed="true"] .bildinfos-toggle-pill__switch::after {
  transform: translateX(12px);
}

/* Row variant (mobile filter sheet): larger, thumb-friendly toggle. The
   bordered desktop pill keeps the compact 28x16 switch. */
.bildinfos-toggle-pill--row .bildinfos-toggle-pill__switch {
  width: 46px;
  height: 28px;
}

.bildinfos-toggle-pill--row .bildinfos-toggle-pill__switch::after {
  width: 24px;
  height: 24px;
}

.bildinfos-toggle-pill--row[aria-pressed="true"] .bildinfos-toggle-pill__switch::after {
  transform: translateX(18px);
}

/* ==============================
   Page-level Info Drawer
   ==============================
   The base .cl-info-panel rules are reused from photo_shooting_comments.css
   (originally written for the PhotoSwipe lightbox). For a page-level
   drawer we need position:fixed instead of absolute so the panel
   slides in from the viewport edge and spans the full viewport height.
   Doppelklasse-Selector, weil photo_shooting_comments.css später im
   Manifest importiert wird und die Basis-Regeln sonst gewinnen.
*/
.cl-info-panel.cl-info-panel--page {
  position: fixed;
  inset-block-start: 0;
  inset-inline-end: 0;
  block-size: 100vh;
  block-size: 100dvh;
  max-block-size: 100vh;
  max-block-size: 100dvh;
  z-index: 100;
}

.cl-info-panel.cl-info-panel--page[aria-hidden="true"] {
  pointer-events: none;
}

.cl-info-panel.cl-info-panel--page.cl-info-panel--open {
  pointer-events: auto;
}



/* ===== gallery_manual_sort.css ===== */
/* ==============================================================
   Gallery Manual Sort
   --------------------------------------------------------------
   Visual layer for the gallery-manual-sort Stimulus controller:
   the sort banner (active + dirty), per-tile chrome (position
   pill, drag handle, multi-drag stack), and the SortableJS state
   classes. The selection action bar is the shared FloatingAction
   BarComponent, fed via dataset.floatingActionBarCountValue.
   All transitions are 120–220ms ease-out per the design spec.
   ============================================================== */

/* ───── Compact Sort Grid ─────────────────────────────────────────
   The reorder view uses small, uniform tiles so a whole gallery is
   sortable at a glance — independent of the size/gap slider values the
   normal view persists to localStorage. Fixed narrow columns, a fixed
   gap and a 4:3 crop (analogous to the gem's gg-grid--grid). The
   images-grid--sort modifier is app-owned and sits on the gem's .gg-grid
   container; the compound selector keeps it ahead of gem layout rules. */
.gg-grid.images-grid--sort {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
  align-items: start;
  column-width: auto;
}

.images-grid--sort .image-display {
  width: 100%;
  margin-bottom: 0;
}

.images-grid--sort .image-display__media {
  aspect-ratio: 4 / 3 !important;
}

/* Scale the position pill and select circle down to fit the smaller tile. */
.images-grid--sort .image-display__position-pill {
  top: 6px;
  left: 6px;
  min-width: 22px;
  height: 20px;
  padding: 0 6px;
  font-size: 0.6875rem;
}

.images-grid--sort .image-display--sortable .image-display__select {
  right: 6px;
}

/* ───── Sort Banner ───────────────────────────────────────────── */

.gallery-sort-banner-wrap {
  display: contents;
}

.gallery-sort-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.875rem 1rem 0.875rem 0.875rem;
  border-radius: 0.75rem;
  border: 1px solid;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  margin-bottom: 1rem;
  transition: background-color 180ms ease-out, border-color 180ms ease-out;
}

.gallery-sort-banner__lead {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}

.gallery-sort-banner__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 0.5rem;
  flex-shrink: 0;
}

.gallery-sort-banner__copy {
  min-width: 0;
}

.gallery-sort-banner__title {
  font-size: 0.875rem;
  font-weight: 600;
  margin: 0;
  line-height: 1.25;
}

.gallery-sort-banner__description {
  font-size: 0.8125rem;
  margin: 2px 0 0;
  line-height: 1.35;
  color: var(--color-text-subtle, #64748b);
}

.gallery-sort-banner__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.gallery-sort-banner__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.4375rem 0.875rem;
  border-radius: 0.5rem;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.2;
  border: 1px solid transparent;
  background: white;
  cursor: pointer;
  transition: background-color 150ms ease-out, border-color 150ms ease-out, color 150ms ease-out;
}

.gallery-sort-banner__btn--ghost {
  background: white;
  border-color: var(--color-border, #e2e8f0);
  color: var(--color-text, #0f172a);
}

.gallery-sort-banner__btn--ghost:hover {
  background: var(--color-secondary, #f8fafc);
}

.gallery-sort-banner__btn--primary {
  background: var(--primary-600, #2563eb);
  border-color: color-mix(in srgb, var(--primary-400, #60a5fa) 30%, transparent);
  color: white;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.gallery-sort-banner__btn--primary:hover {
  background: var(--primary-500, #1d4ed8);
}

.gallery-sort-banner__btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Active variant (mode entered, no changes yet) — calm blue. */
.gallery-sort-banner--active {
  background: #eff6ff;
  border-color: #dbeafe;
}
.gallery-sort-banner--active .gallery-sort-banner__icon {
  background: #dbeafe;
  color: #1d4ed8;
}
.gallery-sort-banner--active .gallery-sort-banner__title {
  color: #1e3a8a;
}

/* Dirty variant — warning amber, drives the user toward Save / Discard. */
.gallery-sort-banner--dirty {
  background: #fff7ed;
  border-color: #fed7aa;
}
.gallery-sort-banner--dirty .gallery-sort-banner__icon {
  background: #ffedd5;
  color: #c2410c;
}
.gallery-sort-banner--dirty .gallery-sort-banner__title {
  color: #7c2d12;
}
.gallery-sort-banner--dirty .gallery-sort-banner__description {
  color: #9a3412;
}

/* ───── Per-Tile Chrome (manual-sort mode only) ──────────────── */

.image-display--sortable {
  position: relative;
  cursor: grab;
}

.image-display--sortable:active,
.image-display--sortable.image-display--chosen {
  cursor: grabbing;
}

/* Kill the browser's native image drag-out (ghost preview, "drag this
   image to your desktop") so the only drag gesture inside the grid is
   the SortableJS one anchored to the explicit handle. */
.image-display--sortable img,
.image-display--sortable a {
  -webkit-user-drag: none;
  user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}

/* The lightbox <a> wraps the entire image. In manual-sort mode it must
   stop intercepting clicks (otherwise PhotoSwipe would open instead of
   selection happening). pointer-events on children are restored where
   we still need them — the drag handle and select circle. */
.image-display--sortable .image-display__lightbox-link {
  pointer-events: none;
}

/* In manual-sort mode the gallery-select checkbox slot moves to the top
   right so the position pill can take the top left without overlap. */
.image-display--sortable .image-display__select {
  left: auto;
  right: 8px;
  /* Always interactive, the lightbox-link rule above set pointer-events
     to none on a wider scope so we explicitly re-enable here. */
  pointer-events: auto;
}

/* Suppress hover-only chrome that we don't render anyway in manual mode
   — the inline overlay sometimes survives via .image-display__menu when
   the component is reused; defence in depth. */
.image-display--sortable .image-display__menu,
.image-display--sortable .image-display__download,
.image-display--sortable .image-display__cover-badge-wrapper {
  display: none !important;
}

.image-display__position-pill {
  position: absolute;
  top: 8px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 24px;
  padding: 0 8px;
  background-color: rgba(15, 23, 42, 0.78);
  color: white;
  border-radius: 6px;
  font-size: 0.75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  z-index: 3;
  pointer-events: none;
  user-select: none;
}

.image-display--sortable .image-display__drag-handle {
  display: none;
}

.image-display--sortable:hover .image-display__media {
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
}

/* Selection state — blue ring around tile in manual mode. */
.image-display--selected .image-display__media {
  box-shadow: 0 0 0 3px #2563eb;
}
.image-display--selected .image-display__select {
  background: #2563eb !important;
  border-color: #2563eb !important;
  opacity: 1;
}
.image-display--selected .image-display__select::after {
  content: "✓";
  color: white;
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
}

/* ───── SortableJS State Classes ─────────────────────────────── */

/* Origin tile while a drag is in progress: stays in flow, fades out. */
.image-display--ghost {
  opacity: 0.35;
  transition: opacity 120ms ease-out;
}

/* The chosen tile (mousedown but not yet moving) — subtle scale-up. */
.image-display--chosen .image-display__media {
  transform: scale(1.02);
  transition: transform 120ms ease-out;
}

/* The fallback element — the floating preview that follows the cursor. */
.image-display--fallback {
  cursor: grabbing !important;
  transform: rotate(2deg);
  box-shadow: 0 24px 48px rgba(15, 23, 42, 0.28), 0 0 0 4px rgba(37, 99, 235, 0.6);
  border-radius: 12px;
  z-index: 9999;
  pointer-events: none;
  opacity: 1 !important;
  transition: none;
}

/* Multi-drag stacked-card affordance attached to the fallback element. */
.image-display__multi-stack {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.image-display__multi-stack-card {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 4px 12px rgba(15, 23, 42, 0.18);
  z-index: -1;
}

.image-display__multi-stack-card--mid {
  transform: translate(8px, 8px) rotate(3deg);
  z-index: -2;
}

.image-display__multi-stack-card--back {
  transform: translate(16px, 16px) rotate(6deg);
  z-index: -3;
  opacity: 0.85;
}

.image-display__multi-stack-badge {
  position: absolute;
  top: -10px;
  right: -10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 28px;
  padding: 0 8px;
  background: #2563eb;
  color: white;
  border: 2px solid white;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 700;
  box-shadow: 0 4px 10px rgba(37, 99, 235, 0.45);
  z-index: 10;
}

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

@media (prefers-reduced-motion: reduce) {
  .gallery-sort-banner,
  .image-display__drag-handle,
  .image-display--ghost,
  .image-display--chosen .image-display__media,
  .image-display--fallback {
    transition: none !important;
    transform: none;
  }
}


/* ===== lightbox.css ===== */
/* Lightbox Component */

/* PhotoSwipe caption overlay */
.pswp__caption {
  text-align: center;
  padding: 12px 16px;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.8);
}

@media (prefers-reduced-motion: reduce) {
  [data-controller="lightbox"] img {
    transition-duration: 0.01ms !important;
  }
}

/* All toolbar icons are Heroicons outlines with stroke="currentColor",
   so force them to white to stay visible against PhotoSwipe's dark
   backdrop. */
.pswp__button--download-button,
.pswp__button--info-button,
.pswp__button--close,
.pswp__button--zoom {
  color: #fff;
}

/* Every toolbar button now uses a Heroicons outline SVG (download +
   info from custom registration, close/zoom via closeSVG/zoomSVG
   options). Size and position them identically so they share a
   single optical baseline. 22×22 inside a 50×60 button centers them
   via left:14 / top:19. */
.pswp__button--download-button > svg,
.pswp__button--info-button > svg,
.pswp__button--close > svg,
.pswp__button--zoom > svg {
  position: absolute !important;
  left: 14px !important;
  top: 19px !important;
  width: 22px !important;
  height: 22px !important;
  fill: none !important;
}

/* Tight grouping for the action cluster (download / info / zoom),
   with a small extra gap separating the close button so it reads as
   a distinct affordance. */
.pswp .pswp__top-bar {
  gap: 0;
}

.pswp .pswp__top-bar .pswp__button--close {
  margin-inline-start: 6px;
}

/* Image info panel triggered by the info-button toolbar element.
   Slides in from the right inside the PhotoSwipe wrapper. The
   `.pswp` prefix bumps specificity above PhotoSwipe's default
   `.pswp *` transition which would otherwise fight our transform. */
.pswp .pswp__info-panel {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  block-size: 100%;
  inline-size: min(420px, 90%);
  background: #ffffff;
  color: #111827;
  box-shadow: -16px 0 48px rgba(0, 0, 0, 0.45);
  transform: translateX(100%);
  transition: transform 350ms var(--ease-drawer) !important;
  z-index: 1600 !important;
  overflow-y: auto;
  padding: 0;
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}

.pswp .pswp__info-panel--open {
  transform: translateX(0);
}

.pswp__info-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.875rem 1.125rem 0.75rem;
  border-bottom: 1px solid rgba(17, 24, 39, 0.08);
  position: sticky;
  top: 0;
  background: #ffffff;
  z-index: 1;
}

.pswp__info-panel__title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  color: #111827;
  letter-spacing: -0.01em;
}

.pswp__info-panel__close {
  position: static;
  width: 2.25rem;
  height: 2.25rem;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: #4b5563;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 150ms ease-out, color 150ms ease-out;
  flex-shrink: 0;
}

.pswp__info-panel__close:hover {
  background: rgba(17, 24, 39, 0.06);
  color: #111827;
}

.pswp__info-panel__body {
  padding: 0.875rem 1.125rem;
}

.pswp__info-panel__section {
  margin-bottom: 1.125rem;
}

.pswp__info-panel__section:last-child {
  margin-bottom: 0;
}

.pswp__info-panel__heading {
  margin: 0 0 0.4375rem;
  font-size: 0.8125rem;
  font-weight: 700;
  color: #111827;
  letter-spacing: -0.005em;
}

.pswp__info-panel__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.3rem 0;
  font-size: 0.8125rem;
}

.pswp__info-panel__label {
  color: #4b5563;
  font-weight: 400;
  flex-shrink: 0;
}

.pswp__info-panel__value {
  color: #111827;
  font-weight: 400;
  text-align: right;
  word-break: break-word;
  min-width: 0;
}

.pswp__info-panel__loading,
.pswp__info-panel__empty,
.pswp__info-panel__error {
  color: #6b7280;
  font-size: 0.8125rem;
  padding: 0.5rem 0;
}

/* Consumer-defined lightbox panels (see LightboxComponent `panels:`). The gem
   owns the chrome: a right-hand side panel on desktop, a bottom sheet on the
   mobile breakpoint. The shell is relocated into the PhotoSwipe root by the
   controller, so it is positioned against that fixed, full-screen overlay. It
   stays off-screen (and non-interactive) until `--open` is added, giving a
   symmetric slide-in / slide-out either way. */
.lightbox-panel {
  position: absolute;
  background: #ffffff;
  color: #111827;
  z-index: 1600;
  overflow-y: auto;
  pointer-events: none;
  transition: transform 350ms var(--ease-drawer) !important;
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}

.lightbox-panel--open {
  pointer-events: auto;
}

.lightbox-panel--side {
  inset-block-start: 0;
  inset-inline-end: 0;
  block-size: 100%;
  inline-size: min(420px, 90%);
  box-shadow: -16px 0 48px rgba(0, 0, 0, 0.45);
  transform: translateX(100%);
}

.lightbox-panel--side.lightbox-panel--open {
  transform: translateX(0);
}

.lightbox-panel--sheet {
  inset-inline: 0;
  inset-block-end: 0;
  max-block-size: 80%;
  border-start-start-radius: 16px;
  border-start-end-radius: 16px;
  box-shadow: 0 -16px 48px rgba(0, 0, 0, 0.45);
  transform: translateY(100%);
}

.lightbox-panel--sheet.lightbox-panel--open {
  transform: translateY(0);
}

.lightbox-panel__body {
  padding: 0.875rem 1.125rem;
}

@media (prefers-reduced-motion: reduce) {
  .lightbox-panel {
    transition-duration: 0.01ms !important;
  }
}


/* ===== pages/user_profile.css ===== */
.user-profile-header {
  background:
    radial-gradient(ellipse at 20% 50%, oklch(0.85 min(0.08, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 20%, oklch(0.90 min(0.06, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    radial-gradient(ellipse at 50% 80%, oklch(0.80 min(0.10, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    linear-gradient(135deg, var(--primary-50) 0%, var(--primary-100) 100%);
}

[data-color-scheme="dark"] .user-profile-header {
  background:
    radial-gradient(ellipse at 20% 50%, oklch(0.25 min(0.06, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 20%, oklch(0.20 min(0.04, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    radial-gradient(ellipse at 50% 80%, oklch(0.30 min(0.08, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    linear-gradient(135deg, var(--primary-950) 0%, var(--primary-900) 100%);
}

.session-header {
  background:
    radial-gradient(ellipse at 20% 50%, oklch(0.95 min(0.02, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 20%, oklch(0.97 min(0.015, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    linear-gradient(135deg, var(--primary-50) 0%, white 100%);
}

[data-color-scheme="dark"] .session-header {
  background:
    radial-gradient(ellipse at 20% 50%, oklch(0.22 min(0.02, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    radial-gradient(ellipse at 80% 20%, oklch(0.18 min(0.015, var(--primary-chroma)) var(--primary-hue)) 0%, transparent 50%),
    linear-gradient(135deg, var(--primary-950) 0%, var(--neutral-900) 100%);
}


/* ===== pages/photo_shooting_comments.css ===== */
/* ============================================================
 * Photo-shooting comment views (photographer-facing)
 * App-local leftovers: the likes-page row list (.cl-row), the
 * ps-* grid/chat views and the comments lightbox dialog.
 * The conversation-card family (.cl-conv / .cl-msg / .cl-reply /
 * .cl-chip / .cl-list-toolbar / .cl-page) moved to gui_components
 * (chat_thread.css, imported via application.css).
 * ============================================================ */

/* ---------- Row ---------- */

.cl-row {
  display: grid;
  grid-template-columns: 200px 1fr;
  align-items: start;
  gap: 1.25rem;
  padding: 0.85rem 0;
  border-radius: 12px;
  text-decoration: none;
  color: inherit;
  transition: background 150ms ease;
}

.cl-row:hover {
  background: transparent;
}

.cl-row:focus-visible {
  outline: 2px solid var(--primary-400);
  outline-offset: 4px;
  border-radius: 12px;
}

/* After the lightbox closes, focus returns to the clicked row. Safari
 * leaves a :focus ring visible even for mouse-triggered focus; suppress
 * it so a closed dialog doesn't leave a rectangle around the list. */
.cl-row:focus:not(:focus-visible) {
  outline: none;
}

/* Thumbnail ---------------------------------------------------------- */

.cl-row__thumb {
  position: relative;
  width: 200px;
  aspect-ratio: 3 / 2;
  border-radius: 12px;
  overflow: hidden;
  flex-shrink: 0;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
}

.cl-row__thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cl-row__thumb-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.72rem;
  font-weight: 600;
  color: rgba(0, 0, 0, 0.55);
}

.cl-row__thumb-count {
  position: absolute;
  top: 8px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.6rem 0.15rem 0.5rem;
  border-radius: 999px;
  background: rgba(15, 15, 16, 0.78);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
}

.cl-row__thumb-count svg {
  width: 13px;
  height: 13px;
}

.cl-row__unread-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  background: var(--red-500, #dc2626);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* Body (heading + recent comments) ----------------------------------- */

.cl-row__body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding-top: 0.15rem;
}

/* Heading row: filename — avatar stack — time */
.cl-row__heading {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
}

.cl-row__filename {
  font-size: 1.05rem;
  font-weight: 700;
  color: #18181b;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1 1 auto;
}

.cl-row__avatars {
  display: inline-flex;
  flex-shrink: 0;
}

.cl-row__avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  margin-left: -8px;
}

.cl-row__avatar:first-child { margin-left: 0; }

.cl-row__time {
  font-size: 0.78rem;
  color: #a1a1aa;
  white-space: nowrap;
  flex-shrink: 0;
}

/* Recent comments list (up to 3) */
.cl-row__recents {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.cl-row__recent {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.cl-row__author-chip {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.62rem;
  font-weight: 700;
  flex-shrink: 0;
}

.cl-row__author {
  font-size: 0.85rem;
  font-weight: 600;
  color: #18181b;
  white-space: nowrap;
  flex-shrink: 0;
}

.cl-row__preview {
  font-size: 0.85rem;
  color: #71717a;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* ---------- Mobile card list ----------
 *
 * Below 640px each row turns into a vertical card: full-width thumbnail
 * on top, filename + preview underneath. (The toolbar/chip mobile rules
 * live in the gem's chat_thread.css.)
 */
@media (width < 40rem) {
  /* Vertical card — thumbnail full-width, body below */
  .cl-row {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: 0;
    width: 100%;
    box-sizing: border-box;
    min-width: 0;
  }

  .cl-row:hover {
    background: transparent;
  }

  .cl-row__thumb {
    width: 100%;
    aspect-ratio: 3 / 2;
    border-radius: 14px;
    box-shadow: 0 4px 14px -6px rgba(0, 0, 0, 0.18);
  }

  .cl-row__thumb-count {
    top: 10px;
    left: 10px;
    padding: 0.22rem 0.7rem 0.22rem 0.55rem;
    font-size: 0.82rem;
  }

  .cl-row__thumb-count svg { width: 14px; height: 14px; }

  .cl-row__unread-badge {
    top: 10px;
    right: 10px;
    padding: 0.22rem 0.75rem;
    font-size: 0.76rem;
  }

  .cl-row__body {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    padding: 0;
    gap: 0.5rem;
    overflow: hidden;
  }

  .cl-row__time {
    display: none;
  }

  .cl-row__heading {
    gap: 0.55rem;
    min-width: 0;
    flex-wrap: nowrap;
  }

  .cl-row__filename {
    flex: 1 1 0;
    min-width: 0;
    font-size: 1rem;
    line-height: 1.3;
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  .cl-row__avatars {
    display: inline-flex;
    flex-shrink: 0;
  }

  .cl-row__avatar {
    width: 22px;
    height: 22px;
    font-size: 0.62rem;
    margin-left: -6px;
  }

  .cl-row__author-chip {
    width: 20px;
    height: 20px;
    font-size: 0.6rem;
  }

  /* On mobile show only the most recent comment inline; the others
   * are one tap away in the detail view. */
  .cl-row__recents {
    gap: 0.25rem;
  }
  .cl-row__recent:nth-child(n+2) {
    display: none;
  }
  .cl-row__recent {
    gap: 0.5rem;
    min-width: 0;
    max-width: 100%;
  }

  .cl-row__author {
    font-size: 0.85rem;
    flex-shrink: 0;
  }

  .cl-row__preview {
    font-size: 0.85rem;
    min-width: 0;
    flex: 1 1 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

}


/* ============================================================
 * Comments panel (cl-panel*)
 * The side panel shell shared by the comments drill-down and the
 * lightbox panels. The former hand-rolled <dialog> viewer (cl-dialog,
 * cl-image-pane, cl-topbar, cl-nav-btn) is gone — every tab now uses
 * PhotoSwipe.
 * ============================================================ */

/* ---------- Panel internals (shared by the comments drill-down and the
 * Likes viewer panel; the hosting container is provided by each caller) ---------- */

/* The chat-card host div inside the frame (header + scroll + composer)
 * needs the same column-flex treatment so .cl-panel__scroll can grow. */
.cl-panel__thread-host {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* Panel header: filename + avatars + stats */
.cl-panel__header {
  padding: 1.25rem 1.5rem 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.07);
  flex-shrink: 0;
}

.cl-panel__filename {
  font-size: 1.05rem;
  font-weight: 700;
  color: #18181b;
  margin: 0 0 0.5rem;
  word-break: break-all;
}

.cl-panel__meta {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.cl-panel__avatars {
  display: flex;
  align-items: center;
}

.cl-panel__avatar {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary-600, #4f46e5) 12%, white);
  border: 2px solid #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  font-weight: 700;
  color: var(--color-primary-700, #4338ca);
  margin-left: -6px;
  flex-shrink: 0;
}

.cl-panel__avatar:first-child { margin-left: 0; }

.cl-panel__stats {
  font-size: 0.72rem;
  color: #71717a;
}

/* Scrollable thread */
.cl-panel__scroll {
  flex: 1;
  overflow-y: auto;
  padding: 1rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0;
  scrollbar-width: thin;
  scrollbar-color: rgba(0, 0, 0, 0.12) transparent;
}

.cl-panel__loading {
  padding: 3rem 1rem;
  text-align: center;
  font-size: 0.82rem;
  color: #71717a;
}

.cl-panel__empty {
  padding: 3rem 1rem;
  text-align: center;
  font-size: 0.82rem;
  color: #71717a;
}


/* ---------- Mobile full-page chat view ----------
 * Only the mobile viewport turns the drill-down into a fullscreen overlay.
 * On desktop the chat renders in-flow within the normal app chrome so
 * the sidebar / header stay usable (desktop users normally open the
 * lightbox dialog and never hit this view directly). */

.ps-mobile-chat {
  display: flex;
  flex-direction: column;
  background: var(--color-main-bg, #faf9fc);
  min-height: 70vh;
}

@media (width < 48rem) {
  .ps-mobile-chat {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    /* 100dvh tracks the dynamic viewport — shrinks when Safari's URL bar
     * is visible so the sticky composer stays above it. Older engines
     * without dvh support fall back to svh (small viewport). */
    height: 100svh;
    height: 100dvh;
    z-index: 1000;
    overscroll-behavior: contain;
    min-height: 0;
  }

  /* Hide the underlying app chrome while the drill-down is on screen so
   * the hamburger header / sidebar / bottom nav don't peek through.
   * The BottomNavComponent renders as <nav aria-label="Main navigation">
   * with no class, so target it by aria-label (i18n-agnostic in source). */
  body:has(.ps-mobile-chat) #header,
  body:has(.ps-mobile-chat) #sidebar,
  body:has(.ps-mobile-chat) nav[aria-label="Main navigation"] {
    visibility: hidden;
  }
}

.ps-mobile-chat__header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 0.9rem;
  background: var(--color-main-bg, #faf9fc);
  min-height: 56px;
  flex-shrink: 0;
}

.ps-mobile-chat__back,
.ps-mobile-chat__nav-btn {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: var(--color-text, #18181b);
  text-decoration: none;
  flex-shrink: 0;
  transition: background 160ms ease;
  -webkit-tap-highlight-color: transparent;
}

/* IconComponent emits an inline style="width/height: var(--size-4)" that
 * wins over the class-based size-7 helper. Override with !important so
 * the header chevrons actually render at 28px. */
.ps-mobile-chat__back svg,
.ps-mobile-chat__nav-btn svg {
  width: 28px !important;
  height: 28px !important;
  stroke-width: 2.2;
}

.ps-mobile-chat__back:hover,
.ps-mobile-chat__nav-btn:hover {
  background: rgba(0, 0, 0, 0.05);
}

[data-color-scheme="dark"] .ps-mobile-chat__back:hover,
[data-color-scheme="dark"] .ps-mobile-chat__nav-btn:hover {
  background: rgba(255, 255, 255, 0.08);
}

.ps-mobile-chat__nav-btn--disabled {
  opacity: 0.3;
  pointer-events: none;
}

.ps-mobile-chat__title {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.ps-mobile-chat__filename {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-text, #18181b);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ps-mobile-chat__subtitle {
  font-size: 0.72rem;
  color: var(--color-text-subtle, #71717a);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ps-mobile-chat__nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex-shrink: 0;
}

.ps-mobile-chat__image {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #c76b46;
  flex-shrink: 0;
}

.ps-mobile-chat__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The chat thread inside ps-mobile-chat is the same Panel markup (gem
 * chat-thread family inside a chat-card host). Frame and host both need to
 * be column-flex boxes so the scroll area owns the overflow. */
.ps-mobile-chat > turbo-frame,
.ps-mobile-chat > turbo-frame#comments-thread {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.ps-mobile-chat .cl-panel__scroll {
  padding: 0.75rem 1rem;
}

/* Full-page / side-pane contexts: the composer is not a card footer here,
 * so drop the card's rounded footer corners. */
.ps-mobile-chat .cl-reply {
  border-radius: 0;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
}

/* ============================================================
 * Lightbox info side panel — slide-in overlay on the right.
 * Mirrors the PhotoSwipe info panel pattern from the gui_components
 * lightbox controller so the photographer-facing lightboxes share
 * the same image-info UI as the public "Alle Bilder" page.
 * ============================================================ */
.cl-info-panel {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  block-size: 100%;
  inline-size: min(420px, 90%);
  background: #ffffff;
  color: #111827;
  box-shadow: -16px 0 48px rgba(0, 0, 0, 0.45);
  transform: translateX(100%);
  visibility: hidden;
  transition:
    transform 350ms var(--ease-drawer),
    visibility 0s 350ms;
  overflow-y: auto;
  padding: 0;
  font: 13px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
  z-index: 50;
}

.cl-info-panel--open {
  transform: translateX(0);
  visibility: visible;
  transition: transform 350ms var(--ease-drawer);
}

.cl-info-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.875rem 1.125rem 0.75rem;
  border-bottom: 1px solid rgba(17, 24, 39, 0.08);
  position: sticky;
  top: 0;
  background: #ffffff;
  z-index: 1;
}

.cl-info-panel__title {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  color: #111827;
  letter-spacing: -0.01em;
}

.cl-info-panel__close {
  width: 2.25rem;
  height: 2.25rem;
  border: none;
  border-radius: 9999px;
  background: transparent;
  color: #4b5563;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 150ms ease-out, color 150ms ease-out;
  flex-shrink: 0;
}

.cl-info-panel__close:hover {
  background: rgba(17, 24, 39, 0.06);
  color: #111827;
}

.cl-info-panel__body {
  padding: 0.875rem 1.125rem;
}

.cl-info-panel__section {
  margin-bottom: 1.125rem;
}

.cl-info-panel__section:last-child {
  margin-bottom: 0;
}

.cl-info-panel__heading {
  margin: 0 0 0.4375rem;
  font-size: 0.8125rem;
  font-weight: 700;
  color: #111827;
}

.cl-info-panel__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.3rem 0;
  font-size: 0.8125rem;
}

.cl-info-panel__label {
  color: #4b5563;
  font-weight: 400;
  flex-shrink: 0;
}

.cl-info-panel__value {
  color: #111827;
  font-weight: 500;
  text-align: right;
  word-break: break-word;
  min-width: 0;
}

.cl-info-panel__loading,
.cl-info-panel__empty,
.cl-info-panel__error {
  color: #6b7280;
  font-size: 0.8125rem;
  padding: 0.5rem 0;
}



/* ===== pages/photo_shooting_likes.css ===== */
/* ============================================================
 * Likes tab viewer (lk-*)
 * The Likes lightbox is a PhotoSwipe viewer (same library as the
 * Alle-Bilder, Favoriten and Kommentare tabs). These rules style the
 * two prerendered nodes the Stimulus controller mounts into the
 * PhotoSwipe element while the viewer is open: the likes/comments side
 * panel and the favorite toggle in the toolbar.
 * ============================================================ */

/* ---------- Side panel ----------
 * Desktop: a solid column pinned to the right edge; PhotoSwipe reserves
 * the same width via its paddingFn, so the photo never slides under it.
 * Mobile: a bottom sheet covering the lower two thirds, sliding in over
 * the photo (no padding reservation there). */

/* One source of truth for the reserved width: the panel sizes itself with it,
 * the toolbar and the next-arrow keep clear of it, and the Stimulus controller
 * feeds the same measured width into PhotoSwipe's paddingFn. */
.pswp.lk-viewer,
.lk-pswp-panel {
  --lk-panel-w: min(440px, 38vw);
}

.lk-pswp-panel {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  inline-size: var(--lk-panel-w);
  background: #fff;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: -1px 0 0 rgba(0, 0, 0, 0.08);
  min-height: 0;
  z-index: 20;
  transform: translateX(100%);
  visibility: hidden;
  transition:
    transform 320ms var(--ease-drawer, cubic-bezier(0.32, 0.72, 0, 1)),
    visibility 0s 320ms;
}

.lk-pswp-panel--open {
  transform: translateX(0);
  visibility: visible;
  transition: transform 320ms var(--ease-drawer, cubic-bezier(0.32, 0.72, 0, 1));
}

/* The panel content arrives as <turbo-frame id="likes-panel">. Frame and
 * chat-card host both need to be column-flex boxes with min-height:0 so
 * the thread — not the panel — owns the overflow (same treatment the
 * comments lightbox gives .cl-panel). */
.lk-pswp-panel > turbo-frame,
.lk-pswp-panel > turbo-frame#likes-panel {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.lk-pswp-panel .cl-panel__thread-host {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* Header, favorite status and the likes list are fixed-height chrome;
 * only the comment thread scrolls. */
.lk-panel__section {
  flex-shrink: 0;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.07);
}

.lk-panel__filename {
  font-size: 0.875rem;
  font-weight: 700;
  color: #18181b;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Favorite status row: both states ship in the markup, `data-favorited`
 * decides which icon/label pair is visible. */
.lk-panel__status {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.lk-panel__status-icon {
  display: none;
  align-items: center;
  justify-content: center;
  inline-size: 1.5rem;
  block-size: 1.5rem;
  border-radius: 999px;
  flex-shrink: 0;
}

.lk-panel__status-icon--on {
  background: #fde68a;
  color: #92400e;
}

.lk-panel__status-icon--off {
  background: #f4f4f5;
  color: #71717a;
}

.lk-panel__status-text {
  display: none;
  font-size: 0.75rem;
  color: var(--color-text-primary, #18181b);
}

.lk-panel__status[data-favorited="true"] .lk-panel__status-icon--on,
.lk-panel__status[data-favorited="false"] .lk-panel__status-icon--off {
  display: inline-flex;
}

.lk-panel__status[data-favorited="true"] .lk-panel__status-text--on,
.lk-panel__status[data-favorited="false"] .lk-panel__status-text--off {
  display: inline;
}

.lk-panel__label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: #71717a;
}

/* Composer is a panel footer here, not a card footer. */
.lk-pswp-panel .cl-reply {
  border-radius: 0;
  border-top: 1px solid rgba(0, 0, 0, 0.06);
}

/* PhotoSwipe's toolbar and arrows span the whole viewport. While the panel is
 * open they have to stay inside the photo area, otherwise the buttons sit on
 * the panel's white background and become unreadable. */
@media (width >= 48rem) {
  .pswp.lk-viewer--panel-open .pswp__top-bar {
    padding-inline-end: var(--lk-panel-w);
  }

  .pswp.lk-viewer--panel-open .pswp__button--arrow--next {
    inset-inline-end: var(--lk-panel-w);
  }
}

@media (width < 48rem) {
  /* The sheet covers the lower two thirds, where the arrows would sit —
   * swiping stays available. */
  .pswp.lk-viewer--panel-open .pswp__button--arrow {
    display: none;
  }

  .lk-pswp-panel {
    inset-block-start: auto;
    inset-inline: 0;
    inline-size: 100%;
    block-size: 68svh;
    border-start-start-radius: 1rem;
    border-start-end-radius: 1rem;
    transform: translateY(100%);
  }

  .lk-pswp-panel--open {
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .lk-pswp-panel {
    transition: none;
  }
}

/* The thread scrolls vertically inside a viewer that otherwise owns all
 * gestures. */
.lk-pswp-panel .cl-panel__scroll {
  touch-action: pan-y;
  overscroll-behavior: contain;
}

/* ---------- Toolbar buttons ----------
 * Our two buttons carry plain currentColor SVGs, unlike PhotoSwipe's own
 * icons, which get their white fill from .pswp__icn. Without an explicit
 * colour they render near-black on the dark stage — the unfavorited star
 * was invisible. The drop shadow matches the built-in buttons so all five
 * stay readable over a bright photo. */

.pswp.lk-viewer .lk-fav-btn,
.pswp.lk-viewer .pswp__button--lk-panel-toggle {
  color: var(--pswp-icon-color, #fff);
}

.pswp.lk-viewer .lk-fav-btn svg,
.pswp.lk-viewer .pswp__button--lk-panel-toggle svg {
  filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.55));
}

/* An SVG dropped straight into a PhotoSwipe button stretches to the button
 * box (50px) and picks up its fill, so the bubble came out oversized and
 * solid white. Pin it to the 24px icon grid the other toolbar glyphs use —
 * the star escapes both effects only because it sits in a wrapper span. */
.pswp.lk-viewer .pswp__button--lk-panel-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.pswp.lk-viewer .pswp__button--lk-panel-toggle > svg {
  fill: none;
  inline-size: 24px;
  block-size: 24px;
}

/* ---------- Toolbar favorite toggle ----------
 * Mounted into the PhotoSwipe top bar next to zoom/close, so it inherits
 * .pswp__button sizing; only the two star states are ours. The filled
 * star shows once the image is a photographer favorite. */

.lk-fav-btn__icon {
  display: none;
  align-items: center;
  justify-content: center;
  inline-size: 100%;
  block-size: 100%;
}

.lk-fav-btn[data-favorited="false"] .lk-fav-btn__icon--add,
.lk-fav-btn[data-favorited="true"] .lk-fav-btn__icon--remove {
  display: inline-flex;
}

/* Scoped to .pswp.lk-viewer as well, otherwise the white toolbar colour above
 * wins on specificity and the favorited star loses its amber. */
.pswp.lk-viewer .lk-fav-btn[data-favorited="true"],
.lk-fav-btn[data-favorited="true"] {
  color: #fbbf24;
}

/* At 24px a stroke-2 star nearly closes up and reads as filled, which would
 * blur the line between "favorite" and "not a favorite". */
.lk-fav-btn__icon--add svg {
  stroke-width: 1.5;
}


/* ===== photo_favorites.css ===== */
/* ============================================================
 * Favoriten tab — tile marker animation + PhotoSwipe viewer chrome.
 * The fullscreen viewer itself is PhotoSwipe (same as the gallery
 * lightboxes); fv-* only styles the favorite toggle mounted into the
 * PhotoSwipe wrapper. Scoped under fv-* so the cl-* split-screen
 * lightbox used by the Kommentare tab stays untouched.
 * ============================================================ */

/* ---------- Tile badge / toggle pop-in (fv-pop keyframe in animations.css) ----------
 * The pop fires only on the transient .fv-just-favorited class the controller
 * adds when the user toggles a tile ON — NOT on the persistent data-favorited
 * state, which every already-favorited tile carries on first paint and would
 * otherwise animate on every page load (doubled on iOS via view-transitions). */

.fv-photo-card.fv-just-favorited .fv-pop {
  animation: fv-pop 250ms ease;
}

/* ---------- PhotoSwipe bottom bar: favorite toggle ---------- */

.fv-pswp-bottom {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  padding: 0.75rem 1.25rem 1.875rem;
  padding-bottom: calc(1.875rem + env(safe-area-inset-bottom, 0px));
  z-index: 10;
  pointer-events: none;
}

.fv-pswp-bottom .fv-fav-toggle {
  pointer-events: auto;
}

.fv-fav-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  width: 100%;
  max-width: 26rem;
  border: none;
  border-radius: 999px;
  padding: 0.9375rem 1.5rem;
  font-size: 1rem;
  font-weight: 800;
  font-family: inherit;
  cursor: pointer;
  transition: background 180ms ease, color 180ms ease, transform 120ms ease;
  background: var(--color-primary-600, #1352f1);
  color: #fff;
  -webkit-tap-highlight-color: transparent;
}

.fv-fav-toggle:active {
  transform: scale(0.98);
}

.fv-fav-toggle[data-favorited="true"] {
  background: #fdf3df;
  color: #b45309;
}

.fv-fav-toggle.fv-just-favorited {
  animation: fv-pop 250ms ease;
}

.fv-fav-toggle__icon {
  display: inline-flex;
}

.fv-fav-toggle__icon--remove { display: none; }

.fv-fav-toggle[data-favorited="true"] .fv-fav-toggle__icon--add { display: none; }
.fv-fav-toggle[data-favorited="true"] .fv-fav-toggle__icon--remove { display: inline-flex; }

@media (prefers-reduced-motion: reduce) {
  .fv-photo-card.fv-just-favorited .fv-pop,
  .fv-fav-toggle.fv-just-favorited {
    animation: none;
  }
}


/* ===== pages/settings_watermarks.css ===== */
/* Watermark settings editor — full-width 2-column layout below a sticky
 * topbar. Style for pieces that are awkward in pure Tailwind utilities:
 * the editor card grid + breakpoints, the sample preview canvas, the
 * swatch row active ring, and a custom range slider thumb.
 *
 * All selectors are scoped under .wm-editor / .wm-library so no rules
 * leak into the rest of the app.
 */

/* ---------- Editor shell (2-column grid, collapses on narrow viewports) ---------- */

.wm-editor {
  display: grid;
  grid-template-columns: 1fr 360px;
  /* Clip the wm-stage's dotted background + wm-canvas shadow to the
   * rounded-2xl frame. overflow:clip clips without creating a scrolling
   * ancestor, so the props panel's position:sticky keeps referencing
   * the viewport instead of binding to this container. */
  overflow: clip;
}

@media (width >= 1080px) {
  /* The scroll container is #main (below the sticky topbar), not the window,
   * so the sticky offset is measured from the top of #main — which already
   * sits under the topbar. Hence top:0 (not 56px); a 56px offset double-counts
   * the bar and pushes the panel's bottom off-screen. The panel then fills the
   * visible area and scrolls itself. */
  .wm-editor__props-sticky {
    position: sticky;
    top: 0;
    max-height: calc(100dvh - 56px);
    overflow: hidden;
  }

  /* Only the field list scrolls; the type tabs in __props-head stay pinned at
   * the top of the panel. min-height:0 lets the flex child shrink below its
   * content height so the overflow actually engages. */
  .wm-editor__props-body {
    min-height: 0;
    overflow-y: auto;
  }
}

.wm-editor__props {
  border-left: 1px solid var(--color-border);
}

@media (width < 80rem) {
  .wm-editor {
    grid-template-columns: 1fr 320px;
  }
}

/* Props-only variant — used when the editor renders without its live
 * preview stage (e.g. inside the guest-preview watermark panel, where
 * the gallery behind the panel already shows the effect directly). */
.wm-editor.wm-editor--props-only {
  grid-template-columns: 1fr;
}

.wm-editor--props-only .wm-editor__props {
  border-left: 0;
}

@media (width < 1080px) {
  .wm-editor {
    grid-template-columns: 1fr;
  }

  .wm-editor__props {
    border-left: 0;
    border-top: 1px solid var(--color-border);
  }
}

/* gui_components Input / Select wrap each field in a div with
 * mb-[25px] / max-sm:mb-[30px] for standalone settings forms. The
 * watermark editor's right props panel is already a flex column with
 * gap-4 between fields, so the wrapper margin stacks awkwardly. */
.wm-editor__props .mb-\[25px\],
.wm-editor__props .max-sm\:mb-\[30px\] {
  margin-bottom: 0;
}

/* ---------- Editor mode toggle (image vs text vs dots) ---------- */

.wm-editor[data-mode="image"] .wm-text-only,
.wm-editor[data-mode="image"] .wm-dots-only,
.wm-editor[data-mode="text"]  .wm-image-only,
.wm-editor[data-mode="text"]  .wm-dots-only,
.wm-editor[data-mode="dots"]  .wm-image-only,
.wm-editor[data-mode="dots"]  .wm-text-only {
  display: none;
}

/* Secondary logo overlay block — visible in text and dots mode, hidden
 * in image mode where a second image overlay would be confusing. */
.wm-editor[data-mode="image"] .wm-overlay-only {
  display: none;
}

/* Image single/tiled sub-visibility: spacing + angle only tile, the
 * position grid only applies to a single placement. data-image-mode is set
 * on the editor root by the image-mode-toggle Stimulus controller. */
.wm-editor[data-image-mode="single"] .wm-image-tiled-only,
.wm-editor[data-image-mode="tiled"]  .wm-image-single-only {
  display: none;
}

/* Collapse the variant + position controls until the photographer
 * actually opts into the logo overlay. The checkbox sits inside
 * .wm-logo-overlay, so :has(:checked) shows the controls; otherwise the
 * .wm-logo-overlay-controls sibling stays hidden. */
.wm-logo-overlay:not(:has(input[type="checkbox"]:checked)) .wm-logo-overlay-controls {
  display: none;
}

/* Mode-driven visibility for the secondary slider/grid controls. The
 * wrapper carries data-logo-overlay-mode, flipped by the logo-mode-
 * toggle Stimulus controller. Single mode hides tiled-only sliders; tiled
 * mode hides the position grid. */
.wm-logo-overlay[data-logo-overlay-mode="single"] .wm-logo-tiled-only,
.wm-logo-overlay[data-logo-overlay-mode="tiled"]  .wm-logo-single-only {
  display: none;
}

/* ---------- Center stage: dotted background + canvas frame ---------- */

.wm-stage {
  background-image: radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--color-text) 6%, transparent) 1px, transparent 0);
  background-size: 16px 16px;
  background-position: 0 0;
}

.wm-canvas {
  width: 100%;
  aspect-ratio: 3/2;
  border-radius: 14px;
  overflow: hidden;
  position: relative;
  box-shadow: 0 6px 22px color-mix(in srgb, var(--color-text) 10%, transparent);
  background-color: var(--color-border-light);
}

@media (width >= 1080px) {
  .wm-canvas {
    width: min(100%, calc((100dvh - 170px) * 1.5));
  }
}

.wm-canvas img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---------- Optimistic client-side preview (watermark-canvas-preview) ----
   A canvas layered deckungsgleich over the server-preview <img>. Drawn on
   every slider tick with no debounce so the overlay tracks the finger; fades
   out (opacity → 0) once the authoritative server render loads, revealing the
   real converter output underneath. pointer-events:none keeps the compare
   slider fully interactive. Parked hidden, so without JS/canvas support the
   editor behaves exactly as before. */
.wm-canvas__optimistic {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  /* Above the preview <img> and its spinner so, while shown, the optimistic
     layer visually covers the converter round-trip (the spinner stays wired
     but is hidden behind the canvas). Below nothing else in the stage. */
  z-index: 3;
}

/* ---------- Before/after comparison (compare-slider controller) ----------
   The clean render sits on top of the watermarked preview, clipped to the
   left --compare-split portion. The invisible range input covers the whole
   canvas and owns every pointer/keyboard interaction; divider, handle and
   label are purely decorative and track the split via the custom property.
   Parked at 0 % the overlay is invisible and the canvas behaves as before. */

.wm-canvas__before {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--compare-split, 0%)) 0 0);
  pointer-events: none;
}

.wm-canvas__compare-divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--compare-split, 0%);
  width: 2px;
  margin-left: -1px;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.45);
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}

.wm-canvas__compare-handle {
  position: absolute;
  top: 50%;
  left: var(--compare-split, 0%);
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.95);
  color: #333;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

.wm-canvas__compare-label {
  position: absolute;
  top: 10px;
  left: 10px;
  padding: 2px 8px;
  border-radius: 9999px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}

.wm-canvas.is-comparing .wm-canvas__compare-divider,
.wm-canvas.is-comparing .wm-canvas__compare-label {
  opacity: 1;
}

.wm-canvas__compare-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ew-resize;
  touch-action: pan-y;
  appearance: none;
  -webkit-appearance: none;
}

/* The image-watermark editor swaps the canvas to a client-side blob: of the
   picked file — no server preview, nothing to compare. The compare-slider
   controller flags that state and the whole overlay disappears. */
.wm-canvas.compare-unavailable .wm-canvas__before,
.wm-canvas.compare-unavailable .wm-canvas__compare-divider,
.wm-canvas.compare-unavailable .wm-canvas__compare-handle,
.wm-canvas.compare-unavailable .wm-canvas__compare-label,
.wm-canvas.compare-unavailable .wm-canvas__compare-input {
  display: none;
}

/* ---------- Library grid (max 3 cards per row, wraps onto more rows) ---------- */

.wm-library {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  padding: 4px;
  margin: -4px;
}

@media (width < 64rem) {
  .wm-library {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (width < 40rem) {
  .wm-library {
    grid-template-columns: 1fr;
  }
}

/*
 * Watermark-library filter pill: NO view-transition-name here on purpose.
 *
 * The Alle | Bild | Text filter is a SegmentedControlComponent in link mode
 * and already slides its pill across Drive visits via the gem's
 * `data-vt-indicator` carry (view_transitions_controller.js). A statically
 * VT-named active segment used to live here — but the name existed only on
 * the watermark pages, so entering from /settings produced an UNPAIRED
 * new-only group whose 0.3s animation held the input-blocking VT overlay
 * open while the old page's unpaired `vt-content` snapshot stayed painted
 * over the new page (a ~0.3s "half faded" ghost of the settings page).
 */

/*
 * Topbar auto-save indicator: "Gespeichert" state. The save-indicator
 * controller flips the span from display:none to flex, which restarts the
 * one-shot entrance each time a save completes — a springy pop plus a
 * primary-tinted pill flash so the confirmation catches the eye without
 * needing JS re-arming. The pill decays to transparent at the end of the
 * animation so the resting state stays quiet.
 */
@keyframes wm-saved-pop {
  0%   { opacity: 0; transform: translateX(8px) scale(0.6); }
  55%  { opacity: 1; transform: translateX(0) scale(1.12); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes wm-saved-flash {
  0%, 60% { background-color: color-mix(in srgb, var(--color-primary) 14%, transparent); }
  100%    { background-color: transparent; }
}

.wm-saved-pop {
  color: var(--color-primary);
  border-radius: 9999px;
  padding: 0.125rem 0.625rem;
  animation:
    wm-saved-pop 0.45s var(--ease-spring),
    wm-saved-flash 1.4s ease-out forwards;
}


/* ===== watermark_thumb.css ===== */
.wm-thumb {
  container-type: inline-size;
}

.wm-thumb-text {
  font-size: calc(var(--wm-text-ratio, 0.3) * 170cqw / var(--wm-text-chars, 13));
  line-height: 1.1;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
}


/* ===== pages/settings_email_templates.css ===== */
/* ============================================================
 * Settings → E-Mail-Vorlagen overview page.
 *
 * Page-scoped under .email-templates-page so the warm Inter
 * palette / card styling stays isolated from the rest of the app.
 * Mirrors the design handoff (warm off-white, oklch accents,
 * 52px icon badges, grouped sections, hover-revealed developer
 * details).
 * ============================================================ */

.email-templates-page {
  --et-bg: var(--brand-neutral-50);
  --et-surface: var(--brand-neutral-100);
  --et-surface-2: var(--brand-neutral-200);
  --et-ink: var(--brand-neutral-900);
  --et-ink-2: var(--brand-neutral-700);
  --et-ink-3: var(--brand-neutral-500);
  --et-ink-4: var(--brand-neutral-400);
  --et-line: var(--brand-neutral-200);
  --et-line-strong: var(--brand-neutral-300);
  --et-accent: oklch(58% 0.08 250);
  --et-accent-soft: oklch(96% 0.018 250);
  --et-accent-line: oklch(88% 0.04 250);
  --et-warm: oklch(65% 0.09 60);
  --et-warm-soft: oklch(96% 0.025 70);
  --et-warm-line: oklch(88% 0.05 65);
  --et-green: oklch(58% 0.08 155);
  --et-green-soft: oklch(96% 0.02 155);
  --et-green-line: #cfe4d8;
  --et-pill-bg: var(--brand-neutral-200);
  --et-radius: 12px;

  background: var(--et-bg);
  color: var(--et-ink);
  font-family: "Inter", -apple-system, Helvetica, Arial, sans-serif;
  font-feature-settings: "ss01", "cv11";
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100%;
}

[data-color-scheme="dark"] .email-templates-page {
  --et-bg: var(--brand-neutral-950);
  --et-surface: var(--brand-neutral-900);
  --et-surface-2: var(--brand-neutral-800);
  --et-ink: var(--brand-neutral-50);
  --et-ink-2: var(--brand-neutral-200);
  --et-ink-3: var(--brand-neutral-400);
  --et-ink-4: var(--brand-neutral-500);
  --et-line: var(--brand-neutral-800);
  --et-line-strong: var(--brand-neutral-700);
  --et-pill-bg: var(--brand-neutral-800);
}

.email-templates-page__inner {
  padding: 24px 16px 64px;
  max-width: 1080px;
  margin-inline: auto;
}

/* ───── Page header ───── */
.email-templates-page .et-hdr {
  margin-bottom: 28px;
}
.email-templates-page .et-hdr h1 {
  margin: 0 0 8px;
  font-size: 28px;
  letter-spacing: -0.02em;
  font-weight: 600;
  line-height: 1.15;
  color: var(--et-ink);
}
.email-templates-page .et-hdr p {
  margin: 0;
  color: var(--et-ink-2);
  font-size: 14.5px;
  line-height: 1.55;
  max-width: 62ch;
}
/* ───── Group ───── */
.email-templates-page .et-group {
  margin-bottom: 34px;
}
.email-templates-page .et-group-hd {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 0 4px;
  margin-bottom: 12px;
}
.email-templates-page .et-group-hd h2 {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--et-ink-3);
}
.email-templates-page .et-group-hd .et-count {
  font-size: 12px;
  color: var(--et-ink-4);
  font-variant-numeric: tabular-nums;
}

/* ───── Cards stack ───── */
.email-templates-page .et-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.email-templates-page .et-card {
  position: relative;
  background: var(--et-surface);
  border: 1px solid var(--et-line);
  border-radius: var(--et-radius);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.email-templates-page .et-card:hover {
  border-color: var(--et-line-strong);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.02), 0 6px 20px -10px rgba(0, 0, 0, 0.08);
}

.email-templates-page .et-card__link {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 14px;
  padding: 16px;
  align-items: start;
  color: inherit;
  text-decoration: none;
}

.email-templates-page .et-card-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  background: var(--et-surface-2);
  border: 1px solid var(--et-line);
  color: var(--et-ink-2);
  margin-top: -2px;
}
.email-templates-page .et-card-icon.kind-team {
  background: var(--et-accent-soft);
  border-color: var(--et-accent-line);
  color: var(--et-accent);
}
.email-templates-page .et-card-icon.kind-gallery {
  background: var(--et-warm-soft);
  border-color: var(--et-warm-line);
  color: var(--et-warm);
}
.email-templates-page .et-card-icon.kind-shoot {
  background: var(--et-green-soft);
  border-color: var(--et-green-line);
  color: var(--et-green);
}
.email-templates-page .et-card-icon.kind-system {
  background: var(--et-surface-2);
  border-color: var(--et-line);
  color: var(--et-ink-3);
}
.email-templates-page .et-card-icon svg {
  width: 22px;
  height: 22px;
}

.email-templates-page .et-card-text {
  padding-top: 2px;
  min-width: 0;
}
.email-templates-page .et-card-text h3 {
  margin: 0 0 4px;
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--et-ink);
  line-height: 1.25;
}
.email-templates-page .et-card-text p {
  margin: 0;
  font-size: 13.5px;
  color: var(--et-ink-2);
  line-height: 1.5;
}
.email-templates-page .et-card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-top: 2px;
  grid-column: 1 / -1;
  flex-wrap: wrap;
}

.email-templates-page .et-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  border: 1px solid color-mix(in oklch, var(--secondary-300) 30%, transparent);
  background: var(--secondary-100);
  color: var(--secondary-800);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.email-templates-page .et-btn:hover {
  background: var(--secondary-200);
}
.email-templates-page .et-btn.is-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--et-ink-2);
}
.email-templates-page .et-btn.is-ghost:hover {
  background: color-mix(in oklch, var(--et-ink) 6%, transparent);
  color: var(--et-ink);
}
.email-templates-page .et-btn svg {
  width: 14px;
  height: 14px;
}

[data-color-scheme="dark"] .email-templates-page .et-btn {
  background: var(--secondary-900);
  color: var(--secondary-100);
  border-color: color-mix(in oklch, var(--secondary-700) 30%, transparent);
}
[data-color-scheme="dark"] .email-templates-page .et-btn:hover {
  background: var(--secondary-800);
}

/* ───── From sm up: roomier padding, 3-column card with inline actions ───── */
@media (width >= 40rem) {
  .email-templates-page__inner {
    padding: 32px 40px 80px;
  }
  .email-templates-page .et-card__link {
    grid-template-columns: 52px 1fr auto;
    gap: 18px;
    padding: 20px;
  }
  .email-templates-page .et-card-actions {
    grid-column: auto;
    flex-wrap: nowrap;
  }
  .email-templates-page .et-card-icon {
    width: 52px;
    height: 52px;
  }
  .email-templates-page .et-card-icon svg {
    width: 26px;
    height: 26px;
  }
}

/* ============================================================
 * Variable pills inside the Lexxy email-template editor.
 *
 * Lexxy's HTML importer only recognises a small set of Lexical
 * text-format tags. `<code>` survives the import → reconcile →
 * export round-trip; `<mark>` is silently dropped. We therefore
 * wrap every variable in `<code>` and distinguish required from
 * optional via an extra `<strong>` (= bold format), which Lexical
 * renders as `<code class="lexxy-content__bold">` in the editor
 * and as nested `<code><strong>…</strong></code>` on export.
 *
 *   - plain `<code>`                            → OPTIONAL (blue)
 *   - `<code class="lexxy-content__bold">` or
 *     `<code><strong>…</strong></code>`         → REQUIRED (red)
 *
 * Selector scope is limited to the contenteditable so usage of
 * `<code>` / `<strong>` elsewhere is unaffected.
 * ============================================================ */
lexxy-editor .lexxy-editor__content code {
  display: inline;
  padding: 1px 9px 1px 8px;
  border-radius: 999px;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  font-weight: 500;
  line-height: inherit;
  white-space: nowrap;
  text-decoration: none;
  background: #eef2ff;
  color: #2855ff;
  border: 1px solid rgba(40, 85, 255, 0.22);
}
lexxy-editor .lexxy-editor__content code.lexxy-content__bold,
lexxy-editor .lexxy-editor__content code:has(> strong) {
  background: #fdf0ee;
  color: #a83a30;
  border-color: #f3c3bd;
  font-weight: 500;
}
lexxy-editor .lexxy-editor__content code > strong {
  font-weight: inherit;
  color: inherit;
  background: transparent;
}

@media (prefers-color-scheme: dark) {
  lexxy-editor .lexxy-editor__content code {
    background: rgba(40, 85, 255, 0.14);
    color: #93b0ff;
    border-color: rgba(147, 176, 255, 0.32);
  }
  lexxy-editor .lexxy-editor__content code.lexxy-content__bold,
  lexxy-editor .lexxy-editor__content code:has(> strong) {
    background: rgba(224, 72, 60, 0.16);
    color: #f0a59c;
    border-color: rgba(240, 165, 156, 0.4);
  }
  lexxy-editor .lexxy-editor__content code.lexxy-content__bold::before,
  lexxy-editor .lexxy-editor__content code:has(> strong)::before {
    background: #f0a59c;
  }
}


/* ===== hold_to_confirm.css ===== */
/* Hold-to-confirm buttons (hold_to_confirm Stimulus controller).
 *
 * A destructive button that requires press-and-hold instead of a confirm
 * dialog: while holding, a currentColor sweep lightens the button left-to-right
 * for exactly --hold-duration (set from the controller value), led by a 3px
 * progress edge. The button also presses in (scale) while held. Because the
 * fill derives from currentColor it stays visible on any variant — a light
 * sweep on dark/negative buttons, a dark sweep on light ones — never tone-on-tone.
 * Releasing early snaps the fill back and shakes the button once as a
 * "hold me longer" hint; a completed hold keeps the fill until release.
 */

.hold-to-confirm {
  position: relative;
  overflow: hidden;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  transition: transform 0.15s var(--ease-out-soft, ease-out);
}

.hold-to-confirm::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in oklch, currentColor 25%, transparent);
  border-right: 3px solid currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.18s ease-out;
  pointer-events: none;
}

.hold-to-confirm--holding::before {
  transform: scaleX(1);
  transition: transform var(--hold-duration, 800ms) linear;
}

.hold-to-confirm--armed::before {
  transform: scaleX(1);
}

/* Press-in feedback while the button is held or armed. Layout-neutral (transform
   only); the ::before sweep lives on a separate element and is unaffected. */
.hold-to-confirm--holding,
.hold-to-confirm--armed {
  transform: scale(0.96);
}

.hold-to-confirm--rejected {
  animation: shake-x 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

/* Reduced motion: the fill still communicates progress (it IS the state), but
   the rejection shake and the press-in scale are skipped. */
@media (prefers-reduced-motion: reduce) {
  .hold-to-confirm--rejected {
    animation: none;
  }

  .hold-to-confirm--holding,
  .hold-to-confirm--armed {
    transform: none;
  }
}


/* ===== pages/notifications.css ===== */
/* Notification timeline — "Changelog" look for the in-app feed page and the
   header bell dropdown. Neutrals come from the app theme tokens (light + dark);
   the four announcement categories get their own colour-coded type tokens. */

:root {
  --nt-feature-fg: oklch(0.58 0.12 168);
  --nt-feature-bg: oklch(0.95 0.045 168);
  --nt-news-fg: oklch(0.56 0.13 252);
  --nt-news-bg: oklch(0.95 0.045 252);
  --nt-downtime-fg: oklch(0.62 0.13 65);
  --nt-downtime-bg: oklch(0.95 0.05 75);
  --nt-info-fg: oklch(0.52 0.04 255);
  --nt-info-bg: oklch(0.95 0.012 255);
  --nt-contact-fg: oklch(0.55 0.14 300);
  --nt-contact-bg: oklch(0.95 0.04 300);

  --nt-shadow-sm: 0 1px 2px oklch(0.5 0.02 250 / 0.06), 0 1px 3px oklch(0.5 0.02 250 / 0.05);
  --nt-shadow-md: 0 4px 12px oklch(0.4 0.02 250 / 0.08), 0 2px 6px oklch(0.4 0.02 250 / 0.05);
}

[data-color-scheme="dark"] {
  --nt-feature-fg: oklch(0.80 0.11 168);
  --nt-feature-bg: oklch(0.30 0.05 168);
  --nt-news-fg: oklch(0.78 0.11 252);
  --nt-news-bg: oklch(0.31 0.06 252);
  --nt-downtime-fg: oklch(0.82 0.11 75);
  --nt-downtime-bg: oklch(0.32 0.06 70);
  --nt-info-fg: oklch(0.80 0.03 255);
  --nt-info-bg: oklch(0.32 0.02 255);
  --nt-contact-fg: oklch(0.82 0.12 300);
  --nt-contact-bg: oklch(0.31 0.06 300);
}

/* ---- Shared type primitives ---- */
.nt-tchip {
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 10px;
}
.nt-tchip.feature  { background: var(--nt-feature-bg);  color: var(--nt-feature-fg); }
.nt-tchip.news     { background: var(--nt-news-bg);     color: var(--nt-news-fg); }
.nt-tchip.downtime { background: var(--nt-downtime-bg); color: var(--nt-downtime-fg); }
.nt-tchip.info     { background: var(--nt-info-bg);     color: var(--nt-info-fg); }
.nt-tchip.contact  { background: var(--nt-contact-bg);  color: var(--nt-contact-fg); }

.nt-tlabel {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 3.5px 11px;
  border-radius: 999px;
  white-space: nowrap;
}
.nt-tlabel.feature  { background: var(--nt-feature-bg);  color: var(--nt-feature-fg); }
.nt-tlabel.news     { background: var(--nt-news-bg);     color: var(--nt-news-fg); }
.nt-tlabel.downtime { background: var(--nt-downtime-bg); color: var(--nt-downtime-fg); }
.nt-tlabel.info     { background: var(--nt-info-bg);     color: var(--nt-info-fg); }
.nt-tlabel.contact  { background: var(--nt-contact-bg);  color: var(--nt-contact-fg); }

.nt-udot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-primary);
  margin-left: 6px;
  vertical-align: middle;
}

.nt-time {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--color-text-subtle);
  white-space: nowrap;
}

.nt-date-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-subtle);
  margin: 0 0 12px;
}

/* ---- Empty state (left-aligned icon tile + inline text) ---- */
.nt-empty {
  display: flex;
  align-items: center;
  gap: 16px;
  max-width: 680px;
  margin-top: 44px;
}
.nt-empty-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 11px;
  background: var(--color-shade);
  border: 1px solid var(--color-border);
  color: var(--color-text-subtle);
}
.nt-empty-text {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--color-text-subtle);
}
.nt-empty-strong {
  color: var(--color-text);
  font-weight: 600;
}

/* Tighter variant inside the bell dropdown popover. */
.nt-empty--dd {
  margin-top: 0;
  gap: 14px;
  padding: 18px;
}
.nt-empty--dd .nt-empty-icon {
  width: 40px;
  height: 40px;
}
.nt-empty--dd .nt-empty-text {
  font-size: 14px;
}

/* ======================================================
   Full page timeline
   ====================================================== */
.nt-group + .nt-group {
  margin-top: 30px;
}
.nt-page-rail {
  position: relative;
}
.nt-pitem {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 6px;
  position: relative;
}
.nt-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.nt-rail .nt-line {
  width: 2px;
  flex: 1;
  background: var(--color-border);
}
.nt-pitem:last-of-type .nt-rail .nt-line.bot {
  background: transparent;
}
.nt-node {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  box-shadow: 0 0 0 5px var(--color-bg);
}
.nt-card {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  padding: 16px 18px;
  margin-bottom: 14px;
  box-shadow: var(--nt-shadow-sm);
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.nt-card:hover {
  box-shadow: var(--nt-shadow-md);
}
.nt-card.unread {
  border-color: var(--color-primary);
}
.nt-card__meta {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 8px;
}
.nt-card__title {
  margin: 0 0 5px;
  font-size: 17px;
  font-weight: 700;
  color: var(--color-text);
}
.nt-card__body {
  margin: 0;
  color: var(--color-text-subtle);
  font-size: 14.5px;
  line-height: 1.55;
  white-space: pre-line;
}
.nt-card__foot {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 13px;
}

/* ======================================================
   Bell dropdown timeline (inside the shared .menu popover)
   ====================================================== */
.nt-dd {
  padding: 0;
  row-gap: 0;
}
.nt-dd-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--color-border-light);
}
.nt-dd-head .nt-dd-title {
  margin: 0;
  font-size: 15px;
  font-weight: 800;
  white-space: nowrap;
  color: var(--color-text);
}
.nt-count-pill {
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--nt-feature-bg);
  color: var(--nt-feature-fg);
}
.nt-dd-body {
  max-height: 396px;
  overflow-y: auto;
  padding: 8px 0;
}
.nt-dd-item {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 14px;
  padding: 4px 18px 4px 6px;
  text-decoration: none;
  color: inherit;
}
.nt-dd-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-left: 6px;
}
.nt-dd-rail .nt-line {
  width: 2px;
  flex: 1 1 auto;
  min-height: 8px;
  background: var(--color-border);
}
.nt-dd-rail .nt-line.top {
  flex: 0 0 13px;
  min-height: 13px;
}
.nt-dd-item:first-child .nt-dd-rail .nt-line.top {
  background: transparent;
}
.nt-dd-item:last-of-type .nt-dd-rail .nt-line.bot {
  background: transparent;
}
.nt-dd-node {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  box-shadow: 0 0 0 4px var(--color-bg);
}
.nt-dd-item.read .nt-dd-node {
  opacity: 0.78;
}
.nt-dd-content {
  padding: 12px 0 16px;
  min-width: 0;
}
.nt-dd-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.nt-dd-itemtitle {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--color-text);
}
.nt-dd-item.read .nt-dd-itemtitle {
  font-weight: 600;
}
.nt-dd-itembody {
  margin-top: 5px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-text-subtle);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.nt-dd-foot {
  border-top: 1px solid var(--color-border-light);
}


/* Public authentication cards (sign-in, password reset) — "Editorial & Warm" brand look.
 * Imported after pages/landing.css (below) is not required: the warm/gold :root tokens
 * are global once landing.css loads, and auth.css also carries fallbacks. */

/* ===== pages/auth.css ===== */
/* ============================================================================
 * Public authentication pages (sign-in, password-reset request, choose new
 * password) — "Editorial & Warm" brand look (issue #690). App layer served raw
 * by Propshaft (no Tailwind build). Reads the warm/gold tokens defined on :root
 * in pages/landing.css (--ink, --sub, --hair, --gold, --gold-deep, --warm), so
 * these cards match the marketing surfaces without touching the gem.
 *
 * Scoping: the warm page background is applied only when an .auth-shell is on
 * the page (via :has()), so the shared SessionLayout — also used by invitations
 * and the forced-password-change flow — is left untouched. The card overrides
 * the theme --color-* tokens locally so it always reads light/editorial,
 * regardless of the visitor's color-scheme cookie. Unlayered rules here win
 * over the gem's layered Tailwind utilities by cascade-layer precedence.
 * ========================================================================== */

/* Warm page background — only on the branded auth cards, not every SessionLayout page. */
html:has(.auth-shell),
body:has(.auth-shell) {
  background: var(--warm, #f7f3ea);
}

.auth-shell {
  width: 100%;
  max-width: 26rem;
  margin: 0 auto;
  padding: clamp(24px, 6vh, 56px) 16px;
}

/* Always-light editorial card: pin the theme tokens the gem inputs read so the
 * card, fields and floating labels stay light even under a dark color scheme. */
.auth-card {
  --color-bg: #fff;
  --color-text: #1d1c19;
  --color-text-subtle: #5d594f;
  --color-border: #e3ddd0;
  --color-focus-ring: #c8912b;
  --color-link: #a37318;

  background: #fff;
  border: 1px solid var(--hair, #eae6dc);
  border-radius: 20px;
  box-shadow: 0 24px 60px -34px rgba(25, 23, 19, 0.4);
  padding: clamp(28px, 5vw, 40px);
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  color: var(--ink, #1d1c19);
}

/* Mobile: drop the card chrome and sit fields directly on the warm surface,
 * matching the app's existing edge-to-edge auth layout below the sm breakpoint. */
@media (width < 40rem) {
  .auth-shell {
    max-width: 100%;
    padding: 24px 20px calc(24px + env(safe-area-inset-bottom));
  }
  .auth-card {
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
    padding: 0;
  }
}

.auth-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 22px;
}

/* Gold wordmark on the editorial/warm auth cards (mirrors landing.css:460-462).
   Literal fallbacks keep the mark gold-on-ink regardless of stylesheet load order. */
.auth-logo .brand-logo__frame { fill: var(--ink, #1d1c19); }
.auth-logo .brand-logo__cell { fill: var(--gold, #c8912b); }
.auth-logo .brand-logo__gate { color: var(--gold, #c8912b); }

.auth-head {
  text-align: center;
  margin-bottom: 26px;
}

.auth-title {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-size: 1.5rem;
  line-height: 1.2;
  color: var(--ink, #1d1c19);
  margin: 0 0 6px;
}

.auth-sub {
  color: var(--sub, #5d594f);
  font-size: 0.95rem;
  line-height: 1.5;
  margin: 0;
}

/* Dark ink pill submit — overrides the gem ButtonComponent's :primary styling. */
.auth-card .auth-submit {
  background: var(--ink, #1d1c19);
  color: #fff;
  border: none;
  border-radius: 999px;
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
  font-weight: 600;
  box-shadow: 0 12px 26px -14px rgba(25, 23, 19, 0.6);
  transition: transform 0.15s ease, background 0.15s ease;
}

.auth-card .auth-submit:hover {
  background: #000;
  transform: translateY(-1px);
}

.auth-card .auth-submit:disabled {
  transform: none;
}

/* In-card secondary links (forgot password / back to sign-in). */
.auth-alt {
  text-align: center;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--hair, #eae6dc);
}

.auth-alt-link {
  color: var(--sub, #5d594f);
  font-size: 0.9rem;
  text-decoration: none;
}

.auth-alt-link:hover {
  color: var(--gold-deep, #a37318);
}

/* Subtle "back to the website" link at the foot of the card. */
.auth-back {
  text-align: center;
  margin-top: 16px;
}

.auth-back-link {
  color: var(--sub, #5d594f);
  font-size: 0.85rem;
  text-decoration: none;
}

.auth-back-link:hover {
  color: var(--gold-deep, #a37318);
  text-decoration: underline;
  text-underline-offset: 3px;
}


/* Public marketing landing page (LandingController) — gem building blocks + app layer */

/* ===== landing_base.css ===== */
/* =================================================================
   LANDING — Basis-Stylesheet für die Landing-Page-Bausteine.

   Prinzip:
   · Jede Landing-Sektion rendert als <section class="bs bs-…"> und liest
     ausschließlich die Flächen-Tokens (--bg, --fg, --line, …).
   · data-theme="dark" auf der Sektion kippt nur die Tokens — dadurch
     funktioniert jeder Baustein hell UND dunkel.
   · Alles ist unter .bs gescoped, damit nichts in die Host-App leakt.
   · Marke/Typo sind über --landing-*-Variablen der Host-App übersteuerbar.
   ================================================================= */

.bs {
  /* Marke — via --landing-* von außen übersteuerbar */
  --brand:       var(--landing-brand, #394dfe);
  --brand-700:   var(--landing-brand-700, #2c3ad6);
  --brand-300:   var(--landing-brand-300, #8a96ff);
  --violet:      var(--landing-accent, #6f5bff);
  --brand-rgb:   var(--landing-brand-rgb, 57, 77, 254);
  --violet-rgb:  var(--landing-accent-rgb, 111, 91, 255);
  --teal:    #18b8a6;
  --green:   #25c26e;
  --orange:  #ff8a3d;
  --red:     #ef5b6e;
  --indigo-deep: var(--landing-dark-surface, #0d033b);
  --indigo-2:    var(--landing-dark-surface-2, #160a52);

  --font-display: var(--landing-font-display, "InterVariable", "Inter", system-ui, sans-serif);
  --font-body:    var(--landing-font-body, "InterVariable", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif);

  --r-sm: 10px; --r-md: 16px; --r-lg: 24px; --r-xl: 32px;
  --maxw: 1180px;

  /* Flächen-Tokens — Default: HELL */
  --bg:        #fbfbfe;
  --bg-soft:   #f1f0f8;
  --card:      #ffffff;
  --fg:        #14101f;
  --fg-soft:   #4a4658;
  --fg-muted:  #8b8798;
  --line:      #e9e7f0;
  --glass:     rgba(20, 16, 31, .04);
  --shadow:    0 1px 2px rgba(20, 16, 31, .04), 0 14px 44px -14px rgba(20, 16, 31, .14);

  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  color: var(--fg-soft);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* Dunkle Fläche — pro Sektion via data-theme="dark" */
.bs[data-theme="dark"] {
  --bg:        var(--indigo-deep);
  --bg-soft:   var(--indigo-2);
  --card:      rgba(255, 255, 255, .05);
  --fg:        #ffffff;
  --fg-soft:   rgba(255, 255, 255, .74);
  --fg-muted:  rgba(255, 255, 255, .5);
  --line:      rgba(255, 255, 255, .13);
  --glass:     rgba(255, 255, 255, .06);
  --shadow:    0 24px 60px -24px rgba(0, 0, 0, .7);
}

/* ---------- Reset & Typo (scoped) ---------- */
.bs, .bs *, .bs *::before, .bs *::after { box-sizing: border-box; }
.bs :is(h1, h2, h3, h4) {
  font-family: var(--font-display); color: var(--fg);
  margin: 0; line-height: 1.06; letter-spacing: -.02em;
}
.bs p { margin: 0; }
.bs a { color: inherit; text-decoration: none; }
.bs ul { margin: 0; padding: 0; list-style: none; }
.bs img, .bs svg { max-width: 100%; display: block; }
.bs :focus-visible { outline: 2px solid var(--brand-300); outline-offset: 3px; }

.bs-container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }

/* ---------- Buttons ---------- */
.bs .bs-btn {
  --bh: 48px;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  height: var(--bh); padding: 0 22px; border-radius: 999px;
  font-family: var(--font-body); font-weight: 600; font-size: 16px;
  cursor: pointer; border: 1.5px solid transparent; white-space: nowrap;
  transition: transform .15s ease, background-color .2s, border-color .2s, box-shadow .2s, color .2s;
}
.bs .bs-btn:hover { transform: translateY(-1px); }
.bs .bs-btn:active { transform: translateY(0); }
.bs .bs-btn-sm { --bh: 40px; font-size: 15px; padding: 0 16px; }
.bs .bs-btn-lg { --bh: 54px; font-size: 17px; padding: 0 26px; }

.bs .bs-btn-primary { background: var(--brand); color: #fff; border-color: var(--brand); box-shadow: 0 8px 20px -8px rgba(var(--brand-rgb), .7); }
.bs .bs-btn-primary:hover { background: var(--brand-700); }
.bs .bs-btn-outline { background: transparent; color: var(--fg); border-color: var(--line); }
.bs .bs-btn-outline:hover { border-color: var(--brand); color: var(--brand); }
.bs[data-theme="dark"] .bs-btn-outline:hover { border-color: rgba(255, 255, 255, .6); color: #fff; }
.bs .bs-btn-ghost { background: var(--glass); color: var(--fg); border-color: var(--line); }
.bs .bs-btn-ghost:hover { border-color: var(--fg-muted); }

/* ---------- Eyebrow / Badge / Sektionen-Kopf ---------- */
.bs .bs-eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 13px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--brand); margin-bottom: 16px;
}
.bs[data-theme="dark"] .bs-eyebrow { color: var(--brand-300); }
.bs .bs-eyebrow::before { content: ""; width: 8px; height: 8px; border-radius: 3px; background: currentColor; }

.bs .bs-badge {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 600; color: var(--fg-soft);
  border: 1px solid var(--line); background: var(--glass);
  padding: 6px 14px; border-radius: 999px;
}

.bs .bs-section-head { max-width: 720px; margin: 0 auto 56px; text-align: center; }
.bs .bs-section-head h2 { font-size: clamp(28px, 3.6vw, 44px); font-weight: 800; }
.bs .bs-section-head p { margin-top: 14px; font-size: 18px; color: var(--fg-soft); }
.bs .bs-section-head.left { margin-left: 0; text-align: left; }

/* ---------- Scroll-Reveals (via landing_fx_controller.js) ----------
   <div data-reveal>            → weich von unten
   <div data-reveal="left">     → von links
   <div data-reveal="right">    → von rechts
   <div data-reveal="zoom">     → skaliert ein
   data-reveal-delay="200"      → Verzögerung in ms
   Kinder staffeln: <div data-stagger> … Kinder erben Reveal
   Sicherheitsnetz: versteckt wird NUR, wenn der Controller verbunden ist
   ([data-fx-ready]) — ohne JS bleibt jeder Inhalt sichtbar.            */
.bs[data-fx-ready] [data-reveal] { opacity: 0; transform: translateY(28px); transition: opacity .7s cubic-bezier(.2, .7, .3, 1), transform .7s cubic-bezier(.2, .7, .3, 1); transition-delay: var(--reveal-delay, 0ms); }
.bs[data-fx-ready] [data-reveal="left"]  { transform: translateX(-36px); }
.bs[data-fx-ready] [data-reveal="right"] { transform: translateX(36px); }
.bs[data-fx-ready] [data-reveal="zoom"]  { transform: scale(.92); }
.bs[data-fx-ready] [data-reveal].is-in   { opacity: 1; transform: none; }

/* ---------- Formular-Fundament ---------- */
.bs .bs-field { display: flex; flex-direction: column; gap: 6px; }
.bs .bs-field label { font-size: 13.5px; font-weight: 700; color: var(--fg); }
.bs .bs-input, .bs .bs-select, .bs .bs-textarea {
  width: 100%; font: 500 16px/1.4 var(--font-body); color: var(--fg);
  background: var(--card); border: 1.5px solid var(--line); border-radius: 12px;
  padding: 12px 14px; transition: border-color .2s, box-shadow .2s;
}
.bs[data-theme="dark"] .bs-input, .bs[data-theme="dark"] .bs-select, .bs[data-theme="dark"] .bs-textarea { background: rgba(255, 255, 255, .06); }
.bs .bs-input::placeholder, .bs .bs-textarea::placeholder { color: var(--fg-muted); }
.bs .bs-input:focus-visible, .bs .bs-select:focus-visible, .bs .bs-textarea:focus-visible {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(var(--brand-rgb), .18);
}
.bs .bs-input[aria-invalid="true"], .bs .bs-textarea[aria-invalid="true"] { border-color: var(--red); }
.bs .bs-error { font-size: 13px; font-weight: 600; color: var(--red); }
.bs .bs-check { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; color: var(--fg-soft); }
.bs .bs-check input { width: 17px; height: 17px; margin-top: 2px; accent-color: var(--brand); flex: none; }
.bs .bs-form-note { font-size: 12.5px; color: var(--fg-muted); }
.bs .bs-success-box {
  display: flex; gap: 12px; align-items: flex-start;
  background: rgba(37, 194, 110, .12); border: 1px solid rgba(37, 194, 110, .4);
  border-radius: var(--r-md); padding: 14px 16px; color: var(--fg); font-weight: 600;
}
.bs .bs-hp { position: absolute !important; left: -9999px !important; opacity: 0; pointer-events: none; }

/* ---------- Code-Snippet mit Copy-Button ---------- */
.bs .bs-snippet { position: relative; background: #0a0823; border: 1px solid rgba(255, 255, 255, .14); border-radius: 14px; overflow: hidden; }
.bs .bs-snippet pre { margin: 0; padding: 18px 20px; overflow-x: auto; font: 400 13.5px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace; color: #b9c2ff; }
.bs .bs-snippet .cm { color: rgba(185, 194, 255, .45); }
.bs .bs-snippet .hl { color: #8fe3d0; }
.bs .bs-copy {
  position: absolute; top: 10px; right: 10px; cursor: pointer;
  font: 600 12px/1 var(--font-body); color: rgba(255, 255, 255, .75);
  background: rgba(255, 255, 255, .08); border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 8px; padding: 7px 11px; transition: border-color .2s, color .2s;
}
.bs .bs-copy:hover { border-color: rgba(255, 255, 255, .5); color: #fff; }

/* ---------- Hilfsmuster: Punkt-Raster ---------- */
.bs .bs-dots { background-image: radial-gradient(currentColor 1px, transparent 1.4px); background-size: 22px 22px; color: rgba(128, 128, 160, .25); }

/* ---------- Reduced Motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .bs *, .bs *::before, .bs *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .bs[data-fx-ready] [data-reveal] { opacity: 1; transform: none; }
}


/* ===== landing_navigation.css ===== */
/* =================================================================
   LANDING — Navigation-Bausteine (.bs-nav-*)

   8 Sticky-Navigation-Varianten für Landing Pages:
   · .bs-nav-solid        deckende Leiste, Blur + Schatten beim Scrollen
   · .bs-nav-pill         schwebende, zentrierte Kapsel (kompakter beim Scrollen)
   · .bs-nav-banner       Announcement-Banner über der Leiste (schließbar)
   · .bs-nav-mega         Mega-Dropdown-Panel unter der Leiste
   · .bs-nav-transparent  startet transparent, wird beim Scrollen fest
   · .bs-nav-center       Editorial: zentriertes Logo, Doppellinie
   · .bs-nav-overlay      Minimal-Leiste + Fullscreen-Overlay-Menü
   · .bs-nav-progress     Fortschrittsbalken + Scrollspy

   Die Klasse "scrolled" setzt der landing-nav Stimulus-Controller ab
   12 px Scroll. Mega/Overlay/Banner/Progress steuert landing-nav-extras.
   ================================================================= */

/* ---------- Gemeinsam: Aktionen, Burger, Mobile-Menü ---------- */
.bs.bs-nav .actions { display: flex; gap: 10px; }
.bs.bs-nav .bs-nav-burger { display: none; margin-left: auto; padding: 8px;
  background: none; border: 0; color: var(--fg); cursor: pointer; }
.bs.bs-nav .bs-nav-mobile { display: none; padding: 6px 24px 22px; background: var(--bg); }
.bs.bs-nav .bs-nav-mobile.open { display: flex; flex-direction: column; }
.bs.bs-nav .bs-nav-mobile a { padding: 11px 0; font-weight: 600; color: var(--fg);
  border-bottom: 1px solid var(--line); }
.bs.bs-nav .bs-nav-mobile .bs-btn { margin-top: 16px; border-bottom: 0; }

/* ---------- Nav 1 · Solide Leiste ---------- */
.bs.bs-nav-solid { position: sticky; top: 0; z-index: 60;
  background: transparent; border-bottom: 1px solid transparent;
  transition: background-color .25s, border-color .25s, box-shadow .25s; }
.bs.bs-nav-solid.scrolled { background: color-mix(in srgb, var(--bg) 80%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--line); box-shadow: 0 12px 34px -18px rgba(0,0,0,.55); }
.bs-nav-solid .inner { display: flex; align-items: center; gap: 28px; height: 68px; }
.bs-nav-solid .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--fg); }
.bs-nav-solid .logo svg { color: var(--brand-300); }
.bs-nav-solid .links { display: flex; gap: 26px; margin: 0 auto; }
.bs-nav-solid .links a { font-size: 15px; font-weight: 600; color: var(--fg-soft); transition: color .2s; }
.bs-nav-solid .links a:hover { color: var(--fg); }
.bs-nav-solid .bs-nav-mobile { border-top: 1px solid var(--line); }
@media (max-width: 860px) {
  .bs.bs-nav-solid .links, .bs.bs-nav-solid .actions { display: none; }
  .bs.bs-nav-solid .bs-nav-burger { display: inline-flex; }
}

/* ---------- Nav 2 · Schwebende Pill ---------- */
.bs.bs-nav-pill { position: sticky; top: 0; z-index: 60; background: transparent;
  display: flex; justify-content: center; padding: 14px 16px 0; }
.bs-nav-pill .wrap { position: relative; width: min(860px, 100%); }
.bs-nav-pill .pill { display: flex; align-items: center; gap: 20px;
  padding: 11px 11px 11px 22px; border-radius: 999px;
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--card) 78%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  box-shadow: var(--shadow);
  transition: padding .25s ease, box-shadow .25s ease; }
.bs.bs-nav-pill.scrolled .pill { padding: 6px 6px 6px 18px;
  box-shadow: 0 4px 10px rgba(20,16,31,.06), 0 24px 54px -18px rgba(20,16,31,.28); }
.bs-nav-pill .logo { display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-display); font-weight: 800; font-size: 18px; color: var(--fg); }
.bs-nav-pill .logo svg { color: var(--teal); }
.bs-nav-pill .links { display: flex; gap: 22px; margin: 0 auto; }
.bs-nav-pill .links a { font-size: 14.5px; font-weight: 600; color: var(--fg-soft); transition: color .2s; }
.bs-nav-pill .links a:hover { color: var(--brand); }
.bs-nav-pill .bs-nav-mobile { position: absolute; top: calc(100% + 10px); left: 0; right: 0;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow); padding: 8px 20px 20px; }
@media (max-width: 760px) {
  .bs.bs-nav-pill .links, .bs.bs-nav-pill .actions { display: none; }
  .bs.bs-nav-pill .bs-nav-burger { display: inline-flex; }
}

/* ---------- Nav 3 · Mit Announcement-Banner ---------- */
.bs.bs-nav-banner { position: sticky; top: 0; z-index: 60;
  background: transparent;
  transition: background-color .25s, box-shadow .25s; }
.bs.bs-nav-banner.scrolled { background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 12px 34px -20px rgba(20,16,31,.35); }
.bs-nav-banner .banner { display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 8px 44px 8px 16px; position: relative; font-size: 14px; font-weight: 600; color: #fff;
  background: linear-gradient(92deg, var(--brand), var(--violet)); }
.bs-nav-banner .banner a { text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.bs-nav-banner .banner .close { position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px;
  background: none; border: 0; color: #fff; cursor: pointer; opacity: .8; }
.bs-nav-banner .banner .close:hover { opacity: 1; background: rgba(255,255,255,.15); }
.bs-nav-banner .bar { display: flex; align-items: center; gap: 28px; height: 66px;
  border-bottom: 1px solid var(--line); }
.bs-nav-banner .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--fg); }
.bs-nav-banner .logo svg { color: var(--brand); }
.bs-nav-banner .links { display: flex; gap: 26px; margin: 0 auto; }
.bs-nav-banner .links a { font-size: 15px; font-weight: 600; color: var(--fg-soft); transition: color .2s; }
.bs-nav-banner .links a:hover { color: var(--fg); }
.bs-nav-banner .bs-nav-mobile { border-bottom: 1px solid var(--line); }
@media (max-width: 860px) {
  .bs.bs-nav-banner .links, .bs.bs-nav-banner .actions { display: none; }
  .bs.bs-nav-banner .bs-nav-burger { display: inline-flex; }
}

/* ---------- Nav 4 · Mega-Dropdown ---------- */
.bs.bs-nav-mega { position: sticky; top: 0; z-index: 60;
  background: transparent; border-bottom: 1px solid transparent;
  transition: background-color .25s, border-color .25s, box-shadow .25s; }
.bs.bs-nav-mega.scrolled { background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--line); box-shadow: 0 12px 34px -20px rgba(20,16,31,.35); }
.bs-nav-mega .inner { display: flex; align-items: center; gap: 28px; height: 68px; }
.bs-nav-mega .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--fg); }
.bs-nav-mega .logo svg { color: var(--brand); }
.bs-nav-mega .links { display: flex; align-items: center; gap: 26px; margin: 0 auto; }
.bs-nav-mega .links a { font-size: 15px; font-weight: 600; color: var(--fg-soft); transition: color .2s; }
.bs-nav-mega .links a:hover { color: var(--fg); }
.bs-nav-mega .mega-trigger { display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 15px; font-weight: 600; color: var(--fg-soft);
  background: none; border: 0; padding: 0; cursor: pointer; transition: color .2s; }
.bs-nav-mega .mega-trigger:hover, .bs-nav-mega .mega-trigger[aria-expanded="true"] { color: var(--fg); }
.bs-nav-mega .mega-trigger svg { transition: transform .2s; }
.bs-nav-mega .mega-trigger[aria-expanded="true"] svg { transform: rotate(180deg); }
/* Panel: Karte unter der Leiste (Header ist sticky = positioniert) */
.bs-nav-mega .mega-panel { position: absolute; top: calc(100% + 8px); left: 50%;
  width: min(720px, calc(100vw - 32px));
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow); padding: 18px;
  opacity: 0; visibility: hidden; transform: translateX(-50%) translateY(8px);
  transition: opacity .22s ease, transform .22s ease, visibility 0s linear .22s; }
.bs-nav-mega .mega-panel.open { opacity: 1; visibility: visible;
  transform: translateX(-50%); transition: opacity .22s ease, transform .22s ease; }
.bs-nav-mega .mega-grid { display: grid; grid-template-columns: 1.3fr 1fr; gap: 18px; }
.bs-nav-mega .mega-cards { display: grid; gap: 6px; align-content: start; }
.bs-nav-mega .mega-card { display: flex; gap: 14px; padding: 12px; border-radius: var(--r-sm);
  transition: background-color .15s; }
.bs-nav-mega .mega-card:hover { background: var(--glass); }
.bs-nav-mega .mega-card .ico { flex: none; display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: 12px; color: var(--brand); background: rgba(var(--brand-rgb), .1); }
.bs-nav-mega .mega-card:nth-child(2) .ico { color: var(--violet); background: rgba(var(--violet-rgb), .1); }
.bs-nav-mega .mega-card:nth-child(3) .ico { color: var(--teal); background: color-mix(in srgb, var(--teal) 12%, transparent); }
.bs-nav-mega .mega-card strong { display: block; font-size: 15px; color: var(--fg); }
.bs-nav-mega .mega-card small { display: block; font-size: 13.5px; line-height: 1.45; color: var(--fg-muted); }
.bs-nav-mega .mega-side { border-left: 1px solid var(--line); padding-left: 18px; }
.bs-nav-mega .mega-head { font-size: 11.5px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-muted); margin: 6px 0 8px; }
.bs-nav-mega .mega-side a { display: flex; align-items: center; gap: 8px; padding: 7px 0;
  font-size: 14.5px; font-weight: 600; color: var(--fg-soft); transition: color .15s; }
.bs-nav-mega .mega-side a:hover { color: var(--brand); }
.bs-nav-mega .mega-side a svg { color: var(--fg-muted); }
.bs-nav-mega .bs-nav-mobile { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
@media (max-width: 860px) {
  .bs.bs-nav-mega .links, .bs.bs-nav-mega .actions, .bs.bs-nav-mega .mega-panel { display: none; }
  .bs.bs-nav-mega .bs-nav-burger { display: inline-flex; }
}

/* ---------- Nav 5 · Transparent → fest ---------- */
.bs.bs-nav-transparent { position: sticky; top: 0; z-index: 60;
  background: transparent; border-bottom: 1px solid transparent;
  transition: background-color .3s, border-color .3s, box-shadow .3s; }
.bs.bs-nav-transparent.scrolled { background: color-mix(in srgb, var(--bg) 76%, transparent);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border-bottom-color: var(--line); box-shadow: 0 14px 40px -20px rgba(0,0,0,.6); }
.bs-nav-transparent .inner { display: flex; align-items: center; gap: 28px; height: 68px; }
.bs-nav-transparent .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--fg); }
.bs-nav-transparent .logo svg { color: var(--orange); }
.bs-nav-transparent .links { display: flex; gap: 26px; margin: 0 auto; }
.bs-nav-transparent .links a { font-size: 15px; font-weight: 600; color: var(--fg-soft); transition: color .2s; }
.bs-nav-transparent .links a:hover { color: var(--fg); }
.bs-nav-transparent .bs-nav-mobile { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
@media (max-width: 860px) {
  .bs.bs-nav-transparent .links, .bs.bs-nav-transparent .actions { display: none; }
  .bs.bs-nav-transparent .bs-nav-burger { display: inline-flex; }
}

/* ---------- Nav 6 · Zentriertes Logo ---------- */
.bs.bs-nav-center { position: sticky; top: 0; z-index: 60;
  background: var(--bg); border-bottom: 1px solid var(--line); }
/* zweite Linie der Doppellinie: 1px Abstand unter dem Border */
.bs.bs-nav-center::after { content: ""; position: absolute; left: 0; right: 0;
  bottom: -3px; height: 1px; background: var(--line); }
.bs-nav-center .inner { display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; column-gap: 34px; padding-block: 26px;
  transition: padding .3s ease; }
.bs.bs-nav-center.scrolled .inner { padding-block: 10px; }
.bs-nav-center .brand { font-family: var(--font-display); font-weight: 800;
  font-size: 24px; letter-spacing: .16em; text-transform: uppercase;
  text-align: center; color: var(--fg); transition: font-size .3s ease; }
.bs.bs-nav-center.scrolled .brand { font-size: 19px; }
.bs-nav-center .side { display: flex; gap: 26px; }
.bs-nav-center .side.left { justify-content: flex-end; }
.bs-nav-center .side a { font-size: 13px; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--fg-soft); transition: color .2s; }
.bs-nav-center .side a:hover { color: var(--fg); }
.bs-nav-center .bs-nav-burger { position: absolute; right: 14px; top: 50%;
  transform: translateY(-50%); margin-left: 0; }
.bs-nav-center .bs-nav-mobile { border-top: 1px solid var(--line); }
@media (max-width: 760px) {
  .bs-nav-center .side { display: none; }
  .bs-nav-center .inner { grid-template-columns: 1fr; padding-block: 18px; }
  .bs-nav-center .brand { font-size: 20px; }
  .bs.bs-nav-center .bs-nav-burger { display: inline-flex; }
}

/* ---------- Nav 7 · Fullscreen-Overlay ----------
   Bewusst KEIN backdrop-filter auf der Leiste — der würde das
   position:fixed-Overlay im Header einsperren. */
.bs.bs-nav-overlay { position: sticky; top: 0; z-index: 60;
  background: var(--bg); border-bottom: 1px solid transparent;
  transition: border-color .25s, box-shadow .25s; }
.bs.bs-nav-overlay.scrolled { border-bottom-color: var(--line);
  box-shadow: 0 12px 34px -18px rgba(0,0,0,.55); }
.bs-nav-overlay .inner { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; height: 64px; }
.bs-nav-overlay .brand { font-family: var(--font-display); font-weight: 800;
  font-size: 19px; color: var(--fg); }
.bs-nav-overlay .brand em { font-style: normal; color: var(--brand-300); }
.bs-nav-overlay .right { display: flex; align-items: center; gap: 12px; }
.bs-nav-overlay .ov-open, .bs-nav-overlay .ov-close { display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 12px; background: var(--glass);
  border: 1px solid var(--line); color: var(--fg); cursor: pointer; transition: border-color .2s; }
.bs-nav-overlay .ov-open:hover, .bs-nav-overlay .ov-close:hover { border-color: var(--fg-muted); }
.bs-nav-overlay .ov-panel { position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column; padding: 11px 0 30px; background: var(--bg);
  opacity: 0; visibility: hidden;
  transition: opacity .3s ease, visibility 0s linear .45s; }
.bs-nav-overlay .ov-panel.open { opacity: 1; visibility: visible; transition: opacity .3s ease; }
.bs-nav-overlay .ov-top { display: flex; align-items: center; justify-content: space-between;
  min-height: 42px; }
.bs-nav-overlay .menu { margin-block: auto; display: flex; flex-direction: column; }
.bs-nav-overlay .menu a { display: flex; align-items: baseline; gap: 18px; padding: 9px 0;
  font-family: var(--font-display); font-weight: 800; letter-spacing: -.02em; line-height: 1.05;
  font-size: clamp(34px, 6.5vw, 56px); color: var(--fg);
  opacity: 0; transform: translateY(38px);
  transition: opacity .5s ease, transform .5s cubic-bezier(.2,.7,.3,1), color .2s; }
.bs-nav-overlay .menu a small { font-family: var(--font-body); font-size: 13px; font-weight: 700;
  letter-spacing: .1em; color: var(--fg-muted); }
.bs-nav-overlay .menu a:hover { color: var(--brand-300); }
.bs-nav-overlay .ov-panel.open .menu a { opacity: 1; transform: none; }
.bs-nav-overlay .ov-panel.open .menu a:nth-child(1) { transition-delay: .05s; }
.bs-nav-overlay .ov-panel.open .menu a:nth-child(2) { transition-delay: .11s; }
.bs-nav-overlay .ov-panel.open .menu a:nth-child(3) { transition-delay: .17s; }
.bs-nav-overlay .ov-panel.open .menu a:nth-child(4) { transition-delay: .23s; }
.bs-nav-overlay .ov-panel.open .menu a:nth-child(5) { transition-delay: .29s; }
.bs-nav-overlay .ov-social { display: flex; flex-wrap: wrap; gap: 22px;
  border-top: 1px solid var(--line); padding-top: 18px; }
.bs-nav-overlay .ov-social a { font-size: 13px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-muted); transition: color .2s; }
.bs-nav-overlay .ov-social a:hover { color: var(--fg); }
@media (max-width: 480px) { .bs-nav-overlay .right .bs-btn { display: none; } }

/* ---------- Nav 8 · Scroll-Progress + Scrollspy ---------- */
.bs.bs-nav-progress { position: sticky; top: 0; z-index: 60;
  background: var(--bg); border-bottom: 1px solid var(--line);
  transition: box-shadow .25s; }
.bs.bs-nav-progress.scrolled { box-shadow: 0 12px 34px -20px rgba(20,16,31,.35); }
.bs-nav-progress .inner { display: flex; align-items: center; gap: 28px; height: 64px; }
.bs-nav-progress .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--fg); }
.bs-nav-progress .logo svg { color: var(--green); }
.bs-nav-progress .links { display: flex; gap: 24px; margin-left: auto; }
.bs-nav-progress .links a { font-size: 15px; font-weight: 600; color: var(--fg-soft);
  padding: 4px 0; border-bottom: 2px solid transparent;
  transition: color .2s, border-color .2s; }
.bs-nav-progress .links a:hover { color: var(--fg); }
.bs-nav-progress .links a.active { color: var(--brand); border-bottom-color: var(--brand); }
/* Fortschrittsbalken an der Unterkante (überdeckt den Border) */
.bs-nav-progress .progress { position: absolute; left: 0; bottom: -1px;
  width: 0; height: 3px; background: var(--brand); border-radius: 0 2px 2px 0; }
@media (max-width: 640px) {
  .bs-nav-progress .inner { gap: 14px; }
  .bs-nav-progress .links { gap: 14px; }
  .bs-nav-progress .links a { font-size: 13.5px; }
  .bs-nav-progress .logo { font-size: 17px; }
}


/* ===== landing_hero.css ===== */
/* =================================================================
   LANDING · HERO — Stile für die Hero-Bausteine.

   · .bs-hero-split     Split mit Produkt-Visual (dunkel)
   · .bs-hero-aurora    Aurora zentriert (dunkel, Verlaufs-Headline)
   · .bs-hero-minimal   Hell & minimal (Typo-Bühne)
   · .bs-hero-browser   Browser-Mockup zentriert (hell)
   · .bs-hero-terminal  Developer / Terminal (dunkel)

   Wiederverwendbares Chrome:
   · .bs-logo-marquee    Logo-Marquee-Zeile ("Im Einsatz bei Teams von …")
   · .bs-browser-frame   Browser-Fenster-Rahmen
   · .bs-terminal-frame  Terminal-Fenster-Rahmen

   Alles unter .bs gescoped — Flächen-Tokens kommen aus landing_base.css.
   ================================================================= */

/* ---------- Hero 1 · Split mit Produkt-Visual ---------- */
.bs.bs-hero-split { position: relative; overflow: hidden; }
.bs.bs-hero-split::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(70% 90% at 85% 0%, rgba(var(--brand-rgb), .30), transparent 60%),
    radial-gradient(50% 70% at 5% 100%, rgba(var(--violet-rgb), .22), transparent 60%);
}
.bs-hero-split .bs-dots { position: absolute; inset: 0; pointer-events: none;
  mask-image: radial-gradient(80% 80% at 70% 20%, #000 30%, transparent 75%);
  -webkit-mask-image: radial-gradient(80% 80% at 70% 20%, #000 30%, transparent 75%); }
.bs-hero-split .inner { position: relative; display: grid; grid-template-columns: 1.05fr 1fr;
  gap: 48px; align-items: center; padding-top: 96px; padding-bottom: 88px; }
.bs-hero-split h1 { font-size: clamp(38px, 4.8vw, 60px); font-weight: 800; margin: 18px 0 20px; }
.bs-hero-split .sub { font-size: 18px; max-width: 46ch; margin-bottom: 30px; }
.bs-hero-split .cta { display: flex; gap: 14px; flex-wrap: wrap; }
.bs-hero-split .note { margin-top: 14px; font-size: 13.5px; color: var(--fg-muted); }

/* Produkt-Mockup (reines CSS — durch echten Screenshot ersetzbar) */
.bs-hero-split .mockup { position: relative; background: #fff; color: #14101f;
  border-radius: var(--r-md); box-shadow: 0 30px 70px -24px rgba(0, 0, 0, .65);
  font-size: 12px; transform: rotate(1.5deg); }
.bs-hero-split .mockup-bar { display: flex; gap: 6px; padding: 12px 14px; border-bottom: 1px solid #e9e7f0; }
.bs-hero-split .mockup-bar i { width: 10px; height: 10px; border-radius: 50%; background: #e9e7f0; }
.bs-hero-split .mockup-bar i:first-child { background: var(--red); }
.bs-hero-split .mockup-body { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; padding: 18px; }
.bs-hero-split .panel { border: 1px solid #e9e7f0; border-radius: 10px; padding: 12px; }
.bs-hero-split .panel b { display: block; font-size: 11px; margin-bottom: 8px; }
.bs-hero-split .bars { display: flex; align-items: flex-end; gap: 5px; height: 58px; }
.bs-hero-split .bars span { flex: 1; border-radius: 3px 3px 0 0; background: var(--brand); opacity: .85; }
.bs-hero-split .bars span:nth-child(2n) { background: var(--violet); }
.bs-hero-split .bars span:nth-child(3n) { background: color-mix(in srgb, var(--brand) 45%, var(--violet)); }
.bs-hero-split .rows div { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #f1f0f8; color: #4a4658; }
.bs-hero-split .badge-float { position: absolute; right: -14px; top: 20%;
  background: #fff; color: #14101f; font-weight: 700; font-size: 12.5px;
  border: 1px solid #e9e7f0; border-radius: 999px; padding: 8px 14px;
  box-shadow: 0 14px 34px -14px rgba(0, 0, 0, .5); }
.bs-hero-split .badge-float i { color: var(--green); font-style: normal; }

/* Trust-Zeile unter dem Hero */
.bs-hero-split .trust { position: relative; padding: 0 0 56px; }

@media (max-width: 900px) {
  .bs-hero-split .inner { grid-template-columns: 1fr; padding-top: 64px; }
  .bs-hero-split .mockup { transform: none; }
  .bs-hero-split .badge-float { right: 8px; }
}

/* ---------- Logo-Marquee (wiederverwendbar) ---------- */
.bs .bs-logo-marquee p { font-size: 13px; color: var(--fg-muted); margin-bottom: 16px; }
.bs .bs-logo-marquee .marquee { overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
.bs .bs-logo-marquee .marquee-track { display: flex; gap: 52px; width: max-content; animation: bsMarquee 26s linear infinite; }
.bs .bs-logo-marquee .marquee-track span { font-family: var(--font-display); font-weight: 700; font-size: 19px; color: var(--fg-muted); white-space: nowrap; }
@keyframes bsMarquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .bs .bs-logo-marquee .marquee-track { animation: none; }
}

/* ---------- Hero 2 · Aurora zentriert ---------- */
.bs.bs-hero-aurora { position: relative; overflow: hidden; text-align: center; }
.bs.bs-hero-aurora::before {
  content: ""; position: absolute; inset: -25%; pointer-events: none;
  background:
    radial-gradient(30% 40% at 30% 35%, rgba(var(--violet-rgb), .4), transparent 80%),
    radial-gradient(26% 36% at 70% 55%, rgba(var(--brand-rgb), .36), transparent 80%),
    radial-gradient(22% 32% at 52% 80%, rgba(var(--brand-rgb), .22), transparent 80%),
    radial-gradient(18% 28% at 80% 25%, rgba(var(--violet-rgb), .16), transparent 80%);
  filter: blur(18px); animation: bsAurora 20s ease-in-out infinite alternate;
}
@keyframes bsAurora {
  from { transform: translate3d(-3%, -2%, 0) rotate(0deg) scale(1); }
  to   { transform: translate3d(3%, 3%, 0) rotate(5deg) scale(1.07); }
}
@media (prefers-reduced-motion: reduce) {
  .bs.bs-hero-aurora::before { animation: none; }
}
.bs-hero-aurora .inner { position: relative; padding-top: 110px; padding-bottom: 96px; }
.bs-hero-aurora h1 { font-size: clamp(42px, 6vw, 76px); font-weight: 800; max-width: 17ch; margin: 0 auto 22px; }
.bs-hero-aurora h1 em { font-style: normal;
  background: linear-gradient(92deg, var(--brand-300), color-mix(in srgb, var(--violet) 55%, #fff) 50%, color-mix(in srgb, var(--brand) 35%, #fff));
  -webkit-background-clip: text; background-clip: text; color: transparent; }
.bs-hero-aurora .sub { font-size: 19px; max-width: 54ch; margin: 0 auto 34px; }
.bs-hero-aurora .cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.bs-hero-aurora .stats { display: flex; gap: clamp(28px, 6vw, 72px); justify-content: center; flex-wrap: wrap;
  margin-top: 64px; padding-top: 34px; border-top: 1px solid var(--line); }
.bs-hero-aurora .stats b { display: block; font-family: var(--font-display); font-size: clamp(28px, 3vw, 40px); font-weight: 800; color: var(--fg); }
.bs-hero-aurora .stats span { font-size: 14px; color: var(--fg-muted); }

/* ---------- Hero 3 · Hell & minimal ---------- */
.bs.bs-hero-minimal { position: relative; overflow: hidden; }
.bs.bs-hero-minimal::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 90% at 80% -20%, rgba(var(--brand-rgb), .08), transparent 60%); }
.bs-hero-minimal .inner { position: relative; padding-top: 110px; padding-bottom: 90px; max-width: 880px; }
.bs-hero-minimal h1 { font-size: clamp(44px, 6.4vw, 84px); font-weight: 800; letter-spacing: -.03em; margin-bottom: 26px; }
.bs-hero-minimal h1 .bs-underline { position: relative; white-space: nowrap; text-decoration: none; }
.bs-hero-minimal h1 .bs-underline svg { position: absolute; left: 0; bottom: -.14em; width: 100%; height: .3em; overflow: visible; }
.bs-hero-minimal h1 .bs-underline path { fill: none; stroke: var(--brand); stroke-width: 6; stroke-linecap: round;
  stroke-dasharray: 420; stroke-dashoffset: 420; animation: bsDraw 1s ease .6s forwards; }
@keyframes bsDraw { to { stroke-dashoffset: 0; } }
.bs-hero-minimal .sub { font-size: clamp(18px, 2vw, 21px); max-width: 52ch; margin-bottom: 34px; }
.bs-hero-minimal .cta { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.bs-hero-minimal .avatars { display: flex; align-items: center; gap: 12px; margin-top: 40px; }
.bs-hero-minimal .avatars .stack { display: flex; }
.bs-hero-minimal .avatars .stack i { width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--bg);
  margin-left: -10px; font-style: normal; display: grid; place-items: center;
  font-size: 12px; font-weight: 700; color: #fff; }
.bs-hero-minimal .avatars .stack i:first-child { margin-left: 0; }
.bs-hero-minimal .avatars small { font-size: 13.5px; color: var(--fg-muted); }

/* ---------- Hero 4 · Browser-Mockup zentriert ---------- */
.bs.bs-hero-browser { position: relative; overflow: hidden; text-align: center;
  background: linear-gradient(180deg, var(--bg) 0%, var(--bg-soft) 100%); }
.bs-hero-browser .inner { position: relative; padding-top: 100px; }
.bs-hero-browser h1 { font-size: clamp(38px, 5vw, 64px); font-weight: 800; max-width: 20ch; margin: 16px auto 20px; }
.bs-hero-browser .sub { font-size: 18.5px; max-width: 52ch; margin: 0 auto 32px; }
.bs-hero-browser .cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 64px; }

/* ---------- Browser-Fenster-Rahmen (wiederverwendbar) ---------- */
.bs .bs-browser-frame { max-width: 960px; margin: 0 auto; text-align: left;
  background: #fff; color: #14101f; border: 1px solid var(--line); border-radius: 18px 18px 0 0;
  box-shadow: 0 -10px 80px -20px rgba(var(--brand-rgb), .35), 0 1px 2px rgba(20, 16, 31, .06); overflow: hidden; }
.bs .bs-browser-frame .browser-bar { display: flex; align-items: center; gap: 8px; padding: 13px 16px; border-bottom: 1px solid #e9e7f0; }
.bs .bs-browser-frame .browser-bar i { width: 11px; height: 11px; border-radius: 50%; background: #e9e7f0; }
.bs .bs-browser-frame .browser-bar i:nth-child(1) { background: #ef5b6e; }
.bs .bs-browser-frame .browser-bar i:nth-child(2) { background: #ffce4a; }
.bs .bs-browser-frame .browser-bar i:nth-child(3) { background: #25c26e; }
.bs .bs-browser-frame .browser-bar .url { flex: 1; max-width: 380px; margin: 0 auto; text-align: center;
  background: #f7f6fb; border-radius: 8px; font-size: 12.5px; color: #8b8798; padding: 5px 12px; }
.bs .bs-browser-frame .browser-body { display: grid; grid-template-columns: 200px 1fr; min-height: 300px; }
.bs .bs-browser-frame .side { border-right: 1px solid #f1f0f8; padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.bs .bs-browser-frame .side span { height: 12px; border-radius: 6px; background: #f1f0f8; }
.bs .bs-browser-frame .side span.on { background: rgba(var(--brand-rgb), .18); }
.bs .bs-browser-frame .canvas { padding: 22px; display: flex; flex-direction: column; gap: 14px; }
.bs .bs-browser-frame .canvas .block { border-radius: 10px; background: #f7f6fb; height: 64px; }
.bs .bs-browser-frame .canvas .block.tall { flex: 1; background:
  radial-gradient(40% 70% at 30% 40%, rgba(var(--brand-rgb), .14), transparent 70%),
  radial-gradient(35% 60% at 75% 60%, rgba(var(--violet-rgb), .12), transparent 70%), #f7f6fb; }
@media (max-width: 720px) {
  .bs .bs-browser-frame .browser-body { grid-template-columns: 1fr; }
  .bs .bs-browser-frame .side { display: none; }
}

/* ---------- Hero 5 · Developer / Terminal ---------- */
.bs.bs-hero-terminal { position: relative; overflow: hidden; }
.bs.bs-hero-terminal::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(55% 80% at 90% 10%, rgba(var(--brand-rgb), .16), transparent 60%),
              radial-gradient(45% 70% at 0% 100%, rgba(var(--brand-rgb), .2), transparent 60%); }
.bs-hero-terminal .inner { position: relative; display: grid; grid-template-columns: 1fr 1fr;
  gap: 56px; align-items: center; padding-top: 96px; padding-bottom: 96px; }
.bs-hero-terminal h1 { font-size: clamp(36px, 4.6vw, 58px); font-weight: 800; margin: 16px 0 20px; }
.bs-hero-terminal .sub { font-size: 18px; max-width: 44ch; margin-bottom: 30px; }
.bs-hero-terminal .cta { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.bs-hero-terminal .install { display: inline-flex; align-items: center; gap: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px; color: var(--fg);
  background: var(--glass); border: 1px solid var(--line); border-radius: 12px; padding: 12px 18px; }
.bs-hero-terminal .install b { color: var(--brand-300); font-weight: 400; }
@media (max-width: 900px) { .bs-hero-terminal .inner { grid-template-columns: 1fr; } }

/* ---------- Terminal-Fenster-Rahmen (wiederverwendbar) ---------- */
.bs .bs-terminal-frame { background: #0a0823; border: 1px solid rgba(255, 255, 255, .14); border-radius: 14px;
  box-shadow: 0 30px 70px -24px rgba(0, 0, 0, .7); overflow: hidden;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13.5px; line-height: 1.75; }
.bs .bs-terminal-frame .term-bar { display: flex; gap: 6px; padding: 12px 14px; border-bottom: 1px solid rgba(255, 255, 255, .1); }
.bs .bs-terminal-frame .term-bar i { width: 10px; height: 10px; border-radius: 50%; background: rgba(255, 255, 255, .15); }
.bs .bs-terminal-frame .term-bar i:nth-child(1) { background: #ef5b6e; }
.bs .bs-terminal-frame .term-bar i:nth-child(2) { background: #ffce4a; }
.bs .bs-terminal-frame .term-bar i:nth-child(3) { background: #25c26e; }
.bs .bs-terminal-frame .term-body { padding: 18px 20px 24px; color: #b9c2ff; }
.bs .bs-terminal-frame .term-body .ln { opacity: 0; animation: bsTermLn .01s forwards; }
.bs .bs-terminal-frame .term-body .ln:nth-child(1) { animation-delay: .4s; }
.bs .bs-terminal-frame .term-body .ln:nth-child(2) { animation-delay: 1.1s; }
.bs .bs-terminal-frame .term-body .ln:nth-child(3) { animation-delay: 1.7s; }
.bs .bs-terminal-frame .term-body .ln:nth-child(4) { animation-delay: 2.3s; }
.bs .bs-terminal-frame .term-body .ln:nth-child(5) { animation-delay: 3s; }
@keyframes bsTermLn { to { opacity: 1; } }
.bs .bs-terminal-frame .term-body .p { color: var(--brand-300); }
.bs .bs-terminal-frame .term-body .ok { color: #6ee7a8; }
.bs .bs-terminal-frame .term-body .dim { color: rgba(185, 194, 255, .55); }
.bs .bs-terminal-frame .cursor { display: inline-block; width: 8px; height: 15px; background: var(--brand-300);
  vertical-align: -2px; animation: bsBlink 1s steps(1) infinite; }
@keyframes bsBlink { 50% { opacity: 0; } }


/* ===== landing_features.css ===== */
/* =================================================================
   LANDING — Feature-Bausteine
   · .bs-feat-grid   Karten-Grid mit Pastell-Icon-Kacheln
   · .bs-feat-split  Abwechselnder Text/Visual-Split mit Produkt-Panel
   · .bs-feat-bento  Bento-Grid aus Glas-Karten (dunkel)
   · .bs-feat-stats  Zahlen-Band mit animierten Kennzahlen
   Alle Regeln setzen den .bs-Scope aus landing_base.css voraus.
   ================================================================= */

/* ---------- Features 1 · Karten-Grid ---------- */
.bs-feat-grid .inner { padding-top: 96px; padding-bottom: 96px; }
.bs-feat-grid .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.bs-feat-grid .card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 28px 26px; transition: transform .2s ease, box-shadow .25s ease, border-color .2s;
}
.bs-feat-grid .card:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: transparent; }
.bs-feat-grid .icon {
  width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center; margin-bottom: 18px;
  background: color-mix(in srgb, var(--tint, var(--brand)) 12%, transparent);
  color: var(--tint, var(--brand));
}
.bs-feat-grid .icon svg { width: 22px; height: 22px; }
.bs-feat-grid h3 { font-size: 19px; font-weight: 700; margin-bottom: 8px; }
.bs-feat-grid p.txt { font-size: 15.5px; color: var(--fg-soft); }
@media (max-width: 900px) { .bs-feat-grid .grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .bs-feat-grid .grid { grid-template-columns: 1fr; } }

/* ---------- Features 2 · Abwechselnder Split ---------- */
.bs-feat-split .inner { padding-top: 96px; padding-bottom: 96px; display: grid; gap: 96px; }
.bs-feat-split .row { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center; }
.bs-feat-split .row.flip .copy { order: 2; }
.bs-feat-split h3 { font-size: clamp(26px, 3vw, 36px); font-weight: 800; margin-bottom: 14px; }
.bs-feat-split .copy p.txt { font-size: 17px; margin-bottom: 22px; max-width: 46ch; }
.bs-feat-split .checks { display: grid; gap: 12px; }
.bs-feat-split .checks li { display: flex; gap: 11px; align-items: flex-start; font-size: 15.5px; color: var(--fg); font-weight: 500; }
.bs-feat-split .checks svg { width: 19px; height: 19px; flex: none; margin-top: 3px; color: var(--green); }

/* Produkt-Panel (reines CSS — Vorbild: .mockup aus dem Hero-Baustein) */
.bs-feat-split .panelbox { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow); overflow: hidden; font-size: 12px; }
.bs-feat-split .panelbox-bar { display: flex; align-items: center; gap: 6px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.bs-feat-split .panelbox-bar i { width: 10px; height: 10px; border-radius: 50%; background: var(--line); }
.bs-feat-split .panelbox-bar b { margin-left: 8px; font-size: 11.5px; color: var(--fg-muted); font-weight: 600; }
.bs-feat-split .panelbox-body { padding: 18px; display: grid; gap: 12px; }
.bs-feat-split .kpis { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
.bs-feat-split .kpi { border: 1px solid var(--line); border-radius: 10px; padding: 12px; }
.bs-feat-split .kpi span { display: block; font-size: 10.5px; color: var(--fg-muted); }
.bs-feat-split .kpi b { font-family: var(--font-display); font-size: 18px; color: var(--fg); }
.bs-feat-split .kpi em { font-style: normal; font-size: 10.5px; font-weight: 700; color: var(--green); }
.bs-feat-split .wave { border: 1px solid var(--line); border-radius: 10px; padding: 12px; }
.bs-feat-split .wave b { display: block; font-size: 11px; color: var(--fg); margin-bottom: 8px; }
.bs-feat-split .bars { display: flex; align-items: flex-end; gap: 5px; height: 64px; }
.bs-feat-split .bars span { flex: 1; border-radius: 3px 3px 0 0; background: var(--brand); opacity: .85; }
.bs-feat-split .bars span:nth-child(2n) { background: var(--violet); }
.bs-feat-split .bars span:nth-child(3n) { background: var(--teal); }
.bs-feat-split .steps { display: grid; gap: 8px; }
.bs-feat-split .step { display: flex; align-items: center; gap: 10px; border: 1px solid var(--line);
  border-radius: 10px; padding: 10px 12px; color: var(--fg-soft); }
.bs-feat-split .step i { width: 22px; height: 22px; flex: none; border-radius: 50%; display: grid; place-items: center;
  font-style: normal; font-size: 11px; font-weight: 700; color: var(--brand);
  background: color-mix(in srgb, var(--brand) 12%, transparent); }
.bs-feat-split .step b { margin-left: auto; font-size: 11px; color: var(--fg); }
.bs-feat-split .step.done i { color: var(--green); background: color-mix(in srgb, var(--green) 14%, transparent); }
@media (max-width: 900px) {
  .bs-feat-split .row, .bs-feat-split .row.flip { grid-template-columns: 1fr; gap: 36px; }
  .bs-feat-split .row.flip .copy { order: 0; }
  .bs-feat-split .inner { gap: 72px; }
}

/* ---------- Features 3 · Bento-Grid ---------- */
.bs.bs-feat-bento { position: relative; overflow: hidden; }
.bs.bs-feat-bento::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(55% 70% at 15% 0%, rgba(var(--violet-rgb), .22), transparent 60%),
              radial-gradient(45% 60% at 90% 100%, rgba(24, 184, 166, .14), transparent 60%); }
.bs-feat-bento .inner { position: relative; padding-top: 96px; padding-bottom: 96px; }
.bs-feat-bento .bento { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(150px, auto);
  gap: 18px; }
.bs-feat-bento .cell { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 26px; display: flex; flex-direction: column; gap: 10px;
  transition: transform .22s ease, border-color .22s, background-color .22s; }
.bs-feat-bento .cell:hover { transform: translateY(-5px); border-color: var(--fg-muted); background: var(--glass); }
.bs-feat-bento .cell.big  { grid-column: span 2; grid-row: span 2; }
.bs-feat-bento .cell.wide { grid-column: span 2; }
.bs-feat-bento .cell svg.ico { width: 24px; height: 24px; color: var(--brand-300); }
.bs-feat-bento h3 { font-size: 19px; font-weight: 700; }
.bs-feat-bento .cell.big h3 { font-size: 24px; }
.bs-feat-bento p.txt { font-size: 15px; color: var(--fg-soft); }
.bs-feat-bento .chart { margin-top: auto; border: 1px solid var(--line); border-radius: 14px;
  background: var(--glass); padding: 16px 16px 14px; }
.bs-feat-bento .chart small { display: block; font-size: 11.5px; color: var(--fg-muted); margin-bottom: 10px; }
.bs-feat-bento .chart .bars { display: flex; align-items: flex-end; gap: 7px; height: 92px; }
.bs-feat-bento .chart .bars span { flex: 1; border-radius: 4px 4px 0 0; background: var(--brand); opacity: .9; }
.bs-feat-bento .chart .bars span:nth-child(2n) { background: var(--violet); }
.bs-feat-bento .chart .bars span:nth-child(3n) { background: var(--teal); }
.bs-feat-bento .pill { align-self: flex-start; font-size: 12px; font-weight: 700; color: var(--fg);
  border: 1px solid var(--line); background: var(--glass); border-radius: 999px; padding: 4px 12px; }
@media (max-width: 900px) {
  .bs-feat-bento .bento { grid-template-columns: 1fr 1fr; }
  .bs-feat-bento .cell.big { grid-column: span 2; }
}
@media (max-width: 620px) {
  .bs-feat-bento .bento { grid-template-columns: 1fr; }
  .bs-feat-bento .cell.big, .bs-feat-bento .cell.wide { grid-column: span 1; grid-row: span 1; }
}

/* ---------- Features 4 · Zahlen-Band ---------- */
.bs-feat-stats .inner { padding-top: 64px; padding-bottom: 64px; }
.bs-feat-stats .band { display: grid; grid-template-columns: repeat(4, 1fr); }
.bs-feat-stats .stat { text-align: center; padding: 10px 18px; border-left: 1px solid var(--line); }
.bs-feat-stats .stat:first-child { border-left: none; }
.bs-feat-stats .stat b { display: block; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(30px, 3.6vw, 46px); color: var(--fg); letter-spacing: -.02em; }
.bs-feat-stats .stat span { font-size: 14px; color: var(--fg-muted); }
@media (max-width: 720px) {
  .bs-feat-stats .band { grid-template-columns: 1fr 1fr; gap: 28px 0; }
  .bs-feat-stats .stat:nth-child(3) { border-left: none; }
}
@media (max-width: 420px) {
  .bs-feat-stats .band { grid-template-columns: 1fr; }
  .bs-feat-stats .stat { border-left: none; border-top: 1px solid var(--line); padding-top: 24px; }
  .bs-feat-stats .stat:first-child { border-top: none; padding-top: 0; }
}


/* ===== landing_cta.css ===== */
/* =================================================================
   LANDING · CTA — Call-to-Action-Bausteine.

   · .bs-cta-center — zentriertes Band mit Radial-Glows + Punktraster
   · .bs-cta-card   — Verlaufs-Karte (brand→violet) mit E-Mail-Formular
   · .bs-cta-split  — zweispaltig mit Vorteils-Liste

   Benötigt landing_base.css (Tokens, .bs-Scope, Buttons, Reveals) und
   muss NACH landing_base.css geladen werden.
   ================================================================= */

/* ---------- CTA · Zentriertes Band ---------- */
.bs.bs-cta-center { position: relative; overflow: hidden; text-align: center; }
.bs.bs-cta-center::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(50% 80% at 50% 110%, rgba(var(--brand-rgb), .38), transparent 65%),
    radial-gradient(35% 55% at 12% 0%, rgba(var(--violet-rgb), .2), transparent 65%),
    radial-gradient(30% 50% at 88% 8%, rgba(24, 184, 166, .14), transparent 65%);
}
.bs-cta-center .bs-dots { position: absolute; inset: 0; pointer-events: none;
  mask-image: radial-gradient(70% 90% at 50% 50%, #000 20%, transparent 75%);
  -webkit-mask-image: radial-gradient(70% 90% at 50% 50%, #000 20%, transparent 75%); }
.bs-cta-center .inner { position: relative; padding-top: 96px; padding-bottom: 100px; }
.bs-cta-center .bs-eyebrow { margin-bottom: 0; }
.bs-cta-center h2 { font-size: clamp(32px, 4.6vw, 56px); font-weight: 800; max-width: 20ch; margin: 18px auto 18px; }
.bs-cta-center .sub { font-size: 18.5px; max-width: 50ch; margin: 0 auto 34px; }
.bs-cta-center .cta { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.bs-cta-center .note { margin-top: 16px; font-size: 13.5px; color: var(--fg-muted); }

/* ---------- CTA · Verlaufs-Karte mit E-Mail-Feld ---------- */
.bs-cta-card .inner { padding-top: 84px; padding-bottom: 84px; }
.bs-cta-card .card { position: relative; overflow: hidden; text-align: center;
  border-radius: var(--r-xl); padding: clamp(40px, 6vw, 76px) clamp(22px, 5vw, 64px);
  color: rgba(255, 255, 255, .82);
  background: linear-gradient(118deg, var(--brand) 0%, var(--violet) 100%);
  box-shadow: 0 34px 80px -32px rgba(var(--brand-rgb), .6); }
.bs-cta-card .card::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 60% at 50% -20%, rgba(255, 255, 255, .28), transparent 60%); }
.bs-cta-card .card > * { position: relative; }
.bs-cta-card h2 { font-size: clamp(28px, 3.8vw, 46px); font-weight: 800; color: #fff; max-width: 22ch; margin: 0 auto 14px; }
.bs-cta-card .sub { font-size: 17.5px; max-width: 46ch; margin: 0 auto 30px; }
.bs-cta-card form { display: flex; gap: 10px; max-width: 500px; margin: 0 auto; }
.bs-cta-card input { flex: 1; min-width: 0; height: 52px; padding: 0 20px;
  border: 1.5px solid rgba(255, 255, 255, .35); border-radius: 999px;
  background: rgba(255, 255, 255, .12); color: #fff;
  font-family: var(--font-body); font-size: 16px; }
.bs-cta-card input::placeholder { color: rgba(255, 255, 255, .6); }
.bs-cta-card input:focus { outline: 2px solid #fff; outline-offset: 2px; }
.bs-cta-card .btn-light { background: #fff; color: var(--brand-700); border-color: #fff;
  box-shadow: 0 10px 26px -10px rgba(13, 3, 59, .5); }
.bs-cta-card .btn-light:hover { background: rgba(255, 255, 255, .9); }
.bs-cta-card .note { margin-top: 16px; font-size: 13.5px; color: rgba(255, 255, 255, .65); }
@media (max-width: 560px) {
  .bs-cta-card form { flex-direction: column; }
  .bs-cta-card form .bs-btn { width: 100%; }
}

/* ---------- CTA · Zweispaltig mit Vorteils-Liste ---------- */
.bs.bs-cta-split { background: linear-gradient(180deg, var(--bg) 0%, var(--bg-soft) 100%); }
.bs-cta-split .inner { display: grid; grid-template-columns: 1.1fr 1fr; gap: 56px;
  align-items: center; padding-top: 92px; padding-bottom: 92px; }
.bs-cta-split h2 { font-size: clamp(30px, 4vw, 46px); font-weight: 800; margin: 14px 0 16px; }
.bs-cta-split .sub { font-size: 18px; max-width: 46ch; margin-bottom: 30px; }
.bs-cta-split .cta { display: flex; gap: 14px; flex-wrap: wrap; }
.bs-cta-split .checks { display: flex; flex-direction: column; gap: 14px; }
.bs-cta-split .checks li { display: flex; gap: 14px; align-items: flex-start;
  padding: 18px 20px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--card); box-shadow: var(--shadow); }
.bs-cta-split .checks svg { flex: none; margin-top: 2px; color: var(--green); }
.bs-cta-split .checks b { display: block; color: var(--fg); font-size: 16px; margin-bottom: 2px; }
.bs-cta-split .checks p { font-size: 15px; color: var(--fg-soft); }
@media (max-width: 860px) {
  .bs-cta-split .inner { grid-template-columns: 1fr; gap: 40px; padding-top: 64px; padding-bottom: 64px; }
}


/* ===== landing_footer.css ===== */
/* =================================================================
   LANDING — Footer-Bausteine.
   · .bs-footer-cols    Brand + 4 Link-Spalten + untere Zeile (dunkel)
   · .bs-footer-minimal Minimal einzeilig: Logo, Links, Social (hell)
   · .bs-footer-cta     Mini-CTA über dem Spalten-Raster (dunkel)
   Alle Regeln sind über den .bs-Scope aus landing_base.css gescoped.
   ================================================================= */

/* ---------- Footer 1 · Brand + 4 Spalten ---------- */
.bs-footer-cols .inner { display: grid; grid-template-columns: 1.6fr repeat(4, 1fr);
  gap: 44px 32px; padding-top: 76px; padding-bottom: 56px; }
.bs-footer-cols .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 20px; color: var(--fg); }
.bs-footer-cols .logo svg { color: var(--brand-300); }
.bs-footer-cols .brand p { margin: 14px 0 22px; font-size: 15px; max-width: 30ch; color: var(--fg-muted); }
.bs-footer-cols .social { display: flex; gap: 10px; }
.bs-footer-cols .social a { display: grid; place-items: center; width: 38px; height: 38px;
  border: 1px solid var(--line); border-radius: 10px; color: var(--fg-soft);
  transition: color .2s, border-color .2s, transform .15s; }
.bs-footer-cols .social a:hover { color: var(--fg); border-color: var(--fg-muted); transform: translateY(-2px); }
.bs-footer-cols h4 { font-size: 14px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg); margin-bottom: 16px; }
.bs-footer-cols .cols a { display: inline-block; padding: 5px 0; font-size: 15px;
  color: var(--fg-muted); transition: color .2s; }
.bs-footer-cols .cols a:hover { color: var(--fg); }
.bs-footer-cols .bottom { border-top: 1px solid var(--line); }
.bs-footer-cols .bottom .bs-container { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; padding-top: 20px; padding-bottom: 22px;
  font-size: 14px; color: var(--fg-muted); }
.bs-footer-cols .lang { display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 600; color: var(--fg-soft);
  border: 1px solid var(--line); border-radius: 999px; padding: 7px 14px;
  background: none; cursor: pointer; transition: border-color .2s; }
.bs-footer-cols .lang:hover { border-color: var(--fg-muted); }
@media (max-width: 960px) { .bs-footer-cols .inner { grid-template-columns: 1fr 1fr; } .bs-footer-cols .brand { grid-column: 1 / -1; } }
@media (max-width: 480px) { .bs-footer-cols .inner { grid-template-columns: 1fr; gap: 32px; } }

/* ---------- Footer 2 · Minimal einzeilig ---------- */
.bs-footer-minimal .inner { display: flex; align-items: center; gap: 20px 28px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 28px; padding-bottom: 30px; }
.bs-footer-minimal .logo { display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-display); font-weight: 800; font-size: 17px; color: var(--fg); }
.bs-footer-minimal .logo svg { color: var(--teal); }
.bs-footer-minimal .logo small { font-family: var(--font-body); font-weight: 500; font-size: 13.5px; color: var(--fg-muted); }
.bs-footer-minimal .links { display: flex; gap: 22px; flex-wrap: wrap; margin: 0 auto; }
.bs-footer-minimal .links a { font-size: 14.5px; font-weight: 600; color: var(--fg-soft); transition: color .2s; }
.bs-footer-minimal .links a:hover { color: var(--brand); }
.bs-footer-minimal .social { display: flex; gap: 16px; }
.bs-footer-minimal .social a { color: var(--fg-muted); transition: color .2s; }
.bs-footer-minimal .social a:hover { color: var(--fg); }
@media (max-width: 720px) {
  .bs-footer-minimal .inner { flex-direction: column; text-align: center; }
  .bs-footer-minimal .links { justify-content: center; margin: 0; }
}

/* ---------- Footer 3 · Mit Mini-CTA ---------- */
.bs.bs-footer-cta { position: relative; overflow: hidden; }
.bs.bs-footer-cta::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(45% 55% at 50% 0%, rgba(var(--brand-rgb), .28), transparent 65%); }
.bs-footer-cta .cta { position: relative; display: flex; align-items: center; justify-content: space-between;
  gap: 20px 32px; flex-wrap: wrap; padding-top: 64px; padding-bottom: 48px;
  border-bottom: 1px solid var(--line); }
.bs-footer-cta .cta h3 { font-size: clamp(26px, 3.2vw, 38px); font-weight: 800; }
.bs-footer-cta .cta p { margin-top: 8px; font-size: 16.5px; color: var(--fg-soft); max-width: 44ch; }
.bs-footer-cta .grid { position: relative; display: grid; grid-template-columns: 1.4fr repeat(3, 1fr);
  gap: 40px 32px; padding-top: 52px; padding-bottom: 52px; }
.bs-footer-cta .logo { display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-display); font-weight: 800; font-size: 19px; color: var(--fg); }
.bs-footer-cta .logo svg { color: var(--brand-300); }
.bs-footer-cta .brand p { margin-top: 12px; font-size: 14.5px; color: var(--fg-muted); max-width: 28ch; }
.bs-footer-cta h4 { font-size: 14px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg); margin-bottom: 14px; }
.bs-footer-cta .grid a { display: inline-block; padding: 4px 0; font-size: 15px;
  color: var(--fg-muted); transition: color .2s; }
.bs-footer-cta .grid a:hover { color: var(--fg); }
.bs-footer-cta .bottom { position: relative; border-top: 1px solid var(--line); }
.bs-footer-cta .bottom .bs-container { display: flex; align-items: center; justify-content: space-between;
  gap: 12px 24px; flex-wrap: wrap; padding-top: 18px; padding-bottom: 20px;
  font-size: 14px; color: var(--fg-muted); }
.bs-footer-cta .legal { display: flex; gap: 20px; }
.bs-footer-cta .legal a { transition: color .2s; }
.bs-footer-cta .legal a:hover { color: var(--fg); }
@media (max-width: 860px) { .bs-footer-cta .grid { grid-template-columns: 1fr 1fr; } .bs-footer-cta .brand { grid-column: 1 / -1; } }
@media (max-width: 480px) { .bs-footer-cta .grid { grid-template-columns: 1fr; gap: 30px; } }


/* ===== landing_leadgen.css ===== */
/* =================================================================
   LANDING · LEADGEN — Lead-Generierungs-Bausteine.

   · .bs-leadmagnet — Lead-Magnet-Formular / Gated Content (Split)
   · .bs-demoslot   — Demo-Buchung mit Slot-Picker
   · .bs-exitintent — Exit-Intent-Overlay (<dialog>)
   · .bs-stickybar  — Sticky-Conversion-Leiste (IntersectionObserver)
   · .bs-planfinder — Plan-Finder-Quiz mit Empfehlungs-Karte
   · .bs-countdown  — Dringlichkeits-Band mit Countdown

   Benötigt landing_base.css (Tokens, .bs-Scope, Buttons, Formular-
   Fundament, Reveals) und muss NACH landing_base.css geladen werden.
   ================================================================= */

/* ---------- Leadgen · Lead-Magnet (Gated Content) ---------- */
.bs.bs-leadmagnet [hidden] { display: none !important; }
.bs.bs-leadmagnet { position: relative; overflow: hidden; }
.bs.bs-leadmagnet::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(55% 85% at 100% 0%, rgba(var(--brand-rgb), .09), transparent 60%),
              radial-gradient(40% 60% at 0% 100%, rgba(var(--violet-rgb), .07), transparent 60%); }
.bs-leadmagnet .inner { position: relative; display: grid; grid-template-columns: 1.15fr 1fr;
  gap: clamp(32px, 5vw, 64px); align-items: center; padding-top: 88px; padding-bottom: 88px; }
.bs-leadmagnet h2 { font-size: clamp(28px, 3.4vw, 42px); font-weight: 800; margin-bottom: 14px; }
.bs-leadmagnet .sub { font-size: 17.5px; max-width: 50ch; margin-bottom: 34px; }
.bs-leadmagnet .visual { display: flex; gap: clamp(24px, 3vw, 40px); align-items: center; }

/* PDF-Cover-Mockup — reines CSS, durch echtes Cover-Bild ersetzbar */
.bs-leadmagnet .coverwrap { position: relative; flex: none; width: 188px; }
.bs-leadmagnet .coverwrap::before { content: ""; position: absolute; inset: 10px -10px -10px 12px;
  border-radius: 12px; background: var(--card); border: 1px solid var(--line); transform: rotate(4deg); }
.bs-leadmagnet .cover { position: relative; aspect-ratio: 3 / 4.1; border-radius: 12px;
  padding: 18px 16px; display: flex; flex-direction: column; transform: rotate(-3deg);
  background: linear-gradient(155deg, var(--brand) 0%, var(--brand-700) 55%, var(--violet) 125%);
  color: #fff; box-shadow: 0 26px 54px -20px rgba(var(--brand-rgb), .55); }
.bs-leadmagnet .cover .brand { font-size: 11px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; opacity: .85; margin-bottom: 14px; }
.bs-leadmagnet .cover h3 { color: #fff; font-size: 19px; line-height: 1.18; letter-spacing: -.01em; }
.bs-leadmagnet .cover .deco { margin-top: auto; display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.bs-leadmagnet .cover .deco i { height: 5px; border-radius: 3px; background: rgba(255, 255, 255, .35); }
.bs-leadmagnet .cover .meta { font-size: 11.5px; font-weight: 600; opacity: .85; }
.bs-leadmagnet .bullets { display: flex; flex-direction: column; gap: 16px; }
.bs-leadmagnet .bullets li { display: flex; gap: 12px; align-items: flex-start; font-size: 15.5px; }
.bs-leadmagnet .bullets svg { flex: none; margin-top: 4px; color: var(--green); }
.bs-leadmagnet .bullets b { color: var(--fg); }

/* Formular-Karte */
.bs-leadmagnet .formcard { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: clamp(24px, 3vw, 34px); box-shadow: var(--shadow); }
.bs-leadmagnet .formcard h3 { font-size: 22px; margin-bottom: 8px; }
.bs-leadmagnet .formcard .hint { font-size: 14.5px; color: var(--fg-muted); margin-bottom: 22px; }
.bs-leadmagnet .formcard form { display: flex; flex-direction: column; gap: 16px; }
.bs-leadmagnet .formcard .bs-btn { width: 100%; }

@media (max-width: 900px) {
  .bs-leadmagnet .inner { grid-template-columns: 1fr; padding-top: 64px; padding-bottom: 64px; }
}
@media (max-width: 560px) {
  .bs-leadmagnet .visual { flex-direction: column; align-items: flex-start; }
  .bs-leadmagnet .coverwrap { width: 168px; }
}

/* ---------- Leadgen · Demo-Buchung mit Slot-Picker ---------- */
.bs.bs-demoslot [hidden] { display: none !important; }
.bs.bs-demoslot { position: relative; overflow: hidden; }
.bs.bs-demoslot::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 80% at 90% 10%, rgba(var(--brand-rgb), .22), transparent 60%),
              radial-gradient(45% 70% at 0% 100%, rgba(var(--violet-rgb), .16), transparent 60%); }
.bs-demoslot .inner { position: relative; display: grid; grid-template-columns: 1.1fr 1fr;
  gap: clamp(32px, 5vw, 60px); align-items: start; padding-top: 88px; padding-bottom: 88px; }
.bs-demoslot h2 { font-size: clamp(28px, 3.4vw, 42px); font-weight: 800; margin-bottom: 14px; }
.bs-demoslot .sub { font-size: 17.5px; max-width: 48ch; margin-bottom: 30px; }
.bs-demoslot .fakten { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 30px; }
.bs-demoslot .agenda { border-left: 2px solid var(--line); padding-left: 20px; display: flex;
  flex-direction: column; gap: 16px; margin-bottom: 30px; }
.bs-demoslot .agenda li { position: relative; font-size: 15.5px; }
.bs-demoslot .agenda li::before { content: ""; position: absolute; left: -25px; top: 8px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--brand-300); }
.bs-demoslot .agenda b { color: var(--fg); display: block; font-size: 13px; letter-spacing: .06em; }
.bs-demoslot .host { display: flex; align-items: center; gap: 12px; font-size: 14px; color: var(--fg-muted); }
.bs-demoslot .host i { width: 40px; height: 40px; border-radius: 50%; font-style: normal; flex: none;
  display: grid; place-items: center; font-weight: 700; font-size: 13px; color: #fff;
  background: linear-gradient(140deg, var(--brand), var(--violet)); }
.bs-demoslot .host b { color: var(--fg); display: block; font-size: 14.5px; }

/* Slot-Picker-Karte */
.bs-demoslot .picker { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: clamp(22px, 3vw, 30px); box-shadow: var(--shadow); }
.bs-demoslot .picker h3 { font-size: 21px; margin-bottom: 18px; }
.bs-demoslot .gruppe { font-size: 12.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--fg-muted); margin: 16px 0 8px; }
.bs-demoslot .grid { display: grid; gap: 8px; }
.bs-demoslot .day-grid { grid-template-columns: repeat(3, 1fr); }
.bs-demoslot .time-grid { grid-template-columns: repeat(4, 1fr); }
.bs-demoslot .slot { font: 600 14px/1.3 var(--font-body); color: var(--fg);
  background: var(--glass); border: 1.5px solid var(--line); border-radius: 10px;
  padding: 10px 6px; cursor: pointer; transition: border-color .15s, background-color .15s, box-shadow .15s; }
.bs-demoslot .slot:hover { border-color: var(--brand-300); }
.bs-demoslot .slot[aria-pressed="true"] { border-color: var(--brand);
  background: rgba(var(--brand-rgb), .22); box-shadow: 0 0 0 3px rgba(var(--brand-rgb), .16); }
.bs-demoslot .auswahl { font-size: 14px; font-weight: 600; color: var(--fg);
  background: var(--glass); border: 1px dashed var(--line); border-radius: 10px;
  padding: 10px 12px; margin: 18px 0 16px; }
.bs-demoslot .picker form { display: flex; flex-direction: column; gap: 14px; }
.bs-demoslot .picker .bs-btn { width: 100%; }

@media (max-width: 940px) { .bs-demoslot .inner { grid-template-columns: 1fr; padding-top: 64px; padding-bottom: 64px; } }
@media (max-width: 420px) { .bs-demoslot .time-grid { grid-template-columns: repeat(2, 1fr); } }

/* ---------- Leadgen · Exit-Intent-Overlay ---------- */
.bs.bs-exitintent [hidden], .bs-exitintent .bs-exitintent-dialog [hidden] { display: none !important; }
.bs-exitintent .inner { padding-top: 88px; padding-bottom: 88px; }
.bs-exitintent .stage { max-width: 620px; margin: 0 auto; text-align: center;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: clamp(26px, 4vw, 40px); box-shadow: var(--shadow); }
.bs-exitintent .stage-icon { width: 52px; height: 52px; margin: 0 auto 16px; border-radius: 16px;
  display: grid; place-items: center; color: var(--brand);
  background: rgba(var(--brand-rgb), .1); border: 1px solid rgba(var(--brand-rgb), .25); }
.bs-exitintent .ei-status { font-size: 15px; font-weight: 600; color: var(--fg); margin-bottom: 20px; }
.bs-exitintent .stage-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-bottom: 24px; }
.bs-exitintent .facts { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.bs-exitintent .facts li { font-size: 12.5px; font-weight: 600; color: var(--fg-muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 5px 12px; }

/* Das Overlay selbst */
.bs-exitintent .bs-exitintent-dialog { border: none; padding: 0; border-radius: var(--r-lg);
  width: min(440px, calc(100vw - 40px)); background: var(--card); color: var(--fg-soft);
  box-shadow: 0 40px 90px -30px rgba(20, 16, 31, .45); }
.bs-exitintent .bs-exitintent-dialog::backdrop { background: rgba(13, 3, 59, .55); backdrop-filter: blur(3px); }
.bs-exitintent .bs-exitintent-dialog .ei-strip { height: 6px;
  background: linear-gradient(90deg, var(--brand), var(--violet)); }
.bs-exitintent .bs-exitintent-dialog .ei-inner { position: relative; padding: 30px 28px 26px; }
.bs-exitintent .bs-exitintent-dialog .ei-close { position: absolute; top: 14px; right: 14px;
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--line);
  background: var(--glass); color: var(--fg); font-size: 15px; line-height: 1; cursor: pointer; }
.bs-exitintent .bs-exitintent-dialog .ei-close:hover { border-color: var(--fg-muted); }
.bs-exitintent .bs-exitintent-dialog .bs-eyebrow { margin-bottom: 0; }
.bs-exitintent .bs-exitintent-dialog h3 { font-size: 24px; margin: 10px 0 10px; }
.bs-exitintent .bs-exitintent-dialog .ei-text { font-size: 15.5px; margin-bottom: 20px; }
.bs-exitintent .bs-exitintent-dialog form { display: flex; flex-direction: column; gap: 12px; }
.bs-exitintent .bs-exitintent-dialog .bs-btn { width: 100%; }
.bs-exitintent .bs-exitintent-dialog .ei-nein { margin-top: 14px; width: 100%; background: none; border: none;
  font: 600 13.5px var(--font-body); color: var(--fg-muted); text-decoration: underline; cursor: pointer; }
.bs-exitintent .bs-exitintent-dialog .ei-nein:hover { color: var(--fg); }

/* ---------- Leadgen · Sticky-Conversion-Leiste ---------- */
.bs.bs-stickybar .inner { padding-top: 88px; padding-bottom: 88px; }
.bs-stickybar .frame { border: 1.5px dashed var(--line); border-radius: var(--r-lg); padding: 12px; }

/* Sticky-Slot: 0 Höhe, klebt oben — die Leiste fährt hinein */
.bs-stickybar .slot { position: sticky; top: 12px; z-index: 40; height: 0; }
.bs-stickybar .bar { display: flex; align-items: center; gap: 14px;
  padding: 9px 9px 9px 20px; border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border: 1px solid var(--line); box-shadow: var(--shadow);
  transform: translateY(calc(-100% - 26px)); opacity: 0; visibility: hidden;
  transition: transform .4s cubic-bezier(.2, .7, .3, 1), opacity .3s, visibility 0s .4s; }
.bs-stickybar .bar.is-on { transform: none; opacity: 1; visibility: visible; transition-delay: 0s; }
.bs-stickybar .bar .logo { color: var(--brand-300); font-style: normal; }
.bs-stickybar .bar .name { font-family: var(--font-display); font-weight: 700; color: var(--fg); white-space: nowrap; }
.bs-stickybar .bar .claim { flex: 1; min-width: 0; font-size: 14px; color: var(--fg-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Marker-Sektion (Hero-Stellvertreter) */
.bs-stickybar .marker { min-height: 58vh; border-radius: calc(var(--r-lg) - 8px);
  display: grid; place-items: center; text-align: center; padding: 24px;
  background: radial-gradient(60% 90% at 50% 0%, rgba(var(--brand-rgb), .25), transparent 65%), var(--glass);
  border: 1px solid var(--line); }
.bs-stickybar .marker .tag { display: inline-block; font-size: 12px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--brand-300);
  border: 1px solid rgba(var(--brand-rgb), .4); border-radius: 999px; padding: 5px 14px; margin-bottom: 14px; }
.bs-stickybar .marker h3 { font-size: clamp(24px, 3vw, 34px); margin-bottom: 10px; }
.bs-stickybar .marker p { max-width: 44ch; margin: 0 auto; }

/* Demo-Füller — nur für Docs/Demos, damit der Rahmen scrollbar ist */
.bs-stickybar .filler { min-height: 80vh; margin-top: 12px; border-radius: calc(var(--r-lg) - 8px);
  border: 1.5px dashed var(--line); display: grid; place-items: center;
  font-size: 13px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--fg-muted); }

@media (max-width: 640px) {
  .bs-stickybar .bar .claim { display: none; }
  .bs-stickybar .bar { gap: 10px; padding-left: 16px; }
  .bs-stickybar .bar .name { flex: 1; }
}

/* ---------- Leadgen · Plan-Finder-Quiz ---------- */
.bs.bs-planfinder [hidden] { display: none !important; }
.bs.bs-planfinder .inner { padding-top: 88px; padding-bottom: 88px; }
.bs-planfinder .quiz { max-width: 640px; margin: 0 auto; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: clamp(24px, 4vw, 36px); box-shadow: var(--shadow); }
.bs-planfinder .quiz-top { margin-bottom: 26px; }
.bs-planfinder .progress-text { font-size: 13px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-muted); margin-bottom: 10px; }
.bs-planfinder .progress { height: 6px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.bs-planfinder .progress i { display: block; height: 100%; width: 100%; border-radius: inherit;
  transform: translateX(calc(var(--pf-progress, 33.4%) - 100%)); transform-origin: left;
  background: linear-gradient(90deg, var(--brand), var(--violet)); transition: transform .35s ease; }
.bs-planfinder .pf-screen h3 { font-size: 23px; margin-bottom: 18px; outline: none; }
.bs-planfinder .opts { display: grid; gap: 10px; }
.bs-planfinder .opt { display: flex; flex-direction: column; gap: 2px; text-align: left;
  font-family: var(--font-body); color: var(--fg-soft); background: var(--glass);
  border: 1.5px solid var(--line); border-radius: 14px; padding: 14px 18px; cursor: pointer;
  transition: border-color .15s, background-color .15s, box-shadow .15s; }
.bs-planfinder .opt:hover { border-color: var(--brand-300); }
.bs-planfinder .opt.selected { border-color: var(--brand);
  background: rgba(var(--brand-rgb), .08); box-shadow: 0 0 0 3px rgba(var(--brand-rgb), .14); }
.bs-planfinder .opt b { font-size: 16.5px; color: var(--fg); }
.bs-planfinder .opt span { font-size: 13.5px; color: var(--fg-muted); }

/* Empfehlungs-Karte */
.bs-planfinder .reco { border: 1.5px solid rgba(var(--brand-rgb), .4); border-radius: var(--r-md);
  padding: 22px; background: linear-gradient(150deg, rgba(var(--brand-rgb), .07), rgba(var(--violet-rgb), .05)); }
.bs-planfinder .reco-head { display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.bs-planfinder .reco-name { font-size: 26px; outline: none; }
.bs-planfinder .reco-price { font-size: 15px; font-weight: 700; color: var(--brand); }
.bs-planfinder .reco-text { font-size: 15px; margin-bottom: 14px; }
.bs-planfinder .reco-why { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
.bs-planfinder .reco-why li { display: flex; gap: 9px; align-items: flex-start; font-size: 14px; }
.bs-planfinder .reco-why li::before { content: "✓"; color: var(--green); font-weight: 800; flex: none; }
.bs-planfinder .reco .bs-btn { width: 100%; }
.bs-planfinder .restart { display: block; margin: 14px auto 0; background: none; border: none;
  font: 600 13.5px var(--font-body); color: var(--fg-muted); text-decoration: underline; cursor: pointer; }
.bs-planfinder .restart:hover { color: var(--fg); }

.bs-planfinder .quiz-bottom { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 24px; }
.bs-planfinder .back { background: none; border: none; cursor: pointer;
  font: 700 14.5px var(--font-body); color: var(--fg-soft); padding: 6px 4px; }
.bs-planfinder .back:hover { color: var(--fg); }

/* ---------- Leadgen · Dringlichkeits-Band mit Countdown ---------- */
.bs.bs-countdown [hidden] { display: none !important; }
.bs.bs-countdown .inner { padding-top: 72px; padding-bottom: 72px; }
.bs-countdown .cd-band { position: relative; overflow: hidden; display: grid;
  grid-template-columns: 1.15fr auto; gap: clamp(20px, 4vw, 44px); align-items: center;
  padding: clamp(24px, 4vw, 36px); border-radius: var(--r-lg);
  border: 1px solid rgba(var(--brand-rgb), .45);
  background: linear-gradient(120deg, rgba(var(--brand-rgb), .22), rgba(var(--violet-rgb), .14)); }
.bs-countdown .cd-band::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(45% 90% at 85% 0%, rgba(var(--brand-rgb), .25), transparent 65%); }
.bs-countdown .cd-copy { position: relative; }
.bs-countdown h2 { font-size: clamp(22px, 2.8vw, 32px); font-weight: 800; margin: 12px 0 10px; }
.bs-countdown .cd-date { font-size: 14px; color: var(--fg-muted); }
.bs-countdown .cd-side { position: relative; display: flex; flex-direction: column; gap: 16px; align-items: center; }
.bs-countdown .cd-timer { display: flex; align-items: center; gap: 8px; }
.bs-countdown .cd-cell { min-width: 68px; text-align: center; padding: 10px 8px 8px;
  background: var(--glass); border: 1px solid var(--line); border-radius: 12px; }
.bs-countdown .cd-cell b { display: block; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(24px, 3vw, 32px); color: var(--fg); font-variant-numeric: tabular-nums; line-height: 1.1; }
.bs-countdown .cd-cell span { font-size: 11px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--fg-muted); }
.bs-countdown .cd-timer > i { font-style: normal; font-weight: 800; color: var(--fg-muted); }
.bs-countdown .cd-side .bs-btn { width: 100%; }

/* Abgelaufener Zustand */
.bs-countdown .cd-expired { display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--glass);
  padding: 18px 20px; font-size: 15.5px; }
.bs-countdown .cd-expired b { color: var(--fg); }
.bs-countdown .cd-expired a { color: var(--brand-300); font-weight: 700; text-decoration: underline; }

@media (max-width: 860px) {
  .bs-countdown .cd-band { grid-template-columns: 1fr; }
  .bs-countdown .cd-side { align-items: stretch; }
  .bs-countdown .cd-timer { justify-content: center; }
}
@media (max-width: 400px) {
  .bs-countdown .cd-cell { min-width: 58px; }
  .bs-countdown .cd-timer > i { display: none; }
}


/* ===== landing_conversion.css ===== */
/* =================================================================
   LANDING · CONVERSION — Kaufentscheidungs-Bausteine für Landing Pages.

   · .bs-roi   ROI-/Ersparnis-Rechner: drei Slider treiben eine live
               berechnete Jahresersparnis (landing-roi Controller,
               Track-Füllung über --p auf dem Input).
   · .bs-vs    Wettbewerber-Vergleich: Funktions- und eigene Spalte
               links sticky, eigene Spalte farbig, mobil horizontal
               scrollbar.
   · .bs-risk  Garantie- & Risk-Reversal-Band mit drei Siegeln.
   · .bs-onb   Onboarding in 3 Schritten mit Code-Snippet + Copy-Button
               (landing-onboarding Controller).
   · .bs-fit   Einwand-Sektion "Für wen ist das (nicht)?" mit
               Check/Kreuz-Spalten.
   · .bs-ba    Vorher/Nachher-Slider: unsichtbares input[type=range]
               steuert per --pos den clip-path der Nachher-Ebene
               (landing-before-after Controller).

   Baut auf landing_base.css auf (.bs-Scope + Flächen-Tokens).
   ================================================================= */

/* ---------- ROI-Rechner (.bs-roi) ---------- */
.bs.bs-roi { position: relative; overflow: hidden; padding: 88px 0 72px; }
.bs.bs-roi::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(55% 70% at 85% 15%, rgba(var(--brand-rgb),.22), transparent 60%),
    radial-gradient(45% 60% at 5% 90%, rgba(var(--violet-rgb),.18), transparent 60%); }
.bs-roi .bs-container { position: relative; }
.bs-roi .roi-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 28px; align-items: stretch; }

/* Regler-Karte */
.bs-roi .roi-controls { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 30px 28px; display: flex; flex-direction: column; gap: 26px; }
.bs-roi .roi-field label { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  font-size: 15px; font-weight: 700; color: var(--fg); margin-bottom: 12px; }
.bs-roi .roi-field output { font-family: var(--font-display); font-size: 20px; font-weight: 800;
  color: var(--brand); white-space: nowrap; }
.bs.bs-roi[data-theme="dark"] .roi-field output { color: var(--brand-300); }
.bs-roi .roi-field small { display: block; margin-top: 8px; font-size: 12.5px; color: var(--fg-muted); font-weight: 500; }

/* Slider — Track-Füllung über --p (setzt der landing-roi Controller) */
.bs-roi input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%;
  height: 26px; background: transparent; margin: 0; cursor: pointer; }
.bs-roi input[type="range"]::-webkit-slider-runnable-track { height: 6px; border-radius: 999px;
  background: linear-gradient(90deg, var(--brand) var(--p, 50%), var(--line) var(--p, 50%)); }
.bs-roi input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; height: 22px;
  margin-top: -8px; border-radius: 50%; background: #fff; border: 4px solid var(--brand);
  box-shadow: 0 4px 14px -4px rgba(var(--brand-rgb),.8); }
.bs-roi input[type="range"]::-moz-range-track { height: 6px; border-radius: 999px; background: var(--line); }
.bs-roi input[type="range"]::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--brand); }
.bs-roi input[type="range"]::-moz-range-thumb { width: 15px; height: 15px; border-radius: 50%;
  background: #fff; border: 4px solid var(--brand); box-shadow: 0 4px 14px -4px rgba(var(--brand-rgb),.8); }

/* Ergebnis-Karte */
.bs-roi .roi-result { position: relative; overflow: hidden; text-align: center;
  display: flex; flex-direction: column; justify-content: center; gap: 6px;
  background: linear-gradient(160deg, rgba(var(--brand-rgb),.16), rgba(var(--violet-rgb),.10)), var(--card);
  border: 1px solid color-mix(in srgb, var(--brand) 45%, var(--line));
  border-radius: var(--r-lg); padding: 34px 28px; }
.bs-roi .roi-result::before { content: ""; position: absolute; inset: -30% -30% auto;
  height: 70%; pointer-events: none; filter: blur(40px);
  background: radial-gradient(50% 60% at 50% 0%, rgba(var(--brand-rgb),.35), transparent 70%); }
.bs-roi .roi-label { position: relative; font-size: 13px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--fg-muted); }
.bs-roi .roi-total { position: relative; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(42px, 5.6vw, 68px); line-height: 1.05; letter-spacing: -.02em; color: var(--fg);
  font-variant-numeric: tabular-nums; }
.bs-roi .roi-sub { position: relative; display: flex; justify-content: center; flex-wrap: wrap;
  gap: 8px 26px; margin-top: 8px; font-size: 14.5px; color: var(--fg-soft); }
.bs-roi .roi-sub b { color: var(--fg); font-variant-numeric: tabular-nums; }
.bs-roi .roi-result .bs-btn { position: relative; align-self: center; margin-top: 22px; }
.bs-roi .roi-note { max-width: 860px; margin: 26px auto 0; font-size: 12.5px; line-height: 1.6;
  color: var(--fg-muted); text-align: center; }

@media (max-width: 940px) { .bs-roi .roi-grid { grid-template-columns: 1fr; } }
@media (max-width: 480px) { .bs-roi .roi-controls, .bs-roi .roi-result { padding: 24px 18px; } }

/* ---------- Wettbewerber-Vergleich (.bs-vs) ---------- */
.bs.bs-vs { padding: 88px 0 80px; }
.bs-vs .vh { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

.bs-vs .vs-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg);
  box-shadow: var(--shadow); }
.bs-vs table { --lbl: 190px; width: 100%; min-width: 780px; border-collapse: separate; border-spacing: 0;
  font-size: 15px; }
.bs-vs th, .bs-vs td { padding: 14px 18px; text-align: center; border-bottom: 1px solid var(--line);
  vertical-align: middle; }
.bs-vs tbody tr:last-child th, .bs-vs tbody tr:last-child td { border-bottom: 0; }
.bs-vs thead th { font-family: var(--font-display); font-size: 15.5px; font-weight: 800;
  color: var(--fg); background: var(--bg); border-bottom: 2px solid var(--line); }

/* Sticky: Funktions-Spalte links, eigene Spalte direkt daneben */
.bs-vs .vs-feat { position: sticky; left: 0; z-index: 2; width: var(--lbl); min-width: var(--lbl); max-width: var(--lbl);
  background: var(--bg); text-align: left; font-weight: 600; color: var(--fg); font-size: 14.5px; }
.bs-vs .vs-us { position: sticky; left: var(--lbl); z-index: 2; min-width: 160px;
  background: color-mix(in srgb, var(--brand) 8%, var(--bg)); font-weight: 700; color: var(--fg); }
.bs-vs .vs-us::after { content: ""; position: absolute; top: 0; bottom: 0; right: -14px; width: 14px;
  pointer-events: none; background: linear-gradient(90deg, rgba(20, 16, 31, .09), transparent); }
.bs-vs thead .vs-us { background: var(--brand); color: #fff; z-index: 3; }
.bs-vs thead .vs-us .vs-flag { display: block; font-family: var(--font-body); font-size: 11px;
  font-weight: 700; letter-spacing: .07em; text-transform: uppercase; opacity: .9; margin-top: 2px; }
.bs-vs thead th:not(.vs-us):not(.vs-feat) { color: var(--fg-soft); }

/* Check/Kreuz/Teilweise-Chips */
.bs-vs .vs-ic { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  font-size: 14px; font-weight: 800; line-height: 1; }
.bs-vs .vs-yes  { background: color-mix(in srgb, var(--green) 15%, transparent); color: var(--green); }
.bs-vs .vs-no   { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); }
.bs-vs .vs-part { background: color-mix(in srgb, var(--orange) 16%, transparent); color: var(--orange); }
.bs-vs td small { display: block; margin-top: 4px; font-size: 12px; color: var(--fg-muted); font-weight: 500; }

.bs-vs .vs-foot { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-top: 26px; text-align: center; }
.bs-vs .vs-note { font-size: 12.5px; color: var(--fg-muted); max-width: 640px; }

@media (max-width: 560px) {
  .bs-vs table { --lbl: 138px; }
  .bs-vs th, .bs-vs td { padding: 12px 12px; }
  .bs-vs .vs-feat { font-size: 13.5px; }
}

/* ---------- Garantie-Band (.bs-risk) ---------- */
.bs.bs-risk { position: relative; overflow: hidden; padding: 64px 0; }
.bs.bs-risk::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 120% at 50% -20%, rgba(var(--brand-rgb),.14), transparent 65%); }
.bs-risk .inner { position: relative; }
.bs-risk h2 { text-align: center; font-size: clamp(22px, 2.6vw, 30px); font-weight: 800; margin-bottom: 34px; }
.bs-risk .seals { display: grid; grid-template-columns: repeat(3, 1fr); }
.bs-risk .seal { display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-align: center; padding: 8px 26px; }
.bs-risk .seal + .seal { border-left: 1px solid var(--line); }
.bs-risk .seal .ic { display: grid; place-items: center; width: 58px; height: 58px; border-radius: 50%;
  color: var(--green); background: color-mix(in srgb, var(--green) 12%, transparent);
  border: 1.5px dashed color-mix(in srgb, var(--green) 55%, transparent); }
.bs-risk .seal .ic svg { width: 27px; height: 27px; }
.bs-risk .seal b { font-family: var(--font-display); font-size: 17.5px; font-weight: 800; color: var(--fg); }
.bs-risk .seal p { font-size: 14px; color: var(--fg-soft); max-width: 30ch; }
.bs-risk .fine { position: relative; text-align: center; margin-top: 30px; font-size: 12.5px; color: var(--fg-muted); }
@media (max-width: 760px) {
  .bs-risk .seals { grid-template-columns: 1fr; gap: 26px; }
  .bs-risk .seal + .seal { border-left: 0; border-top: 1px solid var(--line); padding-top: 26px; }
}

/* ---------- Onboarding in 3 Schritten (.bs-onb) ---------- */
.bs.bs-onb { padding: 88px 0 84px; }
.bs-onb .vh { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.bs-onb .onb-steps { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: 1.25fr 1fr 1fr; gap: 22px; align-items: stretch; }
.bs-onb .onb-step { display: flex; flex-direction: column; gap: 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 26px 24px; box-shadow: var(--shadow); }
.bs-onb .onb-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.bs-onb .onb-num { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 12px;
  font-family: var(--font-display); font-size: 18px; font-weight: 800; color: #fff;
  background: var(--brand); box-shadow: 0 8px 18px -8px rgba(var(--brand-rgb),.8); }
.bs-onb .onb-time { font-size: 12.5px; font-weight: 700; color: var(--brand);
  background: rgba(var(--brand-rgb),.09); border: 1px solid rgba(var(--brand-rgb),.22);
  padding: 4px 11px; border-radius: 999px; white-space: nowrap; }
.bs.bs-onb[data-theme="dark"] .onb-time { color: var(--brand-300); }
.bs-onb .onb-step h3 { font-size: 19px; font-weight: 800; }
.bs-onb .onb-step > p { font-size: 14.5px; }

/* Code-Block in Step 1 (neutrale dunkle Code-Fläche, keine Markenfarbe) */
.bs-onb .onb-code { margin-top: 4px; border-radius: 12px; overflow: hidden;
  background: #0d0b26; border: 1px solid rgba(255,255,255,.12);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.bs-onb .onb-code-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 8px 8px 14px; border-bottom: 1px solid rgba(255,255,255,.1);
  font-size: 12px; color: rgba(255,255,255,.55); }
.bs-onb .onb-copy { display: inline-flex; align-items: center; gap: 6px;
  font: 700 12px/1 var(--font-body); color: #fff; cursor: pointer;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.18);
  border-radius: 8px; padding: 6px 10px; transition: background-color .2s, border-color .2s; }
.bs-onb .onb-copy:hover { background: rgba(255,255,255,.16); }
.bs-onb .onb-copy.is-copied { color: var(--green); border-color: color-mix(in srgb, var(--green) 60%, transparent); }
.bs-onb .onb-copy svg { width: 13px; height: 13px; }
.bs-onb .onb-code pre { margin: 0; padding: 14px 16px; overflow-x: auto;
  font-size: 12.5px; line-height: 1.7; color: #b9c2ff; }
.bs-onb .onb-code code .tag { color: var(--brand-300); }
.bs-onb .onb-code code .val { color: #6ee7a8; }

/* Mini-Mocks in Step 2 + 3 */
.bs-onb .onb-mock { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
.bs-onb .onb-mock .m-row { display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: var(--fg-soft); background: var(--glass);
  border: 1px solid var(--line); border-radius: 9px; padding: 8px 11px; }
.bs-onb .onb-mock .m-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); flex: none; }
.bs-onb .onb-mock .m-err { margin-left: auto; font-weight: 700; color: var(--red); white-space: nowrap; }
.bs-onb .onb-done { margin-top: auto; display: inline-flex; align-items: center; gap: 9px; align-self: flex-start;
  font-size: 13px; font-weight: 700; color: var(--green);
  background: color-mix(in srgb, var(--green) 11%, transparent);
  border: 1px solid color-mix(in srgb, var(--green) 40%, transparent);
  border-radius: 999px; padding: 8px 14px; }
.bs-onb .onb-cta { display: flex; justify-content: center; margin-top: 38px; }
@media (max-width: 960px) { .bs-onb .onb-steps { grid-template-columns: 1fr; } }

/* ---------- Einwand-Sektion (.bs-fit) ---------- */
.bs.bs-fit { position: relative; overflow: hidden; padding: 88px 0 80px; }
.bs.bs-fit::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(50% 70% at 15% 10%, rgba(var(--violet-rgb),.16), transparent 60%); }
.bs-fit .inner { position: relative; }
.bs-fit .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: stretch; }
.bs-fit .col { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 30px 28px; }
.bs-fit .col.yes { border-color: color-mix(in srgb, var(--green) 45%, var(--line));
  box-shadow: 0 20px 50px -30px color-mix(in srgb, var(--green) 60%, transparent); }
.bs-fit .col h3 { font-size: 20px; font-weight: 800; margin-bottom: 18px; display: flex; align-items: center; gap: 10px; }
.bs-fit .col h3 .hd-ic { display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: 9px; font-size: 15px; font-weight: 800; flex: none; }
.bs-fit .col.yes .hd-ic { color: var(--green); background: color-mix(in srgb, var(--green) 14%, transparent); }
.bs-fit .col.no  .hd-ic { color: var(--fg-muted); background: var(--glass); }
.bs-fit .col.no h3 { color: var(--fg-soft); }
.bs-fit .col ul { display: flex; flex-direction: column; gap: 13px; }
.bs-fit .col li { display: flex; gap: 11px; font-size: 15px; line-height: 1.55; }
.bs-fit .col li i { font-style: normal; font-weight: 800; flex: none; margin-top: 1px; }
.bs-fit .col.yes li i { color: var(--green); }
.bs-fit .col.no  li i { color: var(--red); opacity: .85; }
.bs-fit .col.no  li { color: var(--fg-muted); }
.bs-fit .col.no  li b { color: var(--fg-soft); }
.bs-fit .fit-cta { display: flex; align-items: center; justify-content: center; gap: 16px;
  flex-wrap: wrap; margin-top: 34px; text-align: center; }
.bs-fit .fit-cta p { font-size: 14.5px; color: var(--fg-soft); }
@media (max-width: 860px) { .bs-fit .cols { grid-template-columns: 1fr; } }
@media (max-width: 480px) { .bs-fit .col { padding: 24px 18px; } }

/* ---------- Vorher/Nachher-Slider (.bs-ba) ---------- */
.bs.bs-ba { padding: 88px 0 80px; }
.bs-ba .vh { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

.bs-ba .ba-stage { position: relative; height: clamp(300px, 44vw, 440px); overflow: hidden;
  border-radius: var(--r-lg); border: 1px solid var(--line); box-shadow: var(--shadow);
  isolation: isolate; }
.bs-ba .ba-pane { position: absolute; inset: 0; padding: clamp(16px, 3vw, 30px); }

/* VORHER: Ticket-Chaos (rötlich getönte Fläche, verstreute Zettel) */
.bs-ba .ba-before { background:
    radial-gradient(70% 90% at 20% 15%, color-mix(in srgb, var(--red) 10%, transparent), transparent 65%),
    var(--bg-soft); }
.bs-ba .ba-err { display: inline-block; max-width: 90%;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: clamp(10.5px, 1.4vw, 13px);
  color: var(--red); background: color-mix(in srgb, var(--red) 9%, var(--card));
  border: 1px solid color-mix(in srgb, var(--red) 40%, transparent);
  border-radius: 9px; padding: 9px 13px; }
.bs-ba .ba-note { position: absolute; max-width: 210px;
  font-size: clamp(11px, 1.5vw, 13.5px); line-height: 1.45; color: var(--fg-soft);
  background: var(--card); border: 1.5px dashed color-mix(in srgb, var(--red) 45%, var(--line));
  border-radius: 10px; padding: 10px 13px; box-shadow: 0 10px 24px -14px rgba(20,16,31,.35); }
.bs-ba .ba-note b { color: var(--fg); }
.bs-ba .n1 { top: 26%; left: 5%; transform: rotate(-3deg); }
.bs-ba .n2 { top: 46%; left: 30%; transform: rotate(2deg); }
.bs-ba .n3 { top: 68%; left: 8%; transform: rotate(-1.5deg); }
.bs-ba .n4 { top: 62%; left: 52%; transform: rotate(3deg); max-width: 180px; }

/* NACHHER: aufgeräumte Session-Timeline — liegt oben, wird geclippt */
.bs-ba .ba-after { background: var(--bg); clip-path: inset(0 0 0 var(--pos, 50%)); }
.bs-ba .ba-after .ui { max-width: 460px; margin-left: auto; display: flex; flex-direction: column;
  gap: clamp(7px, 1.2vw, 11px); }
.bs-ba .ba-after .ui-head { display: flex; justify-content: space-between; align-items: center; gap: 10px;
  font-size: clamp(11.5px, 1.5vw, 13.5px); font-weight: 700; color: var(--fg); margin-bottom: 2px; }
.bs-ba .ba-after .ui-head .ok { color: var(--green); font-size: clamp(10.5px, 1.4vw, 12.5px); white-space: nowrap; }
.bs-ba .ba-after .step { display: flex; align-items: center; gap: 10px;
  font-size: clamp(11px, 1.5vw, 13.5px); color: var(--fg-soft);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: clamp(7px, 1.2vw, 10px) 13px; }
.bs-ba .ba-after .step i { font-style: normal; width: 9px; height: 9px; border-radius: 50%;
  background: var(--brand); flex: none; }
.bs-ba .ba-after .step .t { font-variant-numeric: tabular-nums; color: var(--fg-muted); flex: none; }
.bs-ba .ba-after .step.err { border-color: color-mix(in srgb, var(--red) 45%, var(--line)); color: var(--fg); }
.bs-ba .ba-after .step.err i { background: var(--red); }
.bs-ba .ba-after .fixed { align-self: flex-end; font-size: clamp(10.5px, 1.4vw, 12.5px); font-weight: 700;
  color: var(--green); background: color-mix(in srgb, var(--green) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--green) 40%, transparent);
  border-radius: 999px; padding: 5px 12px; }

/* Ecken-Etiketten */
.bs-ba .ba-tag { position: absolute; top: 14px; font-size: 11.5px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; border-radius: 999px; padding: 5px 12px; }
.bs-ba .ba-before .ba-tag { left: 14px; color: var(--red);
  background: color-mix(in srgb, var(--red) 12%, var(--card));
  border: 1px solid color-mix(in srgb, var(--red) 40%, transparent); }
.bs-ba .ba-after .ba-tag { right: 14px; color: var(--green);
  background: color-mix(in srgb, var(--green) 12%, var(--card));
  border: 1px solid color-mix(in srgb, var(--green) 40%, transparent); }

/* Trenner + Griff (rein dekorativ, folgt --pos) */
.bs-ba .ba-divider { position: absolute; top: 0; bottom: 0; left: var(--pos, 50%); width: 3px;
  margin-left: -1.5px; background: var(--fg); z-index: 2; pointer-events: none; }
.bs-ba .ba-handle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: var(--fg); color: var(--bg); font-size: 13px; font-weight: 800; letter-spacing: 1px;
  box-shadow: 0 10px 26px -8px rgba(20,16,31,.5); white-space: nowrap; }

/* Unsichtbarer, aber fokussier- und ziehbarer Regler über der Bühne */
.bs-ba .ba-range { position: absolute; inset: 0; z-index: 3; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none; }
.bs-ba .ba-stage:has(.ba-range:focus-visible) .ba-handle {
  outline: 3px solid var(--brand); outline-offset: 3px; }

.bs-ba .ba-hint { text-align: center; font-size: 13.5px; color: var(--fg-muted); margin-top: 16px; }
@media (max-width: 560px) {
  .bs-ba .ba-stage { height: 340px; }
  .bs-ba .n4 { display: none; }
  .bs-ba .ba-note { max-width: 165px; }
}


/* ===== landing_trust.css ===== */
/* =================================================================
   LANDING · TRUST — Stile für die fünf Vertrauens-Bausteine.

   · .bs-team     Team-Grid „Die Menschen dahinter" mit Initialen-Avataren
   · .bs-founder  Gründer-Note als Papier-Brief mit SVG-Unterschrift
   · .bs-rating   Bewertungs-Widget mit Sternen und Balken-Verteilung
   · .bs-press    Presse „Bekannt aus" mit Wortmarken und Zitaten
   · .bs-awards   Auszeichnungen mit Lorbeerkranz-Medaillen

   Alle Regeln hängen unter dem .bs-Scope aus landing_base.css und
   lesen ausschließlich die Flächen-Tokens — jeder Baustein funktioniert
   dadurch hell UND dunkel.
   ================================================================= */

/* ---------- Vertrauen 1 · Team-Grid ---------- */
.bs-team .inner { padding-top: 88px; padding-bottom: 96px; }
.bs-team .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.bs-team .card { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 12px 12px 22px; box-shadow: var(--shadow); transition: transform .2s ease; }
.bs-team .card:hover { transform: translateY(-4px); }

/* Avatar = Farbfläche + Initialen — durch echtes <img> ersetzbar.
   Farben pro Person via --a1/--a2 (nur Design-Tokens!) am <li>. */
.bs-team .avatar { position: relative; aspect-ratio: 1 / .92; border-radius: 16px; overflow: hidden;
  display: grid; place-items: center; background: linear-gradient(140deg, var(--a1), var(--a2)); }
.bs-team .avatar::before { content: ""; position: absolute; width: 150%; aspect-ratio: 1;
  border-radius: 50%; border: 2px solid rgba(255,255,255,.22); left: 52%; top: 42%; }
.bs-team .avatar::after { content: ""; position: absolute; inset: 0;
  background: radial-gradient(90% 80% at 18% 0%, rgba(255,255,255,.3), transparent 55%); }
.bs-team .avatar b { position: relative; z-index: 1; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(38px, 4.5vw, 52px); color: #fff; text-shadow: 0 2px 14px rgba(0,0,0,.2); }
.bs-team .info { padding: 16px 8px 0; }
.bs-team h3 { font-size: 17px; font-weight: 700; }
.bs-team .role { font-size: 13.5px; color: var(--fg-muted); margin-top: 2px; }
.bs-team .fact { margin-top: 12px; font-size: 14.5px; line-height: 1.5; color: var(--fg-soft);
  padding-left: 12px; border-left: 2px solid rgba(var(--brand-rgb), .45); }

@media (max-width: 940px) { .bs-team .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) {
  .bs-team .grid { grid-template-columns: 1fr; gap: 16px; }
  .bs-team .card { display: grid; grid-template-columns: 92px 1fr; gap: 14px; align-items: center; padding: 12px; }
  .bs-team .avatar { aspect-ratio: 1; border-radius: 12px; }
  .bs-team .avatar b { font-size: 30px; }
  .bs-team .info { padding: 0; }
}

/* ---------- Vertrauen 2 · Gründer-Note mit Unterschrift ---------- */
.bs.bs-founder { position: relative; overflow: hidden; }
.bs.bs-founder::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(55% 60% at 50% 15%, rgba(var(--violet-rgb), .18), transparent 65%),
              radial-gradient(40% 50% at 85% 90%, rgba(var(--brand-rgb), .14), transparent 60%); }
.bs-founder .inner { position: relative; padding-top: 92px; padding-bottom: 104px; }
.bs-founder .bs-section-head { margin-bottom: 46px; }

/* Papier-Karte: bewusst IMMER hell (wie ein echter Brief), deshalb
   hier fixe, neutrale Papiertöne statt Flächen-Tokens. */
.bs-founder .paper { position: relative; max-width: 640px; margin: 0 auto; transform: rotate(-.6deg);
  background: linear-gradient(180deg, #fdfaf2, #f6efdd);
  border: 1px solid #e7dcc0; border-radius: 6px;
  padding: clamp(28px, 6vw, 54px);
  box-shadow: 0 30px 80px -30px rgba(0,0,0,.55);
  font-family: Georgia, "Times New Roman", serif; font-size: 16.5px; line-height: 1.8; color: #4b4433; }
.bs-founder .paper::before { /* Klebestreifen in Markenfarbe */
  content: ""; position: absolute; top: -13px; left: 50%; width: 128px; height: 28px;
  transform: translateX(-50%) rotate(-2deg);
  background: rgba(var(--brand-rgb), .22); border: 1px solid rgba(var(--brand-rgb), .3); border-radius: 3px; }
.bs-founder .date { text-align: right; font-style: italic; font-size: 14px; color: #9a8f6d; }
.bs-founder .salut { font-weight: 700; font-size: 18px; color: #2e2a1d; margin-top: 20px; }
.bs-founder .paper p + p { margin-top: 14px; }
.bs-founder .paper q { font-style: italic; }

/* Unterschrift: zeichnet sich beim Reveal (nur wenn der fx-Controller läuft) */
.bs-founder .sig { width: 225px; height: auto; margin: 28px 0 4px; overflow: visible; }
.bs-founder .sig path { fill: none; stroke: #283163; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.bs.bs-founder[data-fx-ready] .sig path { stroke-dasharray: 1; stroke-dashoffset: 1; }
.bs.bs-founder[data-fx-ready] .is-in .sig path { stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.5s cubic-bezier(.45, 0, .3, 1) .45s; }
.bs.bs-founder[data-fx-ready] .is-in .sig .flourish { transition-duration: .5s; transition-delay: 1.9s; }
@media (prefers-reduced-motion: reduce) { .bs-founder .sig path { transition-delay: 0s !important; } }

.bs-founder .who { display: flex; gap: 12px; align-items: center; font-family: var(--font-body); }
.bs-founder .chip { flex: none; width: 42px; height: 42px; border-radius: 50%;
  background: linear-gradient(140deg, var(--brand), var(--violet));
  color: #fff; display: grid; place-items: center; font: 800 14px/1 var(--font-display); }
.bs-founder .who b { display: block; font-size: 15.5px; color: #2e2a1d; line-height: 1.3; }
.bs-founder .who small { font-size: 13px; color: #9a8f6d; }
.bs-founder .ps { margin-top: 24px; padding-top: 14px; border-top: 1px dashed #dccfa9;
  font-style: italic; font-size: 14.5px; color: #7d7355; }

/* ---------- Vertrauen 3 · Bewertungs-Widget ---------- */
.bs-rating .inner { padding-top: 88px; padding-bottom: 96px; }
.bs-rating .panel { display: grid; grid-template-columns: 1fr 1.15fr; gap: clamp(28px, 5vw, 56px);
  max-width: 980px; margin: 0 auto; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-xl); box-shadow: var(--shadow); padding: clamp(26px, 5vw, 52px); }

/* Links: Gesamtnote + Sterne + Quellen */
.bs-rating .score { display: flex; align-items: baseline; gap: 6px; }
.bs-rating .score .num { font: 800 clamp(56px, 8vw, 76px)/1 var(--font-display); letter-spacing: -.03em; color: var(--fg); }
.bs-rating .score .den { font-size: 20px; font-weight: 700; color: var(--fg-muted); }
.bs-rating .stars { position: relative; width: max-content; margin: 14px 0 12px; }
.bs-rating .stars .row { display: flex; gap: 5px; }
.bs-rating .stars svg { width: 28px; height: 28px; flex: none; fill: var(--line); }
.bs-rating .stars .fillrow { position: absolute; inset: 0 auto 0 0; overflow: hidden; display: flex; gap: 5px; }
.bs-rating .stars .fillrow svg { fill: #f2a51f; } /* semantisches Bewertungs-Gelb */
.bs-rating .basis { font-size: 15px; }
.bs-rating .basis b { color: var(--fg); }
.bs-rating .sources { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
.bs-rating .sources li { display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 600; color: var(--fg-soft);
  border: 1px solid var(--line); background: var(--glass); border-radius: 999px; padding: 6px 14px 6px 7px; }
.bs-rating .sources .logo { width: 24px; height: 24px; border-radius: 8px; background: var(--c, var(--brand));
  color: #fff; display: grid; place-items: center; font: 800 12px/1 var(--font-display); font-style: normal; }
.bs-rating .sources b { color: var(--fg); }
.bs-rating .sources b::before { content: "★ "; color: #f2a51f; }

/* Rechts: Verteilung 5→1 Sterne */
.bs-rating .dist-wrap { border-left: 1px solid var(--line); padding-left: clamp(28px, 5vw, 56px); }
.bs-rating .dist-wrap h3 { font-size: 18px; margin-bottom: 16px; }
.bs-rating .dist li { display: grid; grid-template-columns: 42px 1fr 58px; align-items: center; gap: 12px;
  padding: 7px 0; font-size: 14.5px; }
.bs-rating .dist .lbl { font-weight: 700; color: var(--fg); white-space: nowrap; }
.bs-rating .dist .lbl i { font-style: normal; color: #f2a51f; }
.bs-rating .dist .pct { text-align: right; font-weight: 600; color: var(--fg-soft); font-variant-numeric: tabular-nums; }
.bs-rating .bar { height: 10px; border-radius: 999px; background: var(--glass); border: 1px solid var(--line); overflow: hidden; }
.bs-rating .bar i { display: block; height: 100%; border-radius: inherit; width: 100%;
  transform: translateX(calc(var(--w, 0%) - 100%)); transform-origin: left;
  background: linear-gradient(90deg, #e99a12, #ffc23d);
  transition: transform 1.05s cubic-bezier(.2, .7, .3, 1); }
.bs.bs-rating[data-fx-ready] .bar i { transform: translateX(-100%); }
.bs.bs-rating[data-fx-ready] .panel.is-in .bar i { transform: translateX(calc(var(--w, 0%) - 100%)); }
.bs-rating .dist li:nth-child(2) .bar i { transition-delay: .12s; }
.bs-rating .dist li:nth-child(3) .bar i { transition-delay: .24s; }
.bs-rating .dist li:nth-child(4) .bar i { transition-delay: .36s; }
.bs-rating .dist li:nth-child(5) .bar i { transition-delay: .48s; }
@media (prefers-reduced-motion: reduce) { .bs-rating .bar i { transition-delay: 0s !important; } }

.bs-rating .note { text-align: center; font-size: 13px; color: var(--fg-muted); margin-top: 22px; }

@media (max-width: 820px) {
  .bs-rating .panel { grid-template-columns: 1fr; }
  .bs-rating .dist-wrap { border-left: 0; padding-left: 0; border-top: 1px solid var(--line); padding-top: 26px; }
}

/* ---------- Vertrauen 4 · Presse „Bekannt aus" ---------- */
.bs-press .inner { padding-top: 76px; padding-bottom: 84px; }
.bs-press .label { display: flex; align-items: center; justify-content: center; gap: 18px;
  font-size: 13px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-muted); margin-bottom: 36px; }
.bs-press .label::before, .bs-press .label::after { content: ""; height: 1px; width: min(120px, 16vw); background: var(--line); }
.bs-press .grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.bs-press figure { margin: 0; display: flex; flex-direction: column;
  border: 1px solid var(--line); background: var(--glass); border-radius: var(--r-md);
  padding: 22px 22px 24px; transition: border-color .25s, transform .2s ease; }
.bs-press figure:hover { border-color: rgba(var(--brand-rgb), .55); transform: translateY(-3px); }
/* figcaption darf erstes Kind von <figure> sein — Wortmarke oben */
.bs-press .mark { order: -1; display: flex; align-items: center; min-height: 32px; margin-bottom: 12px;
  color: var(--fg-muted); transition: color .25s; }
.bs-press figure:hover .mark { color: var(--fg); }
.bs-press .mark i { font-style: normal; transition: color .25s; }
.bs-press figure:hover .mark i { color: var(--brand-300); }
.bs-press blockquote { margin: 0 0 12px; font-size: 15px; line-height: 1.55; color: var(--fg-soft); }
.bs-press .meta { margin-top: auto; font-size: 12.5px; color: var(--fg-muted); }
/* 4 unterschiedliche Wortmarken-Typografien — alle monochrom */
.bs-press .mark-1 { font-family: var(--font-display); font-weight: 800; letter-spacing: .16em; font-size: 15px; }
.bs-press .mark-2 { font-family: var(--font-body); font-weight: 800; font-size: 17.5px; letter-spacing: -.01em; }
.bs-press .mark-3 { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 600; font-size: 15px; }
.bs-press .mark-4 { font-family: Georgia, "Times New Roman", serif; font-style: italic; font-size: 18.5px; }
@media (max-width: 960px) { .bs-press .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 540px) { .bs-press .grid { grid-template-columns: 1fr; } }

/* ---------- Vertrauen 5 · Auszeichnungen & Awards ---------- */
.bs-awards .inner { padding-top: 90px; padding-bottom: 96px; }
.bs-awards .grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; max-width: 1040px; margin: 0 auto; }
.bs-awards .card { text-align: center; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 30px 18px 28px; box-shadow: var(--shadow);
  transition: transform .2s ease, border-color .25s; }
.bs-awards .card:hover { transform: translateY(-4px); border-color: rgba(var(--brand-rgb), .45); }
.bs-awards .medal { position: relative; display: block; width: 116px; height: 116px; margin: 0 auto 14px; color: var(--brand); }
.bs.bs-awards[data-theme="dark"] .medal { color: var(--brand-300); }
.bs-awards .medal svg { width: 100%; height: 100%; }
.bs-awards .medal b { position: absolute; inset: 0; display: grid; place-items: center; padding-top: 8px;
  font-family: var(--font-display); font-weight: 800; font-size: 25px; letter-spacing: -.02em; color: var(--fg); }
.bs-awards h3 { font-size: 16.5px; }
.bs-awards .src { font-size: 13px; color: var(--fg-muted); margin-top: 4px; }
@media (max-width: 900px) { .bs-awards .grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 420px) { .bs-awards .medal { width: 96px; height: 96px; } }


/* ===== landing_faq.css ===== */
/* =================================================================
   LANDING · FAQ — Stile für die FAQ-Bausteine.

   · .bs-faq-list — Akkordeon-Liste in Karten-Optik (zentriert)
   · .bs-faq-cols — Zweispaltig mit Sticky-Intro und Kontakt-Karte

   Exklusives Öffnen (nur eine Frage offen) läuft nativ über
   <details name="…"> — ganz ohne JavaScript. Der Chevron dreht sich
   bei [open], die Antwort öffnet weich (grid-template-rows: 0fr → 1fr).
   ================================================================= */

/* ---------- FAQ 1 · Akkordeon-Liste ---------- */
.bs-faq-list .inner { padding-top: 88px; padding-bottom: 96px; max-width: 820px; }

.bs-faq-list details { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-md); margin-bottom: 12px; overflow: hidden;
  transition: border-color .25s, box-shadow .25s; }
.bs-faq-list details[open] { border-color: var(--brand); box-shadow: var(--shadow); }

.bs-faq-list summary { display: flex; align-items: center; justify-content: space-between; gap: 18px;
  cursor: pointer; list-style: none; padding: 20px 24px;
  font-family: var(--font-display); font-size: 18px; font-weight: 700; color: var(--fg); }
.bs-faq-list summary::-webkit-details-marker { display: none; }
.bs-faq-list summary .chev { flex: none; color: var(--fg-muted); transition: transform .35s cubic-bezier(.2,.7,.3,1), color .25s; }
.bs-faq-list details[open] summary .chev { transform: rotate(180deg); color: var(--brand); }

/* Weiches Auf-/Zuklappen: Inhalt bleibt gerendert (::details-content),
   die Höhe steuert der Grid-Trick 0fr → 1fr. */
.bs-faq-list details::details-content { content-visibility: visible; }
/* Zwei getrennte Transitions statt einer: Die Deklaration hier gilt beim
   ZUklappen, die unter [open] beim AUFklappen. Liefen beide gleich lang, war
   der Text waehrend der ganzen Bewegung sichtbar und wurde vom wachsenden bzw.
   schrumpfenden Rand mitten durch die Zeile beschnitten — halbe Buchstaben,
   was als Flackern gelesen wird. Jetzt blendet der Text beim Schliessen zuerst
   weg (.12s) und beim Oeffnen erst aus, wenn Platz da ist (.12s Verzoegerung). */
.bs-faq-list .a-wrap { display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows .4s cubic-bezier(.2,.7,.3,1), opacity .12s ease; }
.bs-faq-list details[open] .a-wrap { grid-template-rows: 1fr; opacity: 1;
  transition: grid-template-rows .4s cubic-bezier(.2,.7,.3,1), opacity .25s ease .12s; }
.bs-faq-list .a { min-height: 0; overflow: hidden; }
.bs-faq-list .a p { padding: 0 24px 22px; font-size: 16px; color: var(--fg-soft); max-width: 62ch; }

@media (max-width: 480px) {
  .bs-faq-list summary { padding: 16px 18px; font-size: 16.5px; }
  .bs-faq-list .a p { padding: 0 18px 18px; }
}

/* ---------- FAQ 2 · Zweispaltig mit Sticky-Intro ---------- */
.bs-faq-cols .inner { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 64px;
  align-items: start; padding-top: 96px; padding-bottom: 104px; }

.bs-faq-cols .intro { position: sticky; top: 100px; }
.bs-faq-cols .intro h2 { font-size: clamp(28px, 3.4vw, 42px); font-weight: 800; margin-bottom: 16px; }
.bs-faq-cols .intro > p { font-size: 17px; max-width: 40ch; }

.bs-faq-cols .contact { display: flex; gap: 16px; align-items: flex-start; margin-top: 32px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md); padding: 20px 22px; }
.bs-faq-cols .contact svg { flex: none; margin-top: 2px; color: var(--brand-300); }
.bs-faq-cols .contact b { display: block; font-family: var(--font-display); font-size: 16.5px; color: var(--fg); margin-bottom: 4px; }
.bs-faq-cols .contact p { font-size: 14.5px; }
.bs-faq-cols .contact a { font-weight: 700; color: var(--brand-300); white-space: nowrap; }
.bs-faq-cols .contact a:hover { text-decoration: underline; }

.bs-faq-cols details { border-bottom: 1px solid var(--line); transition: border-color .25s; }
.bs-faq-cols details[open] { border-color: var(--brand-300); }
.bs-faq-cols summary { display: flex; align-items: center; justify-content: space-between; gap: 18px;
  cursor: pointer; list-style: none; padding: 20px 2px;
  font-family: var(--font-display); font-size: 18px; font-weight: 700; color: var(--fg); }
.bs-faq-cols summary::-webkit-details-marker { display: none; }
.bs-faq-cols summary .chev { flex: none; color: var(--fg-muted); transition: transform .35s cubic-bezier(.2,.7,.3,1), color .25s; }
.bs-faq-cols details[open] summary .chev { transform: rotate(180deg); color: var(--brand-300); }

.bs-faq-cols details::details-content { content-visibility: visible; }
/* Getrennte Zu-/Aufklapp-Transition wie bei .bs-faq-list — siehe dort. */
.bs-faq-cols .a-wrap { display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows .4s cubic-bezier(.2,.7,.3,1), opacity .12s ease; }
.bs-faq-cols details[open] .a-wrap { grid-template-rows: 1fr; opacity: 1;
  transition: grid-template-rows .4s cubic-bezier(.2,.7,.3,1), opacity .25s ease .12s; }
.bs-faq-cols .a { min-height: 0; overflow: hidden; }
.bs-faq-cols .a p { padding: 0 2px 22px; font-size: 16px; color: var(--fg-soft); max-width: 58ch; }

@media (max-width: 900px) {
  .bs-faq-cols .inner { grid-template-columns: 1fr; gap: 40px; padding-top: 72px; padding-bottom: 80px; }
  .bs-faq-cols .intro { position: static; }
}

/* ---------- Reduced Motion ---------- */
/* Beide FAQ-Varianten klappen ohne Bewegung um, wenn das System danach fragt.
   Das Details-Accordion hat das schon; hier fehlte es. */
@media (prefers-reduced-motion: reduce) {
  .bs-faq-list .a-wrap,
  .bs-faq-list details,
  .bs-faq-list summary .chev,
  .bs-faq-cols .a-wrap,
  .bs-faq-cols details,
  .bs-faq-cols summary .chev { transition: none; }
}


/* ===== pages/landing.css ===== */
/* ============================================================================
 * Public marketing landing page (Views::Landing::Show) — "Editorial & Warm"
 * redesign (issue #687). App layer on top of the gem's landing_*.css: the
 * warm/gold token system, the Lora-serif-first headline pattern, the hero
 * floating minis + HTML dashboard mockup, the timeline, feature mockups, chips
 * band, audience/trust grids, founder note, FAQ and the dark final-CTA darkform.
 * Everything sits inside .bs sections, so it reads the gem's surface tokens and
 * the --landing-* hooks below. Served raw by Propshaft (no Tailwind build).
 * ========================================================================== */

/* ---- Gem hooks: warm palette + Inter/Lora ---------------------------------
 * The gem's landing blocks (nav, FAQ, footer, buttons, eyebrow, browser frame)
 * read brand + typography from --landing-*. Mapping them here recolors every
 * .bs component to the editorial-warm look without touching the gem: brand →
 * dark ink (so primary buttons/nav read dark), accent → gold. */
:root {
  --landing-font-display: "InterVariable", "Inter", system-ui, sans-serif;
  --landing-font-body: "InterVariable", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  --landing-brand: #191713;
  --landing-brand-700: #000000;
  --landing-brand-300: #5d594f;
  --landing-brand-rgb: 25, 23, 19;
  --landing-accent: #c8912b;
  --landing-accent-rgb: 200, 145, 43;
  --landing-dark-surface: #191713;
  --landing-dark-surface-2: #201e18;

  /* App-layer editorial tokens (verbatim from the handoff spec). */
  --ink: #1d1c19;
  --sub: #5d594f;
  --hair: #eae6dc;
  --gold: #c8912b;
  --gold-deep: #a37318;
  --warm: #f7f3ea;
  --warm-deep: #f0e9da;
  --dark: #191713;
  --espresso: #3d3428;
}

.landing-body {
  margin: 0;
  background: #fff;
  color: var(--ink);
  font-feature-settings: "cv02", "cv03", "cv04", "cv11";
  -webkit-font-smoothing: antialiased;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  .landing-body { scroll-behavior: auto; }
}

/* ---- Marketing scroll unlock ---------------------------------------------
 * layouts.css freezes html/body for the app shell; the marketing pages have no
 * inner scroll container, so re-enable document scrolling on LandingLayout
 * pages. Wins over layouts.css by source order (bundled after it). */
html:has(body.landing-body) {
  overflow: auto;
  block-size: auto;
  overscroll-behavior: auto;
}
body.landing-body {
  position: static;
  overflow: visible;
}

/* ---- Layout primitives --------------------------------------------------- */
.landing-container {
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 32px;
}
.landing-section {
  padding: clamp(72px, 11vh, 112px) 0;
  background: #fff;
}
.landing-warm { background: var(--warm); }
.landing-dark { background: var(--dark); color: #fff; }

/* Anchor targets clear the pill nav. */
.landing-anchored { scroll-margin-top: 90px; }

/* ---- Headline pattern: Lora serif accent first, Inter sans below ---------- */
.landing-duo { display: block; }
.landing-duo.is-center { text-align: center; }
.landing-h-serif,
.landing-h-sans { display: block; }
.landing-h-serif {
  font-family: "Lora", Georgia, "Times New Roman", serif;
  font-weight: 500;
  font-style: normal;
  letter-spacing: -0.01em;
  font-size: clamp(1.5rem, 3.4vw, 2.125rem);
  line-height: 1.25;
  color: var(--ink);
}
.landing-h-sans {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: -0.025em;
  font-size: clamp(2rem, 5.2vw, 3.25rem);
  line-height: 1.1;
  color: var(--ink);
}
.landing-dark .landing-h-serif,
.landing-dark .landing-h-sans { color: #fff; }

.landing-section-head { margin-bottom: clamp(40px, 6vw, 64px); }

/* Eyebrow: Inter 600 14px uppercase gold with a gold dot (gem .bs-eyebrow). */
.bs .bs-eyebrow {
  color: var(--gold-deep);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-bottom: 18px;
}
.bs .bs-eyebrow::before { background: var(--gold); }

/* ---- Buttons: gold variant (hero CTA + form submit) ---------------------- */
.landing-btn-gold,
.bs .bs-btn-gold {
  background: var(--gold);
  color: #241a05;
  border: none;
}
.landing-btn-gold:hover,
.bs .bs-btn-gold:hover {
  background: #d9a33f;
  transform: translateY(-1px);
}

/* Hero CTA — espresso ink (form submit in the final CTA stays gold). */
.landing-btn-espresso,
.bs .landing-btn-espresso {
  background: var(--espresso);
  color: #f7f3ea;
  border: none;
}
.landing-btn-espresso:hover,
.bs .landing-btn-espresso:hover {
  background: #2c2519;
  transform: translateY(-1px);
  box-shadow: 0 10px 26px rgba(45, 37, 20, 0.28);
}

/* Nav-scoped espresso CTA: same espresso fill as the hero CTA but without the
 * primary button's drop shadow, so the pill/mobile "Beta anfragen" button reads
 * flat against the transparent nav (hero CTA keeps its own subtle shadow). */
.landing-body .bs-nav-pill .landing-btn-espresso,
.landing-body .bs-nav-mobile .landing-btn-espresso { box-shadow: none; }
.landing-body .bs-nav-pill .landing-btn-espresso:hover,
.landing-body .bs-nav-mobile .landing-btn-espresso:hover { box-shadow: none; }

/* ---- Nav overrides -------------------------------------------------------- */
.landing-nav-login {
  color: var(--ink);
  font-weight: 600;
  font-size: 14.5px;
  padding: 12px 8px;
}
.landing-nav-login:hover { color: var(--gold-deep); }
/* Desktop pill CTA ("Beta anfragen"): the gem's :sm button (40px / 15px) reads
 * too large next to the 14.5px nav links and the Login text. Scope to .actions
 * so only the pill's desktop CTA shrinks — the full-width mobile CTA in .mobile
 * keeps the gem defaults. --bh (not height) is overridden so the button's inner
 * flex layout stays intact. */
.landing-body .bs-nav-pill .actions .bs-btn {
  --bh: 36px;
  font-size: 14px;
}
/* The gem's `.actions` flex row has no `align-items`, so it defaults to
 * `stretch`: the taller Login link (14.5px text + 12px vertical padding ≈ 41px)
 * sets the row height while the 36px CTA aligns to the top, leaving the button's
 * mid-line above the Login mid-line. Center the row so button, Login link and the
 * `align-items: center` pill all share one vertical center (Follow-up #834). */
.landing-body .bs-nav-pill .actions {
  align-items: center;
}
/* Mobile: give the floating pill a little more room. The gem sets a 16px inset
 * per side (`.bs.bs-nav-pill { padding: 14px 16px 0 }`), leaving the pill 361px
 * wide at a 393px viewport. Trim the inset to 10px so the pill (with its burger
 * now visible, #1243) is 12px wider; the `.bs.` prefix matches the gem's own
 * 0-3-0 specificity so this reliably wins over the gem base. */
@media (width <= 760px) {
  .landing-body .bs.bs-nav-pill {
    padding: 14px 10px 0;
  }
}
/* Nav-morph (§1): at the top the pill is fully transparent and wide (~1240px)
 * so the hero gradient reads underneath it; past the gem controller's 12px
 * scroll threshold (`scrolled`) it condenses to a ~920px frosted white capsule.
 * Overrides the gem's fixed `.wrap { width: min(860px,100%) }` by animating
 * max-width on a full-width wrap. */
.landing-body .bs-nav-pill .wrap {
  width: 100%;
  max-width: 1240px;
  transition: max-width 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.landing-body .bs-nav-pill.scrolled .wrap {
  max-width: 920px;
}
.landing-body .bs-nav-pill .pill {
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* Balance the gem's asymmetric pill padding (left 22px / right 11px): match the
   * left inset so the CTA no longer clings to the pill border. `padding` is kept in
   * the transition list so the delta animates with the scroll morph (#834). */
  padding-right: 22px;
  transition: background 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease,
    padding 0.35s ease;
}
.landing-body .bs-nav-pill.scrolled .pill {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid #eae6dc;
  box-shadow: 0 12px 34px rgba(60, 48, 20, 0.08);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  /* Keep the extra right padding in the compact (scrolled) state too — the gem
   * shrinks it to 6px, which would let the CTA cling to the edge again. Balanced
   * against the compact left inset (18px) so the capsule stays symmetric (#834). */
  padding-right: 16px;
}
@media (prefers-reduced-motion: reduce) {
  .landing-body .bs-nav-pill .wrap,
  .landing-body .bs-nav-pill .pill { transition: none; }
}

/* Mobile-Menü: statt des display-Sprungs des Gems klappt die Karte aus der
 * Pill — sie fällt mit leichtem Scale nach unten auf, die Einträge staffeln
 * sich nach. Das Menü bleibt im DOM (display: flex) und wird über
 * opacity/visibility geschaltet, sonst wäre nichts animierbar. Der
 * `.landing-body .bs.`-Prefix übertrumpft die 0-3-0-Basisregeln des Gems. */
@media (width <= 760px) {
  .landing-body .bs.bs-nav-pill .bs-nav-mobile {
    display: flex;
    flex-direction: column;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-10px) scale(0.97);
    transform-origin: top center;
    transition: opacity 0.24s ease, transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1),
      visibility 0s linear 0.3s;
  }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition: opacity 0.24s ease, transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile > * {
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity 0.22s ease, transform 0.26s cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open > * {
    opacity: 1;
    transform: none;
  }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open > *:nth-child(1) { transition-delay: 0.04s; }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open > *:nth-child(2) { transition-delay: 0.08s; }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open > *:nth-child(3) { transition-delay: 0.12s; }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open > *:nth-child(4) { transition-delay: 0.16s; }
  .landing-body .bs.bs-nav-pill .bs-nav-mobile.open > *:nth-child(5) { transition-delay: 0.2s; }
  /* Kein Strich unter dem letzten Link — die Karte schließt selbst ab; der
   * Login folgt als Gem-Outline-Button (Abstand kommt aus der Gem-Basisregel). */
  .landing-body .bs.bs-nav-pill .bs-nav-mobile a:nth-last-child(2) { border-bottom: 0; }
  /* Login-Button in der Sekundärfarbe der Landing (Gold). Die volle border
   * stellt zugleich die untere Kante wieder her, die die Gem-Basisregel
   * `.bs-nav-mobile .bs-btn { border-bottom: 0 }` abstreift (Pill wirkte
   * sonst unten abgeschnitten). */
  .landing-body .bs.bs-nav-pill .bs-nav-mobile .bs-btn-outline {
    color: var(--gold-deep);
    border: 1px solid color-mix(in srgb, var(--gold-deep) 45%, transparent);
  }
  @media (prefers-reduced-motion: reduce) {
    .landing-body .bs.bs-nav-pill .bs-nav-mobile,
    .landing-body .bs.bs-nav-pill .bs-nav-mobile > * { transition: none; }
  }
}

/* ---- Hero ----------------------------------------------------------------
 * Pulled up under the transparent sticky pill nav (negative margin equal to the
 * nav height, compensated by extra padding-top) so the warm gradient begins at
 * the very top of the page with no white strip above it. */
.landing-hero {
  position: relative;
  overflow: clip;
  margin-top: -88px;
  padding: calc(clamp(40px, 8vh, 88px) + 88px) 0 0;
  text-align: center;
  background: radial-gradient(1200px 600px at 50% -10%, #efe5cd 0%, rgba(247, 243, 234, 0.7) 55%, #fff 100%);
}
/* Mobile: der clamp läuft auf ~8vh (≈68px) Luft zwischen Pill und Headline —
 * zu viel auf kleinen Screens, deshalb dort ein fester, knapperer Einstieg
 * (die +88px kompensieren weiterhin den negativen margin unter der Nav). */
@media (width <= 760px) {
  .landing-hero { padding-top: calc(28px + 88px); }
}
.landing-hero-copy {
  position: relative;
  z-index: 3;
  max-width: 760px;
  margin: 0 auto;
}
.landing-hero-title { margin: 0 0 22px; }
.landing-hero-title .landing-h-serif { font-size: clamp(1.8rem, 4vw, 2.75rem); line-height: 1.2; margin-bottom: 10px; }
.landing-hero-title .landing-h-sans { font-size: clamp(2.1rem, 6.4vw, 4rem); line-height: 1.06; letter-spacing: -0.03em; }
.landing-hero-sub {
  font-size: 1.15rem;
  color: var(--sub);
  max-width: 620px;
  margin: 30px auto 32px;
  line-height: 1.65;
  text-wrap: balance;
}
.landing-hero-cta { display: flex; flex-direction: column; align-items: center; gap: 16px; margin-top: 42px; }
.landing-hero-alt { color: var(--sub); font-size: 0.98rem; }
.landing-hero-alt a { color: var(--gold-deep); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }

/* Dashboard stage — overlaps −120px into the next (timeline) section. */
.landing-stage {
  position: relative;
  z-index: 3;
  max-width: 1040px;
  margin: clamp(40px, 6vh, 64px) auto -120px;
  padding: 0 20px;
}
.landing-window { box-shadow: 0 40px 90px -40px rgba(40, 32, 12, 0.45); }
.landing-window-body {
  display: grid;
  grid-template-columns: 190px 1fr 300px;
  background: #fff;
  text-align: left;
  min-height: 420px;
}
.landing-mock-side { border-right: 1px solid var(--hair); padding: 18px 14px; display: flex; flex-direction: column; gap: 4px; }
.landing-mock-nav { padding: 9px 12px; border-radius: 9px; font-size: 0.9rem; font-weight: 600; color: var(--sub); }
.landing-mock-nav.is-active { background: var(--warm-deep); color: var(--ink); }
.landing-mock-main { padding: 22px 24px; border-right: 1px solid var(--hair); }
.landing-mock-greet { font-size: 1.15rem; }
.landing-mock-date { color: var(--sub); font-size: 0.86rem; margin-top: 4px; }
.landing-mock-quick { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0 20px; }
.landing-mock-qbtn { font-size: 0.78rem; font-weight: 600; color: var(--ink); background: var(--warm); border: 1px solid var(--hair); border-radius: 999px; padding: 7px 12px; }
.landing-mock-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.landing-mock-cal-title { font-weight: 700; color: var(--ink); }
.landing-mock-cal-pill { font-size: 0.74rem; font-weight: 600; color: var(--gold-deep); background: rgba(200, 145, 43, 0.12); border-radius: 999px; padding: 4px 10px; }
.landing-mock-cal-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; font-size: 0.68rem; color: var(--fg-muted, #8b8798); font-weight: 600; margin-bottom: 4px; }
.landing-mock-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.landing-mock-day { position: relative; aspect-ratio: 1; border-radius: 8px; padding: 4px; font-size: 0.72rem; background: var(--warm); }
.landing-mock-day.is-muted { background: transparent; color: var(--fg-muted, #b8b3a4); }
.landing-mock-day.is-today { outline: 2px solid var(--gold); }
.landing-mock-day.has-event { background: rgba(200, 145, 43, 0.14); }
.landing-mock-daynum { font-weight: 600; color: var(--ink); }
.landing-mock-event { display: block; margin-top: 2px; font-size: 0.56rem; line-height: 1.1; color: var(--gold-deep); font-weight: 600; overflow: hidden; }
.landing-mock-feed { padding: 22px 18px; }
.landing-mock-feed h4 { font-size: 0.95rem; margin-bottom: 12px; }
.landing-mock-feed-list { display: flex; flex-direction: column; gap: 14px; }
.landing-mock-feed-item { display: flex; gap: 10px; }
.landing-mock-avatar { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; font-size: 0.72rem; font-weight: 700; background: #e7e3d8; color: var(--ink); }
.landing-mock-avatar.is-accent { background: var(--gold); color: #241a05; }
.landing-mock-feed-title { font-size: 0.8rem; font-weight: 700; color: var(--ink); line-height: 1.25; }
.landing-mock-feed-sub { font-size: 0.74rem; color: var(--sub); margin-top: 2px; }
.landing-mock-tag { display: inline-block; margin-top: 5px; font-size: 0.66rem; font-weight: 600; border-radius: 999px; padding: 2px 8px; }
.landing-mock-tag--rose { background: #fbe7ec; color: #b03a5b; }
.landing-mock-tag--green { background: #e6f5ea; color: #1c7a3a; }
.landing-mock-tag--gold { background: rgba(200, 145, 43, 0.16); color: var(--gold-deep); }

@media (width <= 960px) {
  .landing-window-body { grid-template-columns: 1fr; min-height: 0; }
  .landing-mock-side, .landing-mock-feed { display: none; }
  .landing-mock-main { border-right: none; }
  .landing-stage { margin-bottom: -80px; }
}

/* ---- Abend timeline ------------------------------------------------------ */
/* Compensates the desktop stage overlap (.landing-stage margin-bottom: -120px)
   for 32px air. Mobile override lives in the @media (width <= 960px) block. */
.landing-abend { padding-top: 152px; }
.landing-abend-inner { max-width: 620px; }
.landing-abend .landing-duo { margin-bottom: clamp(28px, 5vw, 44px); }
.landing-abend-list { position: relative; margin: 0; padding: 0 0 0 28px; list-style: none; }
.landing-abend-list::before { content: ""; position: absolute; left: 4px; top: 11px; bottom: 8px; width: 2px; background: var(--hair); }
.landing-abend-item { position: relative; padding-bottom: clamp(24px, 4vh, 40px); }
.landing-abend-item:last-child { padding-bottom: 0; }
/* Punkt vertikal mittig zur 20px/32px-Zeitzeile: Zeilenmitte 16px − halber
 * Punkt (5px) = top 11px. */
.landing-abend-item::before { content: ""; position: absolute; left: -28px; top: 11px; width: 10px; height: 10px; border-radius: 50%; background: var(--gold); box-shadow: 0 0 0 4px var(--warm); }
.landing-abend-time { display: block; font-family: "InterVariable", "Inter", system-ui, sans-serif; font-weight: 600; font-size: 20px; color: var(--gold-deep); font-variant-numeric: tabular-nums; margin-bottom: 6px; }
.landing-abend-text { color: var(--sub); margin: 0; }

/* ---- Features ------------------------------------------------------------ */
.landing-feat-row { display: grid; grid-template-columns: 1fr 1fr; gap: 72px; align-items: center; margin-top: clamp(56px, 8vw, 96px); }
.landing-feat-row:first-of-type { margin-top: 0; }
.landing-feat-row.is-flip .landing-feat-copy { order: 2; }
.landing-feat-row.is-flip .landing-feat-visual { order: 1; }
/* h3 bewusst regular (400): der editoriale Kontrast zur 700er-Sans-Headline —
   ohne explizites Gewicht hinge er nur am Preflight-inherit. */
.landing-feat-copy h3 { font-size: clamp(1.5rem, 2.6vw, 2rem); font-weight: 400; line-height: 1.15; margin: 0 0 14px; }
.landing-feat-copy p { color: var(--sub); }
.landing-checks { margin-top: 18px; display: flex; flex-direction: column; gap: 10px; }
.landing-checks li { position: relative; padding-left: 28px; color: var(--ink); font-weight: 500; }
.landing-checks li::before { content: "✓"; position: absolute; left: 0; top: 0; color: var(--gold-deep); font-weight: 700; }

.landing-feat-visual > * { border-radius: 18px; }

/* Feature 1 — branded gallery card. */
.landing-galcard { background: #fff; border: 1px solid var(--hair); border-radius: 18px; overflow: hidden; box-shadow: 0 24px 60px -30px rgba(40, 32, 12, 0.35); }
.landing-galcard-photo { position: relative; aspect-ratio: 16 / 10; }
.landing-galcard-photo img { width: 100%; height: 100%; object-fit: cover; }
.landing-galcard-dots { position: absolute; top: 12px; left: 12px; display: flex; gap: 6px; }
.landing-galcard-dots span { width: 14px; height: 14px; border-radius: 50%; background: var(--dot); border: 2px solid #fff; }
.landing-galcard-dots span.is-active { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--dot); }
.landing-galcard-meta { padding: 18px 20px; }
.landing-galcard-cat { font-size: 0.76rem; color: var(--gold-deep); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.landing-galcard-meta h4 { font-size: 1.15rem; margin: 6px 0 4px; }
.landing-galcard-sub { font-size: 0.85rem; color: var(--sub); }

/* Feature 2 — live comments panel. */
.landing-commentsmock { background: #fff; border: 1px solid var(--hair); border-radius: 18px; padding: 18px; box-shadow: 0 24px 60px -30px rgba(40, 32, 12, 0.35); }
.landing-comments-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.landing-comments-pills span { font-size: 0.78rem; font-weight: 600; color: var(--sub); background: var(--warm); border-radius: 999px; padding: 6px 12px; }
.landing-comments-pills span.is-active { background: var(--ink); color: #fff; }
.landing-comment { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; border-top: 1px solid var(--hair); }
.landing-comment:first-of-type { border-top: none; }
.landing-comment-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 10px; flex: 0 0 auto; }
.landing-comment-body { flex: 1; }
.landing-comment-gallery { font-weight: 700; color: var(--ink); font-size: 0.86rem; }
.landing-comment-bubble { margin: 4px 0; color: var(--ink); background: var(--warm); border-radius: 12px; padding: 8px 12px; font-size: 0.86rem; }
.landing-comment-meta { font-size: 0.75rem; color: var(--sub); }
.landing-comment-heart { color: #d5486a; align-self: center; }
@media (prefers-reduced-motion: no-preference) {
  .landing-comment-heart.is-pulse { animation: landing-pulse 1.6s ease-in-out infinite; }
}
@keyframes landing-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.25); }
}

/* Feature 3 — watermarked preview + downloads toggle. */
.landing-wmmock { background: #fff; border: 1px solid var(--hair); border-radius: 18px; overflow: hidden; box-shadow: 0 24px 60px -30px rgba(40, 32, 12, 0.35); }
.landing-wmmock-photo { position: relative; aspect-ratio: 16 / 10; display: grid; place-items: center; overflow: hidden; }
.landing-wmmock-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.landing-wmmock-mark { position: relative; z-index: 1; transform: rotate(-14deg); font-family: "Lora", serif; font-size: 2rem; font-weight: 600; color: rgba(255, 255, 255, 0.72); text-shadow: 0 2px 8px rgba(0, 0, 0, 0.35); letter-spacing: 0.04em; }
.landing-wmmock-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 18px; }
.landing-wmmock-label { font-size: 0.86rem; color: var(--ink); font-weight: 600; }
.landing-wmmock-toggle { display: inline-flex; align-items: center; gap: 8px; font-size: 0.82rem; font-weight: 600; color: #1c7a3a; }
.landing-wmmock-knob { width: 34px; height: 20px; border-radius: 999px; background: #cfe9d3; position: relative; }
.landing-wmmock-toggle.is-on .landing-wmmock-knob { background: #34a853; }
.landing-wmmock-knob::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform 0.2s ease-out; }
.landing-wmmock-toggle.is-on .landing-wmmock-knob::after { transform: translateX(14px); }

/* ---- Steps --------------------------------------------------------------- */
.landing-steps-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: clamp(40px, 6vw, 64px); }
.landing-step-card { background: #fff; border: 1px solid var(--hair); border-radius: 16px; padding: 28px 24px; }
.landing-step-num { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px; background: var(--warm-deep); color: var(--gold-deep); font-weight: 700; font-size: 1.15rem; margin-bottom: 16px; }
.landing-step-card h3 { font-size: 1.2rem; font-weight: 400; margin-bottom: 8px; }
.landing-step-card p { color: var(--sub); }

/* ---- Chips band (dark) --------------------------------------------------- */
.landing-chips-band { text-align: center; padding: 88px 0; }
.landing-chips-label { font-family: "Lora", serif; font-weight: 500; font-size: clamp(1.4rem, 3vw, 2rem); color: #fff; margin-bottom: clamp(40px, 6vw, 64px); }
.landing-chips { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; list-style: none; padding: 0; margin: 0; }
.landing-chip { border: 1px solid rgba(255, 255, 255, 0.22); color: rgba(255, 255, 255, 0.9); border-radius: 999px; padding: 10px 18px; font-weight: 600; font-size: 0.95rem; transition: border-color 0.18s, color 0.18s; }
.landing-chip:hover { border-color: var(--gold); color: var(--gold); }

/* ---- Icon grids (audience / trust) --------------------------------------- */
.landing-icon-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: clamp(40px, 6vw, 64px); }
.landing-icard { background: #fff; border: 1px solid var(--hair); border-radius: 16px; padding: 24px; transition: transform 0.18s; }
.landing-warm .landing-icard { background: #fff; }
.landing-icard:hover { transform: translateY(-3px); }
.landing-icard-icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px; background: #f5ead0; color: var(--gold-deep); font-size: 1.2rem; margin-bottom: 14px; }
.landing-trust .landing-icard-icon { background: #e6f5ea; color: #1c7a3a; border-radius: 50%; }
.landing-icard h3 { font-size: 1.1rem; font-weight: 400; margin-bottom: 8px; }
.landing-icard p { color: var(--sub); font-size: 0.92rem; }

/* ---- Founder note + beta facts ------------------------------------------- */
.landing-founder-card { max-width: 840px; margin: 0 auto; background: #fdfcf8; border: 1px solid var(--hair); border-radius: 20px; padding: clamp(28px, 5vw, 48px); }
.landing-founder-quote { font-family: "Lora", serif; font-size: 1.375rem; line-height: 1.5; color: var(--ink); margin: 0; }
.landing-founder-who { display: flex; align-items: center; gap: 14px; margin-top: 24px; }
.landing-founder-avatar { width: 72px; height: 72px; border-radius: 50%; display: grid; place-items: center; background: #232c3d; color: #fff; font-weight: 700; font-size: 1.4rem; }
.landing-founder-name { font-weight: 700; color: var(--ink); }
.landing-founder-role { color: var(--sub); font-size: 0.9rem; }
.landing-facts { max-width: 840px; margin: 32px auto 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.landing-fact { background: #fdfcf8; border: 1px dashed #d9d0ba; border-radius: 16px; padding: 22px; }
.landing-fact-label { display: block; font-size: 0.76rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--gold-deep); margin-bottom: 8px; }
.landing-fact p { color: var(--sub); font-size: 0.92rem; }

/* ---- FAQ overrides ------------------------------------------------------- */
.landing-faq { scroll-margin-top: 90px; }
.landing-faq .bs-eyebrow { justify-content: center; }
/* Angleichung an den Sektions-Rhythmus: die Gem-FAQ bringt eigene Maße mit
   (.bs-section-head p → 18px-Eyebrow, .inner → 88/96px Padding); hier gelten
   dieselbe 14px-Eyebrow und dasselbe Padding wie in jeder .landing-section. */
.landing-faq .bs-section-head .bs-eyebrow { font-size: 14px; margin-top: 0; }
.landing-faq .inner { padding-top: clamp(72px, 11vh, 112px); padding-bottom: clamp(72px, 11vh, 112px); }
.landing-faq summary { font-weight: 600; color: var(--ink); }
.landing-faq details { border-color: var(--hair); }
.landing-faq .chev { color: var(--gold-deep); }

/* ---- Final CTA (dark) + darkform ----------------------------------------- */
.landing-finalcta { padding: 120px 0; }
.landing-finalcta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(40px, 6vw, 72px); align-items: start; }
.landing-finalcta-lead { color: rgba(255, 255, 255, 0.8); margin: 20px 0 24px; max-width: 440px; }
.landing-finalcta-points { display: flex; flex-direction: column; gap: 12px; }
.landing-finalcta-points li { position: relative; padding-left: 28px; color: #fff; font-weight: 500; }
.landing-finalcta-points li::before { content: "✓"; position: absolute; left: 0; color: var(--gold); font-weight: 700; }

.landing-beta-form {
  max-width: 100%;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 20px;
  padding: clamp(24px, 4vw, 36px);
  display: grid;
  gap: 14px;
  text-align: left;
}
.landing-beta-form-title { font-family: "Lora", serif; font-weight: 600; font-size: 1.4rem; color: #fff; margin: 0 0 4px; }
.landing-beta-form .bs-field { display: grid; gap: 6px; }
.landing-beta-form .bs-field label { color: rgba(255, 255, 255, 0.92); font-size: 0.9rem; font-weight: 600; }
.landing-field-opt { color: rgba(255, 255, 255, 0.65); font-weight: 400; font-style: italic; }
.landing-beta-form .bs-input {
  width: 100%;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 10px;
  padding: 12px 14px;
  color: #fff;
  font: inherit;
}
.landing-beta-form .bs-input::placeholder { color: #8d8776; }
.landing-beta-form .bs-input:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.landing-beta-form .bs-textarea { resize: vertical; min-height: 84px; }
.landing-beta-submit { width: 100%; justify-content: center; }
.landing-beta-form .bs-form-note { color: rgba(255, 255, 255, 0.7); font-size: 0.8rem; margin: 0; }
.landing-beta-form .bs-error { color: #ff9a9a; font-size: 0.82rem; margin: 0; }

/* Invisible honeypot — kept out of view and out of the tab order. */
.landing-beta-form .bs-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.landing-beta-success {
  background: rgba(255, 255, 255, 0.95);
  color: var(--ink);
  border-radius: 20px;
  padding: 28px;
  text-align: center;
}
.landing-beta-success h3 { margin-bottom: 8px; }

/* ---- Footer -------------------------------------------------------------- */
.landing-footer.bs[data-theme="dark"] { background: #12100d; }
.landing-footer-tag { color: rgba(255, 255, 255, 0.62); margin: 8px 0 0; }
.landing-footer-copy { color: rgba(255, 255, 255, 0.5); font-size: 0.9rem; margin: 0; }
/* Brand logo fully white in the footer (mark + "pic" + "gate"). */
.landing-body .landing-footer .brand-logo,
.landing-body .landing-footer .brand-logo__gate { color: #fff; }
.landing-body .landing-footer .brand-logo__frame,
.landing-body .landing-footer .brand-logo__cell { fill: #fff; }

/* Nav/footer brand mark: warm gold on light surfaces (marketing only). */
.landing-body .brand-logo__frame { fill: var(--ink); }
.landing-body .brand-logo__cell { fill: var(--gold); }
.landing-body .brand-logo__gate { color: var(--gold); }

/* ---- Responsive ---------------------------------------------------------- */
@media (width <= 960px) {
  .landing-feat-row { grid-template-columns: 1fr; gap: 32px; }
  .landing-feat-row.is-flip .landing-feat-copy,
  .landing-feat-row.is-flip .landing-feat-visual { order: 0; }
  .landing-steps-grid { grid-template-columns: 1fr; }
  .landing-icon-grid { grid-template-columns: repeat(2, 1fr); }
  .landing-facts { grid-template-columns: 1fr; }
  .landing-finalcta-grid { grid-template-columns: 1fr; }
  .landing-hero-title .landing-h-serif { font-size: 1.875rem; }
  .landing-hero-title .landing-h-sans { font-size: 2.625rem; }
  .landing-duo .landing-h-serif { font-size: 1.625rem; }
  .landing-duo .landing-h-sans { font-size: 2.25rem; }

  /* Unified mobile section rhythm: standard seam ~112px (2x56), width-coupled
     instead of height-coupled (no vh). Dark accent bands breathe slightly more. */
  .landing-section { padding: clamp(56px, 12vw, 72px) 0; }
  /* Stage overlap shrinks to -80px on mobile (see .landing-stage above); 116px
     top padding leaves 36px visible air over the timeline head — knapper als
     der Standard-Rhythmus, dafür bekommt der Head unten eigenen Abstand zur
     Liste. If the -80px overlap changes, adjust this too. */
  .landing-abend { padding-top: 116px; }
  .landing-chips-band { padding: 64px 0; }
  .landing-finalcta { padding: 72px 0; }
  /* App-only override of the gem's .bs-faq-list .inner (88/96) — scoped under
     .landing-faq so Docs/gem-demo FAQ surfaces stay untouched. */
  .landing-faq .bs-faq-list .inner { padding-block: 56px 64px; }
}
@media (width <= 560px) {
  .landing-container { padding: 0 20px; }
  .landing-icon-grid { grid-template-columns: 1fr; }
}

/* ---- Active nav item (e.g. "Blog" on the blog surfaces) ------------------ */
.landing-body .bs-nav-pill .links a[aria-current="page"],
.landing-body .bs-nav .bs-nav-mobile a[aria-current="page"] {
  color: var(--gold-deep);
}

/* ---- Standalone /kontakt page: centered heading + form card --------------
 * The contact section (ContactSectionComponent) is only rendered on /kontakt
 * now; give it the warm editorial chrome and lift the form into a card so it no
 * longer reads as a raw offset form block. */
.landing-body .landing-contact {
  padding: clamp(96px, 14vh, 160px) 0 clamp(56px, 9vh, 104px);
  background: var(--warm);
}
.landing-body .landing-contact .inner {
  max-width: 640px;
}
.landing-body .landing-contact-head {
  text-align: center;
  margin-bottom: clamp(28px, 4vw, 40px);
}
.landing-body .landing-contact-sub {
  margin: 16px auto 0;
  max-width: 460px;
  color: var(--sub);
  font-size: 1.0625rem;
  line-height: 1.6;
}
.landing-body .landing-contact-card {
  background: #fff;
  border: 1px solid var(--hair);
  border-radius: 20px;
  padding: clamp(28px, 4vw, 40px);
  box-shadow: 0 24px 60px -34px rgba(25, 23, 19, 0.4);
}
.landing-body .landing-contact-card .bs-btn-primary {
  width: 100%;
}


/* ===== pages/blog.css ===== */
/* ============================================================================
 * Public marketing blog (Views::Blog::Index + Views::Blog::Post) — "Editorial &
 * Warm" look (issue #689), aligned with the landing redesign (#687). App layer
 * on top of the gem's landing_*.css: the /blog card list and the centered
 * single-article prose column, both reading the warm/gold editorial tokens
 * (--ink, --sub, --hair, --gold, --gold-deep, --warm, --dark) defined in
 * pages/landing.css. Inter for text, Lora for the serif accents (headline
 * accent line + article lead). Served raw by Propshaft (no Tailwind build) —
 * plain CSS only; selectors stay page-scoped since this file is unlayered.
 * ========================================================================== */

/* ---- Index: heading + card list ----------------------------------------- */
.blog-index {
  padding: clamp(48px, 7vh, 88px) 0 clamp(56px, 9vh, 104px);
  background: var(--warm);
}
.blog-index-head {
  max-width: 760px;
  margin: 0 auto 44px;
  text-align: center;
}
.blog-index-head .landing-h-serif {
  font-size: clamp(1.5rem, 3vw, 1.875rem);
}
.blog-index-head .landing-h-sans {
  font-size: clamp(2.125rem, 5vw, 2.875rem);
}
.blog-index .blog-index-intro {
  margin: 18px auto 0;
  max-width: 560px;
  color: var(--sub);
  font-size: 1.0625rem;
  line-height: 1.6;
}

.blog-index .blog-card-list {
  list-style: none;
  margin-inline: auto;
  padding: 0;
  display: grid;
  gap: 16px;
  max-width: 860px;
}
.blog-card {
  border-radius: 18px;
  background: #fff;
  border: 1px solid var(--hair);
  box-shadow: 0 1px 2px rgba(25, 23, 19, 0.03);
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.blog-card:hover {
  transform: translateY(-2px);
  border-color: #d5cfc0;
  box-shadow: 0 18px 40px -24px rgba(25, 23, 19, 0.4);
}
.blog-card-link {
  display: block;
  padding: 30px 34px;
  text-decoration: none;
  color: inherit;
}
.blog-card-meta {
  display: block;
  font-size: 13.5px;
  color: var(--sub);
  margin-bottom: 10px;
}
.blog-card-title {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-size: clamp(1.25rem, 2.4vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  margin: 0 0 10px;
  color: var(--ink);
}
.blog-card .blog-card-excerpt {
  color: var(--sub);
  margin: 0 0 16px;
  line-height: 1.65;
}
.blog-card-more {
  font-weight: 600;
  color: var(--gold-deep);
  font-size: 0.95rem;
  transition: color 0.16s ease;
}
.blog-card:hover .blog-card-more {
  color: var(--ink);
}

/* ---- Article: centered prose column ------------------------------------- */
.blog-article {
  display: block;
  padding: clamp(88px, 13vh, 148px) 0 clamp(56px, 9vh, 104px);
  background: #fff;
}
.blog-prose {
  max-width: 920px;
}
.blog-article-head {
  margin-bottom: 36px;
}
.blog-breadcrumb {
  display: inline-block;
  margin-bottom: 20px;
  font-size: 14px;
  font-weight: 600;
  color: var(--gold-deep);
  text-decoration: none;
  transition: color 0.16s ease;
}
.blog-breadcrumb:hover {
  color: var(--ink);
}
.blog-article-meta {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--sub);
}
.blog-article-head h1 {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-size: clamp(1.9rem, 4.4vw, 2.625rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  margin: 0;
  color: var(--ink);
}
.blog-author {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 22px;
}
.blog-author-avatar {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #232c3d;
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.blog-author-name {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink);
}

.blog-article-body > p:first-of-type {
  font-family: "Lora", Georgia, "Times New Roman", serif;
  font-size: 1.1875rem;
  line-height: 1.6;
  color: var(--ink);
}
.blog-prose h2 {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-size: 1.625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  margin: 44px 0 14px;
  color: var(--ink);
}
.blog-prose p {
  color: var(--sub);
  font-size: 1.03125rem;
  line-height: 1.75;
  margin: 0 0 18px;
}

/* ---- Closing beta CTA (dark card) --------------------------------------- */
.blog-cta {
  margin-top: 56px;
  padding: clamp(32px, 5vw, 48px);
  border-radius: 20px;
  text-align: center;
  color: #fff;
  background: var(--dark);
}
.blog-cta-serif {
  font-family: "Lora", Georgia, "Times New Roman", serif;
  font-weight: 500;
  font-size: 1.375rem;
  line-height: 1.25;
  color: #fff;
  margin: 0;
}
.blog-cta-sans {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(1.75rem, 4vw, 2.25rem);
  letter-spacing: -0.025em;
  line-height: 1.1;
  color: #fff;
  margin: 4px 0 0;
}
.blog-cta-text {
  color: rgba(255, 255, 255, 0.82);
  max-width: 480px;
  margin: 18px auto 24px;
  line-height: 1.65;
  font-size: 1.0625rem;
}
.blog-cta .blog-cta-btn.bs-btn-primary {
  background: #fff;
  color: var(--ink);
  border-color: #fff;
}
.blog-cta .blog-cta-btn.bs-btn-primary:hover {
  background: rgba(255, 255, 255, 0.9);
  border-color: rgba(255, 255, 255, 0.9);
}

/* ---- "Weiterlesen" next-article teaser ---------------------------------- */
.blog-nextread {
  margin-top: 48px;
}
.blog-nextread-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-deep);
  margin-bottom: 14px;
}
.blog-nextread-card {
  display: block;
  padding: 26px 30px;
  border-radius: 18px;
  border: 1px solid var(--hair);
  background: var(--warm);
  text-decoration: none;
  transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
}
.blog-nextread-card:hover {
  transform: translateY(-2px);
  border-color: #d5cfc0;
  box-shadow: 0 18px 40px -24px rgba(25, 23, 19, 0.4);
}
.blog-nextread-title {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-size: 1.1875rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  margin: 0 0 8px;
  color: var(--ink);
}
.blog-nextread-meta {
  display: block;
  font-size: 13.5px;
  color: var(--sub);
}


/* ===== pages/legal.css ===== */
/* ============================================================================
 * Public legal pages (Views::Legal::Impressum + Views::Legal::Datenschutz) —
 * "Editorial & Warm" look (issue #689), aligned with the landing redesign
 * (#687). App layer on top of the gem's landing_*.css: a narrow, centered prose
 * column reading the warm/gold editorial tokens (--ink, --sub, --hair,
 * --gold-deep) defined in pages/landing.css. A clear H1/H2 hierarchy and gold
 * underlined links; no CTA and no scroll reveals — legal texts should read
 * plainly. Served raw by Propshaft (no Tailwind build); selectors stay
 * page-scoped since this file is unlayered.
 * ========================================================================== */

.legal-article {
  display: block;
  padding: clamp(88px, 13vh, 148px) 0 clamp(56px, 9vh, 104px);
  background: #fff;
}
.legal-prose {
  max-width: 680px;
}
.legal-article-head {
  margin-bottom: 32px;
}
.legal-article-head h1 {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-size: clamp(2rem, 4.6vw, 2.75rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  margin: 0;
  color: var(--ink);
}
.legal-prose p.legal-lead {
  font-family: "Lora", Georgia, "Times New Roman", serif;
  font-size: 1.1875rem;
  line-height: 1.6;
  color: var(--ink);
}
.legal-prose h2 {
  font-family: "InterVariable", "Inter", system-ui, sans-serif;
  font-size: 1.375rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  margin: 48px 0 12px;
  color: var(--ink);
}
.legal-prose p {
  color: var(--sub);
  font-size: 1.03125rem;
  line-height: 1.75;
  margin: 0 0 18px;
}
.legal-prose ul {
  color: var(--sub);
  font-size: 1.03125rem;
  line-height: 1.75;
  margin: 0 0 18px;
  padding-left: 1.4em;
  list-style: disc;
}
.legal-prose li {
  margin: 0 0 8px;
}
.legal-prose a {
  color: var(--gold-deep);
  text-decoration: none;
  border-bottom: 1px solid rgba(163, 115, 24, 0.4);
  transition: border-color 0.16s ease, color 0.16s ease;
}
.legal-prose a:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}


/*
 * ── Größen-Token-Brücke ──────────────────────────────────────────────────
 * MUSS nach allen @import-Regeln stehen: eine Style-Regel vor einem @import
 * lässt Browser alle folgenden Imports ignorieren (= App komplett unstyled).
 *
 * Gap in the gem's size-token registry: base.css defines --size-3 and
 * --size-4 but skips 3_5, so every IconComponent(size: 3.5) resolves to an
 * undefined var and the icon silently breaks (7 call sites in this app,
 * e.g. the watermark-editor topbar icons). Bridge it here until the token
 * lands in the gem; drop this block once gui_components ships it.
 */
:root {
  --size-3_5: 0.875rem;
}
