/* Revoltaris Design System, Kopie. --ds-version 0.17.1-beta, entnommen 16.10.2026.
   Erzeugt von infra/initiativen/shop-eigenes-theme/umsetzung/ds-kopie-theme.py - nicht von Hand ändern. */
/* --- tokens/colors.css (unverändert) --- */
:root,
[data-theme] {
  /* ---- Brand -------------------------------------------------------- */
  /* ONE hardcoded brand colour. Every gold tint is derived from it in CSS -
     never paste a second gold hex anywhere. */
  --gold: #b98823;

  /* Ink that sits ON a solid gold fill. Gold is a mid-tone, so near-black wins
     in both themes. */
  --on-accent: #0a0a0a;

  /* Gold at the alpha steps the product actually uses. */
  --gold-a05: rgb(from var(--gold) r g b / 0.05);
  --gold-a08: rgb(from var(--gold) r g b / 0.08);
  --gold-a10: rgb(from var(--gold) r g b / 0.10);
  --gold-a14: rgb(from var(--gold) r g b / 0.14);
  --gold-a15: rgb(from var(--gold) r g b / 0.15);
  --gold-a22: rgb(from var(--gold) r g b / 0.22);
  --gold-a24: rgb(from var(--gold) r g b / 0.24);
  --gold-a30: rgb(from var(--gold) r g b / 0.30);
  --gold-a40: rgb(from var(--gold) r g b / 0.40);
  --gold-a60: rgb(from var(--gold) r g b / 0.60);

  /* ---- Backgrounds -------------------------------------------------------
     One rule, both themes: the page is a pure extreme, every step above it is
     the SAME page colour with a fixed percentage of the opposite colour mixed
     in. Dark mixes white into #000, light mixes black into #fff. Nothing here
     is hand-picked, so the two themes cannot drift apart. */
  --bg: #000000;
  --surface-step-1: 4%;   /* alternating section, footer */
  --surface-step-2: 5.5%; /* panel, CTA block, card */
  --surface-step-3: 7%;   /* raised panel */

  --bg-1: color-mix(in srgb, #ffffff var(--surface-step-1), var(--bg));
  --bg-2: color-mix(in srgb, #ffffff var(--surface-step-2), var(--bg));
  --bg-3: color-mix(in srgb, #ffffff var(--surface-step-3), var(--bg));
  --card: var(--bg-2);
  --nav-bg: rgba(0, 0, 0, 0.72);       /* fixed nav, backdrop-blurred */
  --glass-bg: rgba(22, 22, 22, 0.42);  /* floating glass bar: clearly see-through, the blur carries it */
  --glass-line: rgba(255, 255, 255, 0.12);
  --glass-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.07);
  --menu-bg: rgba(16, 16, 16, 0.97);   /* dropdown / mobile menu */

  /* Translucent white fills - the default card surface over any bg. */
  --fill-1: rgba(255, 255, 255, 0.03);
  --fill-2: rgba(255, 255, 255, 0.045);
  --fill-3: rgba(255, 255, 255, 0.06);
  --fill-4: rgba(255, 255, 255, 0.09);
  --fill-5: rgba(255, 255, 255, 0.12);

  /* ---- Hairlines ---------------------------------------------------- */
  --line: rgba(255, 255, 255, 0.09);   /* default border */
  --line-2: rgba(255, 255, 255, 0.14); /* hover / emphasised border */
  --line-3: rgba(255, 255, 255, 0.24); /* ghost-button border */

  /* ---- Ghost button (0.17.0) ----------------------------------------
     A step greyer than a card, so the secondary action stays visible on
     the black field and inside a card (Alex, 16.10.2026: too dark on --bg). */
  --ghost-fill: var(--fill-5);
  --ghost-fill-hover: rgba(255, 255, 255, 0.16);
  --ghost-line: var(--line-3);
  --ghost-line-hover: rgba(255, 255, 255, 0.34);

  /* ---- Ink ---------------------------------------------------------- */
  --fg: #ffffff;              /* headings, strong values */
  --mut: #bfbfbf;             /* body copy */
  --mut-2: #a8a8a8;           /* secondary copy, captions - a step stronger on the translucent cards since 0.17.0 (#9a9a9a before) */
  --dim: #808080;             /* legal, footnotes, micro-labels - 4.9:1 on a card (#787878 until 0.17.0) */

  /* ---- Semantic ----------------------------------------------------- */
  /* Ein Farbrad um das Markengold: acht 45-Grad-Schritte bei konstanter
     Sättigung (68.2%) und HSL-Helligkeit (43.1%). Jede Farbe ist eine reine
     Hue-Rotation von --gold - es bleibt bei genau einem hartcodierten Hex.

     Bewusste Entscheidung: konstante HSL-Helligkeit heisst NICHT konstante
     wahrgenommene Helligkeit. Gelbgrün ist von Natur aus hell (L 0.39),
     Blauviolett dunkel (L 0.07). Das Rad ist damit farblich stimmig, aber
     jede Farbe trägt nur in EINEM Theme als Text - siehe
     reference/colour.md, Ink contrast. */
  --success: hsl(from var(--gold) calc(h + 90) s l);   /* #23b93d */
  --info:    hsl(from var(--gold) calc(h + 180) s l);  /* #2354b9 */
  --danger:  hsl(from var(--gold) calc(h - 45) s l);   /* #b9232f */

  /* Offene Referenzpunkte auf demselben Rad. Keine Bedeutung zugewiesen -
     frei für Tagging-Systeme, Diagramme und Verläufe. */
  --hue-45:  hsl(from var(--gold) calc(h + 45) s l);   /* #7ab923 */
  --hue-135: hsl(from var(--gold) calc(h + 135) s l);  /* #23b9ad */
  --hue-225: hsl(from var(--gold) calc(h + 225) s l);  /* #6223b9 */
  --hue-270: hsl(from var(--gold) calc(h + 270) s l);  /* #b9239f */

  /* Text inks (0.17.0, Alex 16.10.2026: small text at 4.5:1). The wheel stays
     frozen; only a hue that is too dark for small text on a dark card is
     lifted in lightness for its TEXT, measured on its own 10% tint over a
     card. Fills, lines, dots and glyphs keep the hue itself. Gold is not
     touched (Alex: gold stays as it is). */
  --success-text: var(--success);
  --danger-text:  hsl(from var(--danger) h s calc(l + 16));
  --info-text:    hsl(from var(--info) h s calc(l + 16));
  --hue-45-text:  var(--hue-45);
  --hue-135-text: var(--hue-135);
  --hue-225-text: hsl(from var(--hue-225) h s calc(l + 21));
  --hue-270-text: hsl(from var(--hue-270) h s calc(l + 11));

  /* Only the FILL is an alpha tint - same principle as the gold tint rule.
     Icons, text and spines always use the defined colour at full strength.
     Borders are a TINTED line at the same weight as --gold-a22/--gold-a40, so a
     row of mixed tones (gold tile, danger alert, success alert) reads as one
     family instead of three different border weights. */
  --success-surface: rgb(from var(--success) r g b / 0.12);
  --danger-surface:  rgb(from var(--danger) r g b / 0.12);
  --info-surface:    rgb(from var(--info) r g b / 0.12);

  /* Quiet line - icon tiles, chips, anything glyph-sized. */
  --success-line:   rgb(from var(--success) r g b / 0.22);
  --danger-line:    rgb(from var(--danger) r g b / 0.22);
  --info-line:      rgb(from var(--info) r g b / 0.22);

  /* Emphasised line - alerts and panels that must be noticed. */
  --success-border: rgb(from var(--success) r g b / 0.40);
  --danger-border:  rgb(from var(--danger) r g b / 0.40);
  --info-border:    rgb(from var(--info) r g b / 0.40);

  /* ---- Semantic aliases (prefer these in new work) ------------------ */
  --text-heading: var(--fg);
  --text-body: var(--mut);
  --text-secondary: var(--mut-2);
  --text-faint: var(--dim);
  /* Micro-labels (card captions, column heads, group titles). Deliberately the
     HIGHEST-CONTRAST text value, not a weakened one: a label must read as a
     different KIND of text than body copy, and --mut / --dim only make it look
     like weaker body copy. Stated as contrast the rule holds in both themes -
     on dark that value is the lightest, on light the darkest. It separates by
     weight (700) and tracking, never by stepping down the ink. */
  --text-label: var(--fg);
  --text-accent: var(--gold);
  --text-link-hover: var(--gold);

  --surface-page: var(--bg);
  --surface-alt: var(--bg-1);
  --surface-panel: var(--bg-2);
  /* The ground under every see-through surface - card, button, chip, avatar:
     the page colour at 60%. Over a photo the surface still reads in its theme
     instead of letting the picture run through; the fill sits on top of it. */
  --surface-tint: rgb(from var(--bg) r g b / 0.6); /* @kind color */
  --surface-card: var(--fill-3);
  --surface-card-hover: var(--fill-5);

  --border-default: var(--line);
  --border-strong: var(--line-2);
  --border-accent: var(--gold-a40);

  --accent: var(--gold);
  --accent-hover: var(--gold);
  --accent-surface: var(--gold-a14);

  --selection-bg: rgba(255, 255, 255, 0.18);
  --selection-fg: var(--fg);

  /* ---- Chrome ----------------------------------------------------------
     Values components used to hardcode. They are the pieces that must flip
     with the theme, so they live here rather than inline. */
  --hover-veil: rgba(255, 255, 255, 0.05);   /* menu and nav row hover */
  --overlay-backdrop: rgba(0, 0, 0, 0.72);   /* modal, drawer, sheet scrim */
  --ghost-mark: rgba(255, 255, 255, 0.04);   /* oversized decorative glyph */

  /* A tooltip is dark in BOTH themes - it floats above content and must not
     read as another panel. On light it lifts off pure black to stay soft. */
  --tooltip-bg: rgba(8, 8, 8, 0.96);
  --tooltip-fg: #ffffff;

  /* Caption-Chip auf Fotografie. Aufbau in beiden Themes identisch - gleicher
     Blur, gleicher Rahmenaufbau, gleiche Alpha-Logik. Nur die Werte invertieren
     wie überall sonst: dunkles Material mit weisser Schrift hier, helles
     Material mit dunkler Schrift im Light Mode. */
  --caption-bg: rgba(8, 8, 8, 0.55);
  --caption-fg: #ffffff;
  --caption-line: rgba(255, 255, 255, 0.18);
  --caption-backdrop: blur(var(--blur-surface));

  /* Partner logos (0.9.0). Partner marks are supplied as light artwork
     on a transparent ground - that is what the dark page needs. Here they
     rest in greyscale and show their own colours on hover. tokens/light.css
     turns them into a dark silhouette in both states, because white artwork
     on the white page disappears. */
  --partner-logo-filter: grayscale(1); /* @kind other */
  --partner-logo-filter-hover: none; /* @kind other */

  /* BrandMark: the colours of a mark. A theme switch only recolours the mark,
     it never loads another file. Ink is the kit's white or black, gold the one
     gold; icon-text shows the relief of its theme. */
  --brandmark-ink: #ffffff; /* @kind color */
  --brandmark-gold: var(--gold); /* @kind color */
  --brandmark-show-dark: inline; /* @kind other */
  --brandmark-show-light: none; /* @kind other */

  /* ---- Scrollbar ---------------------------------------------------- */
  --scrollbar-size: 14px;
  --scrollbar-track: var(--bg);
  --scrollbar-track-inset: transparent; /* inner scrollers: the surface they sit on shows through */
  /* Opaque since 0.16.0 (Alex 15.10.2026): the narrow thumb is right, but at
     14% white it hardly stood out from the ground. Same form and width.
     0.17.0 (Alex 16.10.2026: well visible in light and dark): at least 3:1
     on the page and on a card, #6b6b6b (was #5c5c5c, 2.9:1 on a card). */
  --scrollbar-thumb: #6b6b6b;
  --scrollbar-thumb-hover: #8a8a8a;

  /* ---- State -----------------------------------------------------------
     Gold is a MARK, not a field. On an active element the label stays white
     and the small graphic - the underline, the tick, the dot, the filled
     track - carries the gold. That keeps the warmth without flooding a screen:
     text never turns gold for state, fills never turn gold for state. */
  --state-ink: #ffffff;                       /* label of the active item */
  --state-mark: var(--gold);                  /* tick, dot, knob, underline */
  --state-surface: rgba(255, 255, 255, 0.08); /* fill behind the active item */
  --state-border: var(--gold-a40);            /* its border */
  --state-line: var(--gold);                  /* active underline */
  --state-track: var(--gold);                 /* slider and progress fill */

  /* ---- Photo overlays ---------------------------------------------
     The only gradients left in the system, and they never sit on a page or
     section surface: both darken PHOTOGRAPHY so type over it stays legible.
     Page, section, card and panel backgrounds are flat (0.5.0). */
  --wash-hero-scrim: linear-gradient(180deg, rgba(10, 10, 11, 0.55) 0%, rgba(10, 10, 11, 0.35) 40%, rgba(10, 10, 11, 0.75) 100%); /* @kind color */
  --wash-hero-photo-opacity: 0.6; /* @kind other */
  /* Bottom-only darkening so a caption chip stays legible on a photo. */
  --wash-image-foot: linear-gradient(180deg, transparent 55%, rgba(0, 0, 0, 0.32)); /* @kind color */

  /* ---- Lightbox (0.8.3) ----------------------------------------
     The one gradient that does not darken a photograph: a soft glow behind
     the image in the Lightbox, so a product photographed on a pale ground
     keeps its edges instead of standing flat on the page colour. Two offset
     ellipses so it never reads as an exact circle, tilted, lying the long
     way of the screen. Here dark: black ground, white light at 30%.
     tokens/light.css re-declares ground, ink and strength; the shape is the
     same in both themes. */
  --lightbox-bg: var(--bg);
  --lightbox-glow-ink: #ffffff;
  --lightbox-glow-opacity: 0.3;
  --lightbox-glow-tilt: -6deg; /* @kind other */
  /* On black a drop shadow is invisible; the glow alone frames the image. */
  --lightbox-image-shadow: none; /* @kind other */
  --lightbox-glow: radial-gradient(ellipse 51% 38% at 53% 46%, rgb(from var(--lightbox-glow-ink) r g b / 0.75) 0%, rgb(from var(--lightbox-glow-ink) r g b / 0) 74%), radial-gradient(ellipse 70% 50% at 50% 50%, var(--lightbox-glow-ink) 0%, rgb(from var(--lightbox-glow-ink) r g b / 0.42) 38%, rgb(from var(--lightbox-glow-ink) r g b / 0.11) 64%, rgb(from var(--lightbox-glow-ink) r g b / 0) 82%); /* @kind color */
  --lightbox-glow-portrait: radial-gradient(ellipse 38% 51% at 46% 53%, rgb(from var(--lightbox-glow-ink) r g b / 0.75) 0%, rgb(from var(--lightbox-glow-ink) r g b / 0) 74%), radial-gradient(ellipse 50% 70% at 50% 50%, var(--lightbox-glow-ink) 0%, rgb(from var(--lightbox-glow-ink) r g b / 0.42) 38%, rgb(from var(--lightbox-glow-ink) r g b / 0.11) 64%, rgb(from var(--lightbox-glow-ink) r g b / 0) 82%); /* @kind color */
}

/* --- tokens/typography.css (unverändert) --- */
:root {
  --font-sans: "Nunito Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  /* Fluid display sizes - copied verbatim from the production clamps. */
  --text-hero: clamp(30px, 5.5vw, 50px);        /* landing h1 */
  --text-page-title: clamp(30px, 5vw, 52px);    /* service-page h1 */
  --text-section: clamp(24px, 3.6vw, 50px);     /* section h2 */
  --text-cta: clamp(28px, 3.8vw, 42px);         /* closing CTA h2 */
  --text-feature: clamp(26px, 3.6vw, 40px);     /* feature h3 */
  --text-stat: clamp(24px, 3vw, 32px);          /* price / stat numerals */

  /* Fixed steps. Values are the literals used across the site. */
  --text-24: 24px;   /* panel h3 */
  --text-19: 19px;   /* footer column heading */
  --text-18: 18px;   /* lead paragraph */
  --text-17: 17px;   /* symptom h3 / hero pillar */
  --text-165: 16.5px;/* feature body, scope h3 */
  --text-16: 16px;   /* card h2 */
  --text-155: 15.5px;/* dense body, footer link */
  --text-15: 15px;   /* list link */
  --text-145: 14.5px;/* testimonial body */
  --text-14: 14px;   /* nav link, author */
  --text-135: 13.5px;/* button label */
  --text-13: 13px;   /* card body, meta chip */
  --text-125: 12.5px;/* stat pill */
  --text-12: 12px;   /* micro link */
  --text-11: 11px;   /* index numeral, caption chip */
  --text-105: 10.5px;/* tag chip */

  --weight-regular: 400; /* @kind font */
  --weight-medium: 500; /* @kind font */
  --weight-semibold: 600; /* @kind font */
  --weight-bold: 700; /* @kind font */

  --leading-tight: 1.05; /* @kind font */   /* h1 - set globally on the site */
  --leading-snug: 1.25; /* @kind font */    /* card headings */
  --leading-normal: 1.5; /* @kind font */
  --leading-relaxed: 1.625; /* @kind font */
  --leading-loose: 1.75; /* @kind font */   /* long-form service copy */

  --tracking-hero: -0.025em; /* @kind font */
  --tracking-title: -0.03em; /* @kind font */
  --tracking-tight: -0.01em; /* @kind font */
  --tracking-normal: 0; /* @kind font */
  --tracking-label: 0.06em; /* @kind font */   /* footer sub-heading */
  --tracking-mono: 0.1em; /* @kind font */     /* index numerals */
  --tracking-wide: 0.2em; /* @kind font */     /* widest micro-label tracking */
  --tracking-tag: 0.04em; /* @kind font */     /* caption chip on imagery */
}

/* --- tokens/breakpoints.css (unverändert) --- */
:root {
  /* ===========================================================================
     BREAKPOINTS

     Four widths, mobile first. A component never invents its own - it either
     reads one of these in a media query or takes a `stackAt` / `from` prop
     naming one of them.

     Custom properties cannot be used inside a media query condition, so the
     literals below are repeated in @media rules. These tokens are the single
     source of truth for those literals and for JS-side matchMedia calls.
     ========================================================================= */
  --bp-sm: 640px;   /* phone -> large phone. Controls grow to 44px below this. */
  --bp-md: 900px;   /* tablet. Sidebars collapse, tables stack below this.     */
  --bp-lg: 1200px;  /* laptop. The container switches to a percentage width.   */
  --bp-xl: 1560px;  /* the container cap.                                      */
}

/* --- tokens/spacing.css (unverändert) --- */
:root {
  /* Interactive controls on one line share one height. Buttons, the language
     toggle and the account button all resolve to this, so a row stays aligned
     whether or not a button carries an icon. */
  --control-height: 38px;
  --control-height-sm: 32px;   /* dense SaaS rows, table toolbars, inline filters */
  --control-height-lg: 52px;   /* a hero action and the buy action of a product page (Button size="lg") */

  /* Form fields. Bordered, --field-radius; see tokens/forms.css for the rest. */
  --field-height: 40px;
  --field-height-sm: 32px;
  --field-padding-x: 12px;
  --field-gap: 7px;            /* label to control */
  --field-row-gap: 16px;       /* field to field */

  /* Card padding. One value for every card surface - 12px, tightening to 10px
     on phones. Only the closing CTA panel and a full-page EmptyState sit
     outside it, because they are page furniture rather than cards. */
  --card-padding: 12px;
  --card-padding-tight: 10px;
  /* A wide card gets more air (0.17.0, Alex 16.10.2026: text- and data-heavy
     surfaces were too tight on a desktop). The step follows the card's OWN
     width, not the screen's: a narrow card stays tight on any screen, a phone
     never reaches the steps. Panel and KpiCard read them in container queries. */
  --card-padding-m: 18px;     /* card 480px and wider */
  --card-padding-l: 24px;     /* card 800px and wider */
  --card-cell-x-l: 18px;      /* table cells in a card 800px and wider (14px below) */

  /* Running text inside a card: paragraph to paragraph, heading to text. */
  --flow-gap: 12px;
  --flow-gap-m: 18px;         /* card 480px and wider */

  /* Gap between cards in a grid and between the blocks of an app view. A
     grid cannot measure itself, so these follow the screen; the phone keeps
     today's values (0.17.0). */
  --grid-gap: var(--space-7);
  --view-gap: 20px;

  /* Base scale - the site works in real pixel literals, not a strict 4pt grid.
     These are the values that actually recur. */
  --space-1: 4px;
  --space-2: 6px;
  --space-3: 8px;
  --space-4: 10px;
  --space-5: 12px;
  --space-6: 14px;
  --space-7: 18px;   /* default grid gap + mobile gutter */
  --space-8: 22px;   /* wide grid gap + card padding */
  --space-9: 26px;   /* dense card padding */
  --space-10: 28px;  /* tablet gutter */
  --space-11: 34px;  /* panel padding */
  --space-12: 40px;
  --space-13: 56px;
  --space-14: 72px;
  --space-15: 110px; /* gap between alternating feature rows */

  /* Section rhythm (mobile / md / lg). */
  --section-y: 66px;
  --section-y-md: 88px;
  --section-y-lg: 120px;
  /* The first section of a page, under the fixed nav: the page already clears
     the nav, so the section starts closer than the rhythm (Section first). */
  --section-y-first: var(--space-12);

  /* Gutters. Below lg the container is padded; at lg it switches to a
     percentage width and the padding goes to zero. */
  --gutter: 18px;
  --gutter-sm: 28px;

  /* Container measures. The measure follows the content, not the page: a
     content surface caps so a line of prose stays readable, an app surface
     fills the viewport wherever its content is data. Named per role, so
     widening a dashboard never widens a landing page. */
  --container: 1560px;        /* default: max-w-[1560px] lg:w-[80%] */
  --container-wide: none; /* @kind spacing */     /* nav + footer: lg:w-[90%], no max */
  --container-spacious: 1600px;
  --container-app: none; /* @kind spacing */      /* app surface: the viewport IS the measure */
  --container-form: 720px;    /* one column of fields inside an app surface */
  --container-width-lg: 80%;
  --container-width-wide: 90%;
  --container-width-app: 100%;

  --measure-lead: 680px;      /* section description */
  --measure-body: 1200px;     /* FAQ question row: the toggle and its plus */
  --measure-answer: 720px;    /* FAQ answer: running text, Core rule 5 (0.9.0) */
  --measure-narrow: 560px;    /* hero description */
  --measure-panel: 880px;     /* a lone text panel: prose or a short list never runs the full container */

  /* Notices cap their own width. Without a cap they run the full width of a
     wide screen and a line of copy becomes unreadable; a stack of toasts
     without a shared cap frays, because each one is as wide as its own text.
     Named per role, so changing a hero measure never moves an alert. */
  --measure-alert: 560px;     /* inline status attached to a form or section */
  --measure-toast: 380px;     /* transient confirmation, so a stack lines up */
  --measure-tooltip: 280px;   /* InfoTooltip bubble: wraps, never one long line */
  /* Running text inside a box that is wider than its text (0.15.0): the cap
     sits on the text block, never on the card or the page. */
  --measure-text: 68ch;

  /* Width classes of an app surface (0.15.0). The width belongs to the
     element, not to the page: an S or M element has a minimum track, the
     grid fills the row with as many tracks as fit (auto-fill), and a track
     never grows past twice its minimum. L fills. A free track stays empty -
     a lone card is never stretched to fill the row. */
  --track-s: 320px;           /* a date, a workshop, a KPI group's card, a contact */
  --track-m: 480px;           /* an alert, a form card, a board column */
  /* The context column beside the main column of a wide view: recent
     documents, contact, hints, the preview of a chosen row. */
  --context-column-min: 360px;
  --context-column-max: 480px;
  /* From this width of its own the layout opens the context column: what a
     1560px screen (--bp-xl) leaves beside the sidenav. A container query
     cannot read a custom property, so the literal is repeated in
     components.css; this token is its single source, as with breakpoints. */
  --context-from: 1240px;

  --nav-height: 70px;
  --nav-row-height: calc(var(--field-height) + 2 * var(--space-5) + 1px); /* @kind spacing */ /* NavBar's second row of fields (0.16.0) */

  /* Minimum hit height of a link or a control that is not sized by
     --control-height: a footer link, a text link on a card. Nothing where a
     pointer is precise, 44px on a phone (below), so a target clears 44px
     there without a component hardcoding it (0.9.0). */
  --touch-target: 0px;
  /* Height of the wordmark in a bar - NavBar and Sidebar head. The value the
     shop's navbar has: its lockup is 22px high, and the lockup is 1.14 times
     its wordmark (22 / 1.14); 15.8px on a phone (18 / 1.14). A bar never sets
     its own. */
  --brandmark-bar: 19.3px;
  /* The phone value as its own token (0.14.0), so a bar that knows it is a
     phone bar by its own width - the App Shell below 640px - takes the same
     value the viewport query gives a real phone. */
  --brandmark-bar-phone: 15.8px;
  /* The signet in a folded 60px rail (0.14.0). Its fine orbits need more
     edge than a glyph to carry the same weight as the 19px wordmark it
     replaces: 32px, a little over half the rail. */
  --brandmark-signet-rail: 32px;

  /* Mobile chrome. */
  --tabbar-height: 62px;
  --sticky-bar-height: 68px;
  --sheet-radius: 32px;       /* the one radius above --radius-xl: a sheet edge (18px until 0.17.0) */
  --sheet-handle: 38px;
}

/* Mobile first, and the two moves are deliberately opposite: every CONTROL
   grows to a 44px touch target, while every SURFACE padding tightens. A phone
   has less room to give away to margins but needs more room for a thumb, so
   the frame shrinks and the hit area grows. One source - components never
   hardcode either. */
@media (max-width: 640px) {
  :root {
    --control-height: 44px;
    /* The small size is a desktop density. On a phone it is a touch target
       like any other, so it clears 44px too (40px until 0.9.0). */
    --control-height-sm: 44px;
    --field-height: 46px;
    --field-height-sm: 44px;
    --touch-target: 44px;
    --brandmark-bar: var(--brandmark-bar-phone);
    --card-padding: 10px;
    --card-padding-tight: 8px;
    --gutter: 14px;
  }
}

/* Wider screens, more room between cards (0.17.0). md and the container cap. */
@media (min-width: 900px) {
  :root { --grid-gap: 24px; --view-gap: 28px; }
}
@media (min-width: 1560px) {
  :root { --grid-gap: 28px; }
}

/* --- tokens/forms.css (unverändert) --- */
:root,
[data-theme] {
  /* ---- Form field surfaces -------------------------------------------
     Fields are BORDERED, not filled: a hairline box on a barely-there fill,
     matching the card system. The border does the work at every state. */
  --field-bg: var(--fill-1);
  --field-bg-hover: var(--fill-2);
  --field-bg-disabled: rgba(255, 255, 255, 0.02);
  --field-border: var(--line-2);
  --field-border-hover: var(--line-3);
  --field-border-focus: var(--gold-a60);
  --field-border-error: var(--danger);
  --field-radius: var(--radius-md);

  --field-text: var(--fg);
  --field-placeholder: var(--dim);
  --field-label: var(--mut);
  --field-hint: var(--dim);
  --field-error: var(--danger);
  /* The error message itself. --danger carries no small text (3.05:1 on a
     card, reference/colour.md, Ink contrast), so the words take a passing ink and the
     colour stays in the glyph in front of them (0.9.0). */
  --field-error-text: var(--fg);
  --field-required: var(--gold);

  /* Focus. A soft glow in real gold - no ring, no flat border. The border only
     warms slightly; the glow does the work. */
  --focus-glow: 0 0 12px -2px rgb(from var(--gold) r g b / 0.55), 0 0 4px 0 rgb(from var(--gold) r g b / 0.22);
  --focus-glow-error: 0 0 12px -2px rgb(from var(--danger) r g b / 0.5), 0 0 4px 0 rgb(from var(--danger) r g b / 0.2);

  /* Depth. Controls are recessed when off and lifted when on - the same
     hard-black-plus-faint-white logic the cards use, at control scale. */
  --control-inset: inset 0 1px 2px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.045);
  --control-raised: 0 1px 3px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.12);

  /* Selection controls (checkbox, radio, switch). Checked is NEUTRAL, not gold:
     gold marks intent (the action), never state. A form of ten checkboxes must
     read as a form, not as a gold wall. */
  --control-off-bg: var(--fill-2);
  --control-off-border: var(--line-3);
  --control-on-bg: var(--state-surface);
  --control-on-border: var(--state-border);
  --control-on-mark: var(--state-mark);

  /* Track-and-thumb controls (slider, progress). */
  --track-bg: var(--fill-3);
  /* Sliders sit black and white at rest - gold appears only while dragging.
     Progress bars are read, not touched, so they keep the gold fill. */
  --track-fill-rest: var(--mut);
  --track-fill: var(--state-track);
  --track-height: 6px;
  --thumb-size: 18px;
}

/* --- tokens/radii.css (unverändert) --- */
:root {
  /* Four steps and a pill. 17px is THE surface radius - cards, panels,
     framed photography and CTA blocks share it; an icon tile and a menu row
     take --radius-md. Raised in 0.17.0
     (Alex, 16.10.2026): 4 / 6 / 10 / 12 became 7 / 11 / 17 / 21. */
  --radius-sm: 7px;    /* small square icon tile, inline check */
  --radius-md: 11px;   /* icon tile, inner image inside a card, nav link, caption chip, field */
  --radius-lg: 17px;   /* cards, panels, framed photos, CTA blocks */
  --radius-xl: 21px;   /* dropdown panel - the one step above the surface radius */
  --radius-pill: 9999px;
}

/* --- tokens/elevation.css (unverändert) --- */
:root,
[data-theme] {
  /* Cards and buttons carry no shadow: a surface is its fill and its
     hairline. Shadows stay only where something floats (menus) or a photo is
     framed. */
  --shadow-menu: 0 30px 60px -20px rgba(0, 0, 0, 0.8);
  --shadow-photo: 0 40px 80px -30px rgba(0, 0, 0, 0.85);
  --shadow-hairline-top: inset 0 1px 0 rgba(255, 255, 255, 0.03);

  /* Blur: chrome floating over content, and every see-through card or button
     so what lies behind it - a photo, a pattern - turns soft. */
  --blur-surface: 8px; /* @kind other */ /* every see-through surface: cards, buttons, chips, glass, badges, avatars */
  --blur-nav: 18px; /* @kind other */
  --blur-menu: 20px; /* @kind other */
  --saturate-nav: 160%; /* @kind other */

  /* Glass (0.14.0). A pane of glass is not a tinted box: its edge catches
     light at the top and loses it towards the bottom, and it lifts what lies
     behind it a little. --glass-light is the edge where the pointer is. */
  --blur-glass: var(--blur-surface); /* @kind other */ /* the same 8px as every see-through surface (Alex 14.10.2026: 22px was far too strong) */
  --glass-brightness: 1.08; /* @kind other */
  --glass-edge-top: rgba(255, 255, 255, 0.20);
  --glass-edge-bottom: rgba(255, 255, 255, 0.05);
  --glass-light: rgb(from var(--gold) r g b / 0.70);
  --glass-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0) 42%);
  --shadow-float: 0 1px 0 rgba(0, 0, 0, 0.5), 0 24px 48px -20px rgba(0, 0, 0, 0.75);
}

/* --- tokens/motion.css (unverändert) --- */
:root {
  /* ===========================================================================
     MOTION

     The brand is a workshop: precise, mechanical, unhurried. Motion follows
     that - things decelerate into place like a tool seating in a fixture. The
     overshoot is real but small; nothing springs, wobbles or bounces twice.

     Four curves, each with one job. Pick by WHAT is moving, not by taste.
     ========================================================================= */

  /* Decelerate. The default: something arriving, opening, settling. Starts
     fast, eases out long. Use for 80% of transitions. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1); /* @kind other */

  /* Accelerate. Something leaving. Exits start slow and speed up, which reads
     as the element being released rather than pulled away. */
  --ease-exit: cubic-bezier(0.55, 0, 0.85, 0.35); /* @kind other */

  /* Symmetric. Something moving between two on-screen positions where neither
     end is an entrance - a sliding indicator, a reordering row. */
  --ease-move: cubic-bezier(0.65, 0, 0.35, 1); /* @kind other */

  /* Settle. ~3% overshoot, one pass, no return bounce. For a state landing:
     a toggle knob, a check appearing, a badge counting up. Never for layout,
     never for colour, never for anything larger than about 60px. */
  --ease-settle: cubic-bezier(0.34, 1.32, 0.64, 1); /* @kind other */

  /* Constant. For a loop - the spinner, a progress bar filling from real
     data. Easing a loop makes it look broken. */
  --ease-linear: linear; /* @kind other */

  /* ---- Durations ---------------------------------------------------------
     Duration follows DISTANCE and SIZE, not importance. A colour swap is not
     a journey; a full-height drawer is. */
  --duration-instant: 120ms; /* @kind other */   /* colour, opacity, border on a small element */
  --duration-fast: 200ms; /* @kind other */      /* link nudge, dot, tick */
  --duration-chip: 220ms; /* @kind other */      /* chip, tag, small CTA */
  --duration-card: 250ms; /* @kind other */      /* card hover */
  --duration-base: 300ms; /* @kind other */      /* button and field states */
  --duration-icon: 350ms; /* @kind other */      /* FAQ plus rotation */
  --duration-image: 400ms; /* @kind other */     /* image zoom inside a frame */
  --duration-accordion: 450ms; /* @kind other */ /* grid-row expansion */
  --duration-overlay: 260ms; /* @kind other */   /* modal, sheet, drawer entering */
  --duration-reveal: 900ms; /* @kind other */    /* scroll reveal */

  /* Exits run at two thirds of their entrance. Leaving should never feel
     as deliberate as arriving. */
  --duration-exit: 180ms; /* @kind other */

  /* ---- Stagger -----------------------------------------------------------
     Siblings entering together get a small offset each, capped so a long list
     does not turn into a queue. Index * step, max 6 steps. */
  --stagger-step: 55ms; /* @kind other */
  --stagger-max: 330ms; /* @kind other */

  /* ---- Displacement ------------------------------------------------------ */
  --reveal-offset: 30px; /* @kind spacing */     /* Reveal starts 30px low at opacity 0 */
  --overlay-offset: 12px; /* @kind spacing */    /* modal rises this far as it fades in */
  --sheet-offset: 100%; /* @kind spacing */      /* bottom sheet travels its own height */
  /* No Y-transform on hover, anywhere in the system. Cards respond with fill
     and border only - see reference/foundations.md, Controls. */
  --hover-nudge: 3px; /* @kind spacing */        /* link slides right on hover */
  --hover-zoom: 1.03; /* @kind other */          /* image scale on card hover */

  /* ---- Interaction layer (0.14.0) ----------------------------------------
     A press is a key seating: in fast, out on the settle curve. Small things
     travel further in scale than large ones, so the felt depth is equal -
     4px on a 140px button, 3px on a 40px glyph button. */
  --duration-press: 70ms; /* @kind other */      /* pointer down: the control seats */
  --duration-release: 280ms; /* @kind other */   /* pointer up: it springs back on --ease-settle */
  --press-scale: 0.97; /* @kind other */         /* button, chip, page */
  --press-scale-sm: 0.92; /* @kind other */      /* glyph button, nudge, selection box */
  --duration-draw: 240ms; /* @kind other */      /* a tick drawn left to right */
  --duration-float: 170ms; /* @kind other */     /* tooltip, popover, mega panel arriving */
  --duration-morph: 340ms; /* @kind other */     /* a panel changing its size or content */
  --float-scale: 0.96; /* @kind other */         /* floating UI grows from its anchor side, never slides */
  --tip-delay: 450ms; /* @kind other */          /* first tooltip waits for intent */
  --tip-warm: 320ms; /* @kind other */           /* after one closed, the next opens at once */
  --menu-intent: 140ms; /* @kind other */        /* moving towards an open panel never switches it */
  --stop-bump: 2px; /* @kind spacing */          /* a value arriving at its bound knocks the end stop */
  --roll-offset: 0.7em; /* @kind spacing */      /* a changed digit rolls in from this far */
}

/* --- tokens/base.css (unverändert) --- */
/* The version of this design system, so a built surface can report which
   version it embedded:
   getComputedStyle(document.documentElement).getPropertyValue("--ds-version")
   Bump it in the same commit as the CHANGELOG.md entry, never separately. */
:root { --ds-version: "0.17.1-beta"; }

html { scroll-behavior: smooth; }

/* Native controls follow the theme: the date and time pickers, the calendar
   glyph in a date field, select popups, autofill and the spin buttons. Without
   this the browser draws them light - a white calendar over a black page and a
   near-invisible glyph inside the field. */
:root, [data-theme="dark"] { color-scheme: dark; }
[data-theme="light"] { color-scheme: light; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

h1 { line-height: var(--leading-tight); }

/* A link is navigation, not the page's key action: it stays neutral and only
   brightens on hover. Gold is reserved for buttons and the brand. */
a { color: var(--mut); text-decoration: none; transition: color var(--duration-base); }
a:hover { color: var(--gold); }

::selection { background: rgba(255, 255, 255, 0.18); color: var(--fg); }

/* Scrollbars. Left at the browser default, a near-black page gets a light-grey
   bar that detaches from everything else. These match the hairline system:
   the page scrollbar has a track the colour of the page, every inner scroller
   (a list in a card, a textarea, a log, a menu) a transparent track, so it sits
   on whatever surface it scrolls. The thumb is the same everywhere.

   Two engines, two mechanisms, and they must not meet: in Chromium, setting
   the standard `scrollbar-color` or `scrollbar-width` switches the
   ::-webkit-scrollbar styling off and draws a native bar with arrow buttons -
   and `scrollbar-color` inherits, so one declaration on html did it to every
   inner scroller too. The standard properties therefore apply only where the
   pseudo-elements do not exist (Firefox). */
::-webkit-scrollbar { width: var(--scrollbar-size); height: var(--scrollbar-size); }
::-webkit-scrollbar-button { display: none; }
::-webkit-scrollbar-track { background: var(--scrollbar-track); }
::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: var(--radius-pill); border: 3px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: var(--scrollbar-track); }
body *::-webkit-scrollbar-track, body *::-webkit-scrollbar-corner { background: var(--scrollbar-track-inset); }

@supports not selector(::-webkit-scrollbar) {
  html { scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); scrollbar-width: thin; }
  /* scrollbar-width does not inherit (scrollbar-color does), so every inner
     scroller needs it itself - otherwise Firefox draws a full-width native bar
     with arrow buttons inside every card. */
  body * { scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track-inset); scrollbar-width: thin; }
}

@keyframes revoltaris-spin { to { transform: rotate(360deg); } }

/* Skeleton shimmer. Constant, quiet, no opacity pulse - see Skeleton. */
@keyframes revoltaris-shimmer {
  from { background-position: 100% 0; }
  to   { background-position: 0 0; }
}

/* Slow ring around a live status dot - see StatusLink. */
@keyframes revoltaris-pulse {
  0%   { box-shadow: 0 0 0 0 var(--pulse-colour); opacity: 1; }
  70%  { box-shadow: 0 0 0 5px transparent; opacity: 0.85; }
  100% { box-shadow: 0 0 0 0 transparent; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* --- tokens/light.css (unverändert) --- */
/* ============================================================================
   Light theme
   ============================================================================
   Opt in by putting data-theme="light" on <html> or on any subtree:

       <html data-theme="light">

   Dark is the brand's default and stays the default here. This file only
   re-declares token VALUES - no component knows the theme exists.

   It is not an inversion. Three foundations work only on near-black and had to
   be rebuilt rather than flipped:

     1. Cards are translucent WHITE over a dark page. On a light page white on
        white is nothing, so the fills become translucent black.
     2. Hairlines are white alpha. They become black alpha, slightly stronger,
        because a light page needs more contrast to show a 1px line.
     3. The neumorphic shadow - hard black drop plus a faint white lift - is a
        dark-surface effect. On light it becomes a soft neutral drop with no
        lift at all.

   The surface ladder itself is NOT rebuilt: both themes mix the same
   percentages (--surface-step-1/2/3) into their page colour, so the two can
   never drift apart.
   ========================================================================= */

[data-theme="light"] {
  /* ---- Brand -------------------------------------------------------------
     --gold bleibt exakt #b98823, in beiden Themes. Eine definierte Farbe ist in
     jedem Theme dieselbe Farbe. Nur die Alpha-Stufen steigen leicht, weil ein
     Tint über Weiss weniger trägt als über Schwarz. */

  --gold-a05: rgb(from var(--gold) r g b / 0.07);
  --gold-a08: rgb(from var(--gold) r g b / 0.10);
  --gold-a10: rgb(from var(--gold) r g b / 0.12);
  --gold-a14: rgb(from var(--gold) r g b / 0.16);
  --gold-a15: rgb(from var(--gold) r g b / 0.17);
  --gold-a22: rgb(from var(--gold) r g b / 0.30);
  --gold-a24: rgb(from var(--gold) r g b / 0.26);
  --gold-a30: rgb(from var(--gold) r g b / 0.38);
  --gold-a40: rgb(from var(--gold) r g b / 0.50);
  --gold-a60: rgb(from var(--gold) r g b / 0.72);

  /* ---- Backgrounds -------------------------------------------------------
     Same rule as dark, mirrored: the page is pure #fff and every step mixes
     the SAME percentages of black into it. The step tokens are inherited from
     the dark theme, so the two ladders can never drift apart - only the page
     colour and the mixed-in colour flip. */
  --bg: #ffffff;
  --bg-1: color-mix(in srgb, #000000 var(--surface-step-1), var(--bg));
  --bg-2: color-mix(in srgb, #000000 var(--surface-step-2), var(--bg));
  --bg-3: color-mix(in srgb, #000000 var(--surface-step-3), var(--bg));
  --card: var(--bg-2);
  --nav-bg: rgba(255, 255, 255, 0.80);
  --glass-bg: rgba(255, 255, 255, 0.55);
  --glass-line: rgba(26, 22, 15, 0.14);
  --glass-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  --menu-bg: rgba(255, 255, 255, 0.97);

  /* Translucent NEUTRAL BLACK fills - the mirror of the dark theme's white.
     Deliberately not warmed: a tint in the greys reads as a yellow cast over
     the whole page and competes with the gold. Gold is the only colour here. */
  --fill-1: rgba(0, 0, 0, 0.025);
  --fill-2: rgba(0, 0, 0, 0.04);
  --fill-3: rgba(0, 0, 0, 0.055);
  --fill-4: rgba(0, 0, 0, 0.085);
  --fill-5: rgba(0, 0, 0, 0.12);

  /* ---- Hairlines ---------------------------------------------------------
     Slightly stronger than the dark theme's: a 1px line needs more help to
     register against a bright field. */
  --line: rgba(0, 0, 0, 0.11);
  --line-2: rgba(0, 0, 0, 0.17);
  --line-3: rgba(0, 0, 0, 0.28);

  /* Ghost button (0.17.0): greyer than a card, as in the dark theme. */
  --ghost-fill: var(--fill-4);
  --ghost-fill-hover: var(--fill-5);
  --ghost-line: var(--line-3);
  --ghost-line-hover: rgba(0, 0, 0, 0.38);

  /* ---- Ink ---------------------------------------------------------------
     Warm near-black rather than pure black, to sit with the gold. All four
     clear 4.5:1 on --bg. */
  /* ---- Ink ---------------------------------------------------------------
     Neutral, for the same reason as the fills: a warm cast in the greys reads
     as a yellow tint over the page. All four clear 4.5:1 on --bg. */
  --fg: #141414;      /* 16.8:1 */
  --mut: #4b4b4b;     /*  8.6:1 */
  --mut-2: #5c5c5c;   /*  6.7:1, a step stronger since 0.17.0 (#6b6b6b before) */
  --dim: #6a6a6a;     /*  5.4:1, 4.6:1 on a card (#737373 until 0.17.0) */

  /* ---- Semantic ----------------------------------------------------------
     KEINE Neudefinition. Das Hue-Rad ist festgelegt: acht 45-Grad-Schritte bei
     konstanter Sättigung (68.2%) und HSL-Helligkeit (43.1%), also reine
     Hue-Rotationen von --gold. Diese acht Farben sind in
     beiden Themes identisch und werden hier bewusst NICHT überschrieben - sie
     erben aus tokens/colors.css.

     Nur ihr EINSATZ passt sich an: eine Fläche braucht über Weiss weniger
     Alpha, eine Linie mehr. */
  /* Text inks (0.17.0): the light hues darkened for their text only, see
     tokens/colors.css. */
  --success-text: hsl(from var(--success) h s calc(l - 16));
  --danger-text:  var(--danger);
  --info-text:    var(--info);
  --hue-45-text:  hsl(from var(--hue-45) h s calc(l - 17));
  --hue-135-text: hsl(from var(--hue-135) h s calc(l - 17));
  --hue-225-text: var(--hue-225);
  --hue-270-text: hsl(from var(--hue-270) h s calc(l - 3));

  --success-surface: rgb(from var(--success) r g b / 0.08);
  --danger-surface:  rgb(from var(--danger) r g b / 0.08);
  --info-surface:    rgb(from var(--info) r g b / 0.08);

  --success-line:   rgb(from var(--success) r g b / 0.28);
  --danger-line:    rgb(from var(--danger) r g b / 0.28);
  --info-line:      rgb(from var(--info) r g b / 0.28);

  --success-border: rgb(from var(--success) r g b / 0.45);
  --danger-border:  rgb(from var(--danger) r g b / 0.45);
  --info-border:    rgb(from var(--info) r g b / 0.45);

  /* ---- Aliases -----------------------------------------------------------
     A card is see-through like the dark one: black alpha on the
     --surface-tint ground - on a white page the grey of --bg-2, over a photo
     glass. Hover DEEPENS instead of lightening - the light-theme equivalent of
     "hover lightens" on dark. */
  --text-accent: var(--gold);
  --text-link-hover: var(--gold);

  /* Ink auf der vollen Goldfläche. Im Light Mode Weiss: auf einer hellen
     Seite wirkt der Solid-Button mit weisser Schrift als zusammenhängende
     Fläche, während Fast-Schwarz ihn in Füllung und Text zerlegt. */
  --on-accent: #ffffff;

  /* Caption-Chip - gleicher Aufbau wie im Dark Mode, invertierte Werte:
     helles Material mit dunkler Schrift. Die Alpha liegt etwas höher als die
     0.55 im Dark Mode, weil ein heller Film über einem dunklen Foto weniger
     deckt als umgekehrt. Über einer dunklen Bildstelle trägt die Schrift
     damit rund 7.9:1. */
  --caption-bg: rgba(250, 250, 250, 0.62);
  --caption-fg: #141414;
  --caption-line: rgba(0, 0, 0, 0.18);

  /* Partner logos (0.9.0): the light artwork turns into a dark
     silhouette, at rest and on hover - its own colours would show white
     artwork as nothing on the white page. */
  --partner-logo-filter: grayscale(1) brightness(0);
  --partner-logo-filter-hover: grayscale(1) brightness(0);

  --brandmark-ink: #000000;
  --brandmark-show-dark: none;
  --brandmark-show-light: inline;

  --surface-card: var(--fill-3);
  --surface-card-hover: var(--fill-4);

  --accent: var(--gold);
  --accent-hover: var(--gold);

  --selection-bg: rgb(from var(--gold) r g b / 0.28);
  --selection-fg: var(--fg);

  /* ---- Scrollbar ---------------------------------------------------- */
  --scrollbar-track: var(--bg);
  --scrollbar-thumb: #888888;       /* 3.5:1 on white, 3.1:1 on a card (#a8a8a8, 2.4:1, until 0.17.0) */
  --scrollbar-thumb-hover: #6b6b6b;

  /* ---- State -------------------------------------------------------------
     The rule is unchanged: the label stays ink, the small graphic carries the
     gold. Only the ink colour flips. */
  --state-ink: var(--fg);
  --state-surface: rgba(26, 22, 15, 0.06);

  /* ---- Chrome ------------------------------------------------------- */
  --hover-veil: rgba(26, 22, 15, 0.05);
  --overlay-backdrop: rgba(26, 22, 15, 0.42);
  --ghost-mark: rgba(26, 22, 15, 0.05);
  /* The tooltip stays dark - see tokens/colors.css. The caption chip inverts,
     above (--caption-*). */

  /* ---- Elevation ---------------------------------------------------------
     No white lift: on a light surface an inner highlight reads as a smudge.
     A single soft neutral drop does the whole job. */
  --shadow-menu: 0 18px 44px -14px rgba(26, 22, 15, 0.22);
  --shadow-photo: 0 24px 56px -24px rgba(26, 22, 15, 0.30);
  --shadow-hairline-top: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --control-inset: inset 0 1px 2px rgba(26, 22, 15, 0.07);

  /* ---- Form fields -------------------------------------------------- */
  --field-bg: #ffffff;
  --field-bg-hover: #ffffff;
  --field-bg-disabled: rgba(26, 22, 15, 0.04);
  --field-border: rgba(26, 22, 15, 0.20);
  --field-border-hover: rgba(26, 22, 15, 0.32);
  --focus-glow: 0 0 0 1px var(--gold), 0 0 0 4px rgb(from var(--gold) r g b / 0.20);
  --focus-glow-error: 0 0 0 1px var(--danger), 0 0 0 4px rgb(from var(--danger) r g b / 0.16);

  --control-off-bg: rgba(26, 22, 15, 0.05);
  --control-off-border: rgba(26, 22, 15, 0.28);
  --track-bg: rgba(26, 22, 15, 0.10);
  --track-fill-rest: var(--mut-2);

  /* Hero photograph: full strength under a light scrim in the page colour,
     only behind the text (left) and dimmed so the photo still carries the
     hero; a soft fade into the page at the bottom. The type over it is dark.
     A phone gets a top-to-bottom scrim (below this block). */
  --wash-hero-photo-opacity: 1;
  --wash-hero-scrim:
    linear-gradient(90deg, rgb(from var(--bg) r g b / 0.67) 0%, rgb(from var(--bg) r g b / 0.58) 48%, rgb(from var(--bg) r g b / 0) 80%),
    linear-gradient(180deg, rgb(from var(--bg) r g b / 0) 70%, var(--bg) 100%); /* @kind color */

  /* ---- Lightbox ----------------------------------------------------------
     The shape and tilt of the glow are the dark theme's (tokens/colors.css). */
  /* Negativ des dunklen Designs (Alex 03.10.2026: «beim lightmode lightbox fehlt
     negativ color beim gradient im hintergrund»): weisser Grund, dunkler Schein in
     derselben Form und Stärke, kein Bildschatten - wie dunkel, nur umgekehrt. */
  --lightbox-bg: var(--bg);
  --lightbox-glow-ink: #000000;
  --lightbox-glow-opacity: 0.3;
  --lightbox-image-shadow: none;

  /* ---- Glass (0.14.0) ----------------------------------------------------
     On white the lit edge is the shadow side: a black hairline that is
     stronger below, the sheen a white wash at the top. */
  --glass-brightness: 1.02;
  --glass-edge-top: rgba(26, 22, 15, 0.08);
  --glass-edge-bottom: rgba(26, 22, 15, 0.18);
  --glass-light: rgb(from var(--gold) r g b / 0.85);
  --glass-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0) 50%);
  --shadow-float: 0 1px 2px rgba(26, 22, 15, 0.06), 0 18px 40px -16px rgba(26, 22, 15, 0.22);
}

/* Phone: the hero text runs the full width, so the light scrim runs top to bottom. */
@media (max-width: 640px) {
  [data-theme="light"] {
    --wash-hero-scrim: linear-gradient(180deg, rgb(from var(--bg) r g b / 0.48) 0%, rgb(from var(--bg) r g b / 0.61) 55%, var(--bg) 100%); /* @kind color */
  }
}

/* Print: no shadows, in either theme (0.10.6).
   Paper has no light source, and a blurred box-shadow is written into a PDF
   as a transparency group: Chrome and Firefox draw it, but the macOS Preview
   paints the whole shadow box as a grey rectangle behind every card. Placed
   last so it wins over both theme blocks. */
@media print {
  :root, [data-theme], [data-theme="light"], [data-theme="dark"] {
    --shadow-menu: none;
    --shadow-photo: none;
    --shadow-hairline-top: none;
    --shadow-float: none;
  }
}

/* --- components.css (unverändert) --- */
/*
 * Revoltaris Design System - components.
 *
 * The one source for how a component looks: variants, hover, disabled,
 * pressed. The React components in components/ only set these classes; plain
 * HTML sets the same classes by hand. Never restate a value from here in a
 * .jsx or in a page - alias the class instead.
 *
 * Every class carries the prefix `rvds-` so it cannot collide with the shop's
 * own `.rv-*` classes when both land on one page. Names follow BEM:
 * block `rvds-button`, modifier `rvds-button--ghost`.
 *
 * Loaded by styles.css after the tokens. Components move here group by group;
 * which ones have moved is listed in reference/stacks.md (Migration), the one
 * place that list is kept. This file runs roughly by group, later additions
 * appended at the end.
 */

/* The fill of every see-through surface is a registered colour (0.17.0), so
   a hover fades instead of jumping: a gradient cannot be animated, a
   registered custom property inside it can. The surface keeps its look -
   `linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint)` -
   and a rule changes only `--rvds-fill`. Without @property (Firefox before
   128, Safari before 16.4) the hover jumps as before. */
@property --rvds-fill {
  syntax: "<color>";
  inherits: false;
  initial-value: transparent;
}

/* ---------------------------------------------------------------------------
 * Button  (components/core/Button.jsx)
 *
 *   <a class="rvds-button rvds-button--solid" href="/anfragen">Jetzt anfragen</a>
 *   <a class="rvds-button" href="/leistungen">Mehr Infos <svg>…</svg></a>
 *   <button type="button" class="rvds-button rvds-button--ghost">Kontakt aufnehmen</button>
 *   <a class="rvds-button" aria-disabled="true" tabindex="-1">Nicht verfügbar</a>
 *   <a class="rvds-button rvds-button--solid rvds-button--lg" href="/warenkorb">In den Warenkorb</a>
 *
 * No modifier is the gold weight, the everyday primary. `--lg` is the 52px
 * size for a hero action and the buy action of a product page. An icon is a direct
 * <svg> child. Disabled is `disabled` on a <button>; on an <a>, which has no
 * such attribute, `aria-disabled="true"` plus tabindex="-1".
 * ------------------------------------------------------------------------ */

.rvds-button {
  box-sizing: border-box;
  /* One height for every button, with or without an icon: an icon child raises
     the line box and would otherwise make a mixed row ragged. */
  min-height: var(--control-height);
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  border-radius: var(--radius-pill);
  padding: 0 20px;
  font-family: var(--font-sans);
  font-size: var(--text-135);
  line-height: 1;
  font-weight: var(--weight-medium);
  white-space: nowrap;
  border: 1px solid var(--gold-a40);
  --rvds-fill: var(--gold-a14);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--gold);
  /* Every see-through variant blurs what lies behind it (Alex 03.10.2026), so a
     tinted fill stays legible over imagery and scrolling content. */
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
  cursor: pointer;
  text-decoration: none;
  transition-property: background, border-color, color, opacity;
  transition-duration: var(--duration-base);
}
.rvds-button:hover {
  border-color: var(--gold-a60);
  --rvds-fill: var(--gold-a24);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--gold);
}

/* Solid gold. Reserved for the single most important commercial action on a
   page - "Jetzt anfragen", "In den Warenkorb", "Jetzt kaufen". Exactly ONE
   action per screen may wear it; it may appear twice for that same action
   (the persistent nav CTA plus the in-page CTA). Never for a link that just
   moves you somewhere, never for "Weiter", never for a second action. */
.rvds-button--solid,
.rvds-button--solid:hover {
  border-color: var(--gold);
  background: var(--gold);
  color: var(--on-accent);
  font-weight: var(--weight-bold);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.rvds-button--solid:hover { filter: brightness(1.12); }

/* Glass secondary, safe over imagery. */
.rvds-button--ghost {
  border-color: var(--ghost-line);
  --rvds-fill: var(--ghost-fill);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--fg);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
}
.rvds-button--ghost:hover {
  border-color: var(--ghost-line-hover);
  --rvds-fill: var(--ghost-fill-hover);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--fg);
}

/* An icon sits at 80% and comes to full strength with its button. */
.rvds-button > svg {
  flex: none;
  opacity: 0.8;
  transition: opacity var(--duration-base);
}
.rvds-button:hover > svg { opacity: 1; }

/* Large (0.8.0): a hero action and the buy action of a product page -
   nothing else. 52px tall with a larger label and an 18px glyph. */
.rvds-button--lg {
  min-height: var(--control-height-lg);
  padding: 0 30px;
  font-size: var(--text-16);
  gap: var(--space-4);
}
.rvds-button--lg > svg { width: 18px; height: 18px; }

.rvds-button[aria-disabled="true"],
.rvds-button:disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* ---------------------------------------------------------------------------
 * IconButton  (components/core/IconButton.jsx)
 *
 *   <button type="button" class="rvds-icon-button" aria-label="Herunterladen"><svg>…</svg></button>
 *   <button type="button" class="rvds-icon-button rvds-icon-button--danger" aria-label="Schliessen">…</button>
 *   <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-icon-button--sm" aria-label="Bearbeiten">…</button>
 *   <button type="button" class="rvds-icon-button" style="--rvds-icon-button-size: 26px" aria-label="…">…</button>
 *
 * No modifier is the bordered ghost (`--ghost` says the same explicitly).
 * `aria-label` is required - the button has no visible text. A glyph that
 * destroys or dismisses (X, Trash) takes `--danger`; the React component picks
 * that on its own, plain HTML sets it. Held-down toggles carry
 * aria-pressed="true" and render like hover. Disabled is the `disabled`
 * attribute. Glyph: a direct <svg> child, 16px, stroke 2 (14px in --sm).
 * ------------------------------------------------------------------------ */

/* An exact box applies to the button it is set on, never to every button
   inside an element that happens to carry it. Browsers without @property
   (Firefox before 128, Safari before 16.4) ignore this and let it inherit. */
@property --rvds-icon-button-size {
  syntax: "*";
  inherits: false;
}

.rvds-icon-button {
  --rvds-icon-button-box: var(--rvds-icon-button-size, var(--control-height));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  box-sizing: border-box;
  height: var(--rvds-icon-button-box);
  width: var(--rvds-icon-button-box);
  padding: 0;
  border-radius: var(--radius-pill);
  border: 1px solid var(--ghost-line);
  --rvds-fill: var(--ghost-fill);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--mut);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
  cursor: pointer;
  transition: background var(--duration-base), --rvds-fill var(--duration-base), border-color var(--duration-base), color var(--duration-base);
}
.rvds-icon-button--sm { --rvds-icon-button-box: var(--rvds-icon-button-size, var(--control-height-sm)); }

.rvds-icon-button:not(:disabled):hover,
.rvds-icon-button[aria-pressed="true"] {
  border-color: var(--ghost-line-hover);
  --rvds-fill: var(--ghost-fill-hover);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--fg);
}

/* Inside a group a member without a modifier is bare. :where() keeps this at
   single-class weight, so an explicit modifier below still wins. */
.rvds-icon-button:where(.rvds-icon-button-group > *, .rvds-icon-button--grouped) { color: var(--mut-2); }

.rvds-icon-button--ghost {
  border-color: var(--ghost-line);
  --rvds-fill: var(--ghost-fill);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--mut);
}

/* Toolbar and table row: no shell until the pointer arrives. Without a fill
   there is nothing to blur behind; the blur arrives with the hover fill. */
.rvds-icon-button--bare {
  border-color: transparent;
  background: transparent;
  color: var(--mut-2);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.rvds-icon-button--bare:not(:disabled):hover,
.rvds-icon-button--bare[aria-pressed="true"] {
  border-color: transparent;
  --rvds-fill: var(--fill-3);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--fg);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
}

.rvds-icon-button--gold {
  border-color: var(--gold-a40);
  --rvds-fill: var(--gold-a14);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--gold);
}
.rvds-icon-button--gold:not(:disabled):hover,
.rvds-icon-button--gold[aria-pressed="true"] {
  border-color: var(--gold-a60);
  --rvds-fill: var(--gold-a24);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--gold);
}

/* Destructive: quiet at rest, danger on hover. */
.rvds-icon-button--danger {
  border-color: transparent;
  background: transparent;
  color: var(--mut-2);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.rvds-icon-button--danger:not(:disabled):hover,
.rvds-icon-button--danger[aria-pressed="true"] {
  border-color: var(--danger-line);
  --rvds-fill: var(--danger-surface);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  color: var(--danger);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
}

.rvds-icon-button:disabled {
  cursor: default;
  opacity: 0.45;
}

.rvds-icon-button > svg { flex: none; display: block; }

/* ---------------------------------------------------------------------------
 * IconButtonGroup  (components/core/IconButton.jsx)
 *
 *   <div class="rvds-icon-button-group rvds-icon-button-group--sm" role="group">
 *     <button type="button" class="rvds-icon-button" aria-label="Bearbeiten">…</button>
 *     <button type="button" class="rvds-icon-button" aria-label="Löschen">…</button>
 *   </div>
 *
 * Fuses adjacent IconButtons into one control: one border and one background
 * around the set, a hairline between members, pill ends. Members drop their
 * own shell; the modifier on a member only colours its glyph.
 * ------------------------------------------------------------------------ */

.rvds-icon-button-group {
  display: inline-flex;
  align-items: stretch;
  flex: none;
  overflow: hidden;
  box-sizing: border-box;
  height: var(--control-height);
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: var(--fill-2);
}
.rvds-icon-button-group--sm { height: var(--control-height-sm); }

/* A member is a direct child - or, in React, any IconButton inside the group,
   which gets --grouped from the component, so one wrapped in a Tooltip still
   belongs. Plain HTML sets --grouped only on a member inside a wrapper. */
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped) {
  /* The divider is the leading border of every direct child after the first.
     When that child is the button itself, its box grows by the pixel so it
     keeps the width of the first member. */
  --rvds-icon-button-divider: 0px;
  height: 100%;
  /* A numeric size still sets the width. Without one the calc() is invalid
     and the width falls back to auto, sized by padding and glyph. */
  width: calc(var(--rvds-icon-button-size) + var(--rvds-icon-button-divider));
  min-width: calc(36px + var(--rvds-icon-button-divider));
  padding-inline: 9px;
  border-radius: 0;
  border: none;
  background: transparent;
  background-clip: padding-box;
}
.rvds-icon-button-group--sm :is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped) {
  min-width: calc(30px + var(--rvds-icon-button-divider));
}

/* inline-start, not left: in a right-to-left context the divider still sits
   between two members. */
.rvds-icon-button-group > :not(:first-child) { border-inline-start: 1px solid var(--line); }
.rvds-icon-button-group > .rvds-icon-button:not(:first-child) { --rvds-icon-button-divider: 1px; }

/* Hover and pressed fill the member, never its divider; a variant's own hover
   border would otherwise recolour the divider. */
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped):not(:disabled):hover,
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped)[aria-pressed="true"] {
  background: var(--fill-3) padding-box;
  border-color: var(--line);
}
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped)[aria-pressed="true"] { color: var(--fg); }

/* A disabled member dims its glyph and its fill, not its divider: opacity on
   the member would take the divider with it. Mixing the fill with transparent
   at 45% gives the same pixel as the member at 45% opacity. */
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped):disabled { opacity: 1; }
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped):disabled > * { opacity: 0.45; }
:is(.rvds-icon-button-group > .rvds-icon-button, .rvds-icon-button--grouped):disabled[aria-pressed="true"] {
  background: color-mix(in srgb, var(--fill-3) 45%, transparent) padding-box;
}

/* ---------------------------------------------------------------------------
 * Icon  (components/core/Icon.jsx)
 *
 *   <i data-lucide="arrow-right" class="rvds-icon" width="16" height="16" stroke-width="2.2" aria-hidden="true"></i>
 *
 * A Lucide glyph. Size, stroke and colour are attributes of the SVG, not of
 * this class: width/height, stroke-width, stroke (default currentColor).
 * Lucide's createIcons() turns the <i> into an <svg> and keeps the class.
 * ------------------------------------------------------------------------ */

.rvds-icon { flex: none; display: block; }

/* ---------------------------------------------------------------------------
 * IconTile  (components/core/IconTile.jsx)
 *
 *   <span class="rvds-icon-tile"><svg>…</svg></span>
 *   <span class="rvds-icon-tile rvds-icon-tile--danger" style="--rvds-icon-tile-size: 36px">…</span>
 *   <span class="rvds-icon-tile rvds-icon-tile--info"
 *         style="--rvds-icon-tile-size: 28px; --rvds-icon-tile-radius: var(--radius-sm)">…</span>
 *
 * The rounded-square glyph holder beside card and panel headings. No modifier
 * is gold; --neutral, --danger, --success and --info are the others. Border,
 * fill and glyph come from the same tone, at the --*-line step (22%), so a row
 * of mixed tones reads as one family. Default 38px at --radius-lg; the size
 * is the inside, and the 1px border adds to it, as it always has.
 * ------------------------------------------------------------------------ */

/* Size and radius apply to the tile they are set on, never to a tile nested
   inside it. Browsers without @property (Firefox before 128, Safari before
   16.4) ignore this and let a nested tile inherit them. */
@property --rvds-icon-tile-size { syntax: "*"; inherits: false; }
@property --rvds-icon-tile-radius { syntax: "*"; inherits: false; }

.rvds-icon-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--rvds-icon-tile-size, 38px);
  width: var(--rvds-icon-tile-size, 38px);
  flex: none;
  /* --radius-md since 0.17.0: with the raised radii, --radius-lg (17px) made
     a 38px tile nearly round (Alex, 16.10.2026: one step too high). */
  border-radius: var(--rvds-icon-tile-radius, var(--radius-md));
  border: 1px solid var(--gold-a22);
  background: var(--gold-a10);
  color: var(--gold);
}
.rvds-icon-tile--neutral { border-color: var(--line-2); background: var(--fill-2); color: var(--fg); }
.rvds-icon-tile--danger { border-color: var(--danger-line); background: var(--danger-surface); color: var(--danger); }
.rvds-icon-tile--success { border-color: var(--success-line); background: var(--success-surface); color: var(--success); }
.rvds-icon-tile--info { border-color: var(--info-line); background: var(--info-surface); color: var(--info); }

/* ---------------------------------------------------------------------------
 * Reveal  (components/core/Reveal.jsx, behaviour in components.js)
 *
 *   <div class="rvds-reveal">…</div>
 *
 * The site's only entrance animation: 30px up, 900ms, brand easing, once.
 * The hidden start state is the class --pending. Reveal.jsx or components.js
 * sets it, never the markup: without the script nothing is ever hidden, and
 * content must never stay invisible. To stagger siblings, give each block an
 * inline transition-delay in steps of --stagger-step, capped at --stagger-max.
 * ------------------------------------------------------------------------ */

.rvds-reveal {
  transition-property: opacity, transform;
  transition-duration: var(--duration-reveal);
  transition-timing-function: var(--ease);
  opacity: 1;
  transform: translateY(0);
}
.rvds-reveal--pending {
  opacity: 0;
  transform: translateY(var(--reveal-offset));
}
/* Set by components.js for a single style step, so a block hides without
   fading out; never in markup. */
.rvds-reveal--instant { transition: none !important; }
@media (prefers-reduced-motion: reduce) {
  .rvds-reveal--pending { opacity: 1; transform: translateY(0); }
}

/* ---------------------------------------------------------------------------
 * Delta  (components/data-display/Delta.jsx)
 *
 *   <span class="rvds-delta rvds-delta--danger">
 *     <span class="rvds-delta__change"><i data-lucide="trending-down" class="rvds-icon"
 *       width="13" height="13" stroke-width="2.2" aria-hidden="true"></i>-39 %</span>
 *     <span class="rvds-delta__context">1. bis 24.07. gegenüber 1. bis 24.09.</span>
 *   </span>
 *
 * A change between two values: glyph, sign, figure, unit, then muted context.
 * No modifier is neutral (grey, no change or not known); --success and --danger
 * are the other tones, --md the standalone size (glyph 15px instead of 13px).
 * The glyph is trending-up, trending-down or minus, and the sign is always
 * written: the colour never carries the meaning alone.
 * ------------------------------------------------------------------------ */

.rvds-delta { display: inline-flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.rvds-delta__change {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 11.5px;
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--mut-2);
}
.rvds-delta--success > .rvds-delta__change { color: var(--success-text); }
.rvds-delta--danger > .rvds-delta__change { color: var(--danger-text); }
.rvds-delta__context { font-size: 11.5px; color: var(--mut-2); }
.rvds-delta--md > .rvds-delta__change { gap: 5px; font-size: var(--text-15); }
.rvds-delta--md > .rvds-delta__context { font-size: var(--text-12); }

/* ---------------------------------------------------------------------------
 * KpiCard  (components/app/KpiCard.jsx, trend drawing in components.js)
 *
 *   <div class="rvds-kpi-card" data-trend="16 18 15 21 19 23 21 24 22 17 14 10">
 *     <div class="rvds-kpi-card__body">
 *       <div class="rvds-kpi-card__head">
 *         <span class="rvds-kpi-card__label">Google Ads</span>
 *       </div>
 *       <div class="rvds-kpi-card__value"><b class="rvds-kpi-card__figure">267</b></div>
 *       <div class="rvds-kpi-card__foot">
 *         <span class="rvds-delta rvds-delta--danger">…</span>
 *         <span class="rvds-kpi-card__note">15 Anfragen</span>
 *       </div>
 *     </div>
 *   </div>
 *
 * One headline figure: label, figure, a foot with the Delta and a note, and
 * optionally a trend strip along the bottom edge - edge to edge, below the
 * content, never behind it. The strip's last segment takes the tone of the
 * card's Delta (data-trend-tone in HTML, which components.js reads).
 *
 * The card is a size container: below 200px of card width - two cards a row
 * on a phone - it tightens on its own. The threshold is a width of the card,
 * not of the viewport, so it lives here and not in tokens/breakpoints.css.
 * Label, note and Delta never wrap, they end in "…". The row sets the grid;
 * the card fills its cell.
 * ------------------------------------------------------------------------ */

.rvds-kpi-card {
  container: rvds-kpi-card / inline-size;
  box-sizing: border-box;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  --rvds-fill: var(--surface-card);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
  /* A grid row stretches every card to the tallest one; the body fills the
     card so the trend can sit on the bottom edge of each. */
  display: flex;
  flex-direction: column;
}
.rvds-kpi-card__body {
  --rvds-kpi-card-pad: var(--card-padding);
  --rvds-kpi-card-trend-gap: var(--space-3);
  --rvds-kpi-card-foot-gap: var(--space-3);
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--rvds-kpi-card-pad);
}
.rvds-kpi-card--trend > .rvds-kpi-card__body { padding-bottom: 0; }

.rvds-kpi-card__head { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.rvds-kpi-card__label {
  flex: 1;
  min-width: 0;
  font-size: var(--text-125);
  font-weight: var(--weight-bold);
  letter-spacing: 0.03em;
  color: var(--text-label);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rvds-kpi-card__icon { flex: none; display: inline-flex; color: var(--dim); }

.rvds-kpi-card__value { display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; margin-top: var(--space-2); }
.rvds-kpi-card__figure {
  font-size: var(--text-24);
  line-height: 1.1;
  font-weight: var(--weight-semibold);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}
.rvds-kpi-card__unit { font-size: var(--text-13); color: var(--mut-2); }

/* The foot never wraps. The note gives way first; the Delta keeps its full
   width and only ends in "…" when it alone is wider than the foot. A shrinking
   Delta would lose a fraction of a pixel and show "…" for nothing. */
.rvds-kpi-card__foot {
  display: flex;
  align-items: center;
  gap: var(--rvds-kpi-card-foot-gap);
  min-width: 0;
  margin-top: var(--space-1);
  font-size: 11.5px;
}
.rvds-kpi-card__foot > .rvds-delta { flex: none; max-width: 100%; min-width: 0; flex-wrap: nowrap; }
/* Beside a note, a long Delta leaves the note at least 3em, so it shows a
   shortened word and never vanishes without a trace. */
.rvds-kpi-card__foot > .rvds-delta:not(:last-child) { max-width: calc(100% - 3em - var(--rvds-kpi-card-foot-gap)); }
/* A Delta's context, if one is given, follows the same rule: one line, "…". */
.rvds-kpi-card__foot > .rvds-delta > .rvds-delta__context {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rvds-kpi-card__foot > .rvds-delta > .rvds-delta__change {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rvds-kpi-card__foot > .rvds-delta > .rvds-delta__change > svg {
  display: inline-block;
  vertical-align: -2px;
  margin-right: 3px;
}
.rvds-kpi-card__note {
  flex: 0 1 auto;
  min-width: 0;
  /* --mut-2, not --dim: the note is read, and --dim fails small text on a
     card (reference/colour.md, Ink contrast). */
  color: var(--mut-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rvds-kpi-card__trend {
  display: block;
  /* Host pages often cap every svg at 100%; the strip is wider than the body
     by design, edge to edge. */
  max-width: none;
  width: calc(100% + 2 * var(--rvds-kpi-card-pad));
  height: 30px;
  /* margin-top: auto pins it to the bottom; the gap above it is kept by the
     element before it. */
  margin: auto calc(-1 * var(--rvds-kpi-card-pad)) 0;
}
.rvds-kpi-card--trend > .rvds-kpi-card__body > :nth-last-child(2) { margin-bottom: var(--rvds-kpi-card-trend-gap); }
.rvds-kpi-card__trend-area { fill: var(--fill-2); }
.rvds-kpi-card__trend-line,
.rvds-kpi-card__trend-last { fill: none; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.rvds-kpi-card__trend-line,
.rvds-kpi-card__trend-last { stroke: var(--line-3); stroke-width: 1.5px; }
.rvds-kpi-card__trend-last--success,
.rvds-kpi-card__trend-last--danger { stroke-width: 2px; }
.rvds-kpi-card__trend-last--success { stroke: var(--success); }
.rvds-kpi-card__trend-last--danger { stroke: var(--danger); }
/* With a Delta in the foot the last segment takes its tone straight from it,
   so the two cannot disagree - in plain HTML too, whatever changes later.
   Browsers without :has (Firefox before 121, Safari before 15.4) keep the
   tone class components.js set when it drew the strip. */
.rvds-kpi-card:has(> .rvds-kpi-card__body > .rvds-kpi-card__foot > .rvds-delta) .rvds-kpi-card__trend-last {
  stroke: var(--line-3);
  stroke-width: 1.5px;
}
.rvds-kpi-card:has(> .rvds-kpi-card__body > .rvds-kpi-card__foot > .rvds-delta--success) .rvds-kpi-card__trend-last {
  stroke: var(--success);
  stroke-width: 2px;
}
.rvds-kpi-card:has(> .rvds-kpi-card__body > .rvds-kpi-card__foot > .rvds-delta--danger) .rvds-kpi-card__trend-last {
  stroke: var(--danger);
  stroke-width: 2px;
}

/* A narrow card - two a row on a phone - tightens on its own: the tight card
   padding, a smaller figure, a flatter trend, and the decorative glyph gives
   its room to the label. The query measures inside the 1px border, so 198px
   here is a card 200px wide. */
/* A wide card takes the padding steps of a Panel (0.17.0). */
@container rvds-kpi-card (width >= 480px) { .rvds-kpi-card__body { --rvds-kpi-card-pad: var(--card-padding-m); } }
@container rvds-kpi-card (width >= 800px) { .rvds-kpi-card__body { --rvds-kpi-card-pad: var(--card-padding-l); } }

@container rvds-kpi-card (width < 198px) {
  .rvds-kpi-card__body {
    --rvds-kpi-card-pad: var(--card-padding-tight);
    --rvds-kpi-card-trend-gap: var(--space-2);
  }
  .rvds-kpi-card__label { font-size: 11.5px; letter-spacing: 0.02em; }
  .rvds-kpi-card__icon { display: none; }
  .rvds-kpi-card__value { margin-top: var(--space-1); }
  .rvds-kpi-card__figure { font-size: 20px; }
  .rvds-kpi-card__body { --rvds-kpi-card-foot-gap: var(--space-2); }
  .rvds-kpi-card__foot { margin-top: 3px; }
  .rvds-kpi-card__trend { height: 22px; }
}

/* ===========================================================================
 * forms  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * Field  (components/forms/Field.jsx)
 *
 *   <div class="rvds-field">
 *     <label class="rvds-field__label" for="email">E-Mail<span class="rvds-field__required" aria-hidden="true">*</span></label>
 *     <span class="rvds-input"><input class="rvds-input__control" id="email" type="email" placeholder="name@firma.ch" aria-describedby="email-message"></span>
 *     <span class="rvds-field__hint" id="email-message">Wir melden uns am selben Werktag.</span>
 *   </div>
 *
 * Label, control, then a hint or an error. An error replaces the hint and
 * takes `rvds-field__hint--error` and role="alert"; the control itself
 * carries aria-invalid="true", which turns its border, and points at the
 * message with aria-describedby. The error's words are in a passing ink
 * (--field-error-text); --danger stays in the glyph the class draws in front
 * (Lucide circle-alert as a mask), because --danger alone fails small text
 * (0.9.0). Fields stack with --field-row-gap.
 * ------------------------------------------------------------------------ */

.rvds-field { display: flex; flex-direction: column; gap: var(--field-gap); min-width: 0; }
.rvds-field__label { font-size: var(--text-13); font-weight: var(--weight-medium); color: var(--field-label); }
.rvds-field__required { margin-left: 4px; color: var(--field-required); }
.rvds-field__hint { font-size: 12px; line-height: 1.45; color: var(--field-hint); }
.rvds-field__hint--error { display: flex; align-items: flex-start; gap: 6px; color: var(--field-error-text); }
.rvds-field__hint--error::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 2px;
  background: var(--field-error);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4'/%3E%3Cpath d='M12 16h.01'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4'/%3E%3Cpath d='M12 16h.01'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ---------------------------------------------------------------------------
 * Input, Select, Textarea  (components/forms/Input.jsx, Select.jsx, Textarea.jsx)
 *
 *   <span class="rvds-input"><input class="rvds-input__control" placeholder="name@firma.ch"></span>
 *   <span class="rvds-input rvds-input--inset">
 *     <span class="rvds-input__icon"><svg …15px, stroke 2…></svg></span>
 *     <input class="rvds-input__control" placeholder="Teilenummer">
 *   </span>
 *   <span class="rvds-input rvds-input--inset">
 *     <input class="rvds-input__control" inputmode="decimal" value="3900">
 *     <span class="rvds-input__suffix">CHF</span>
 *   </span>
 *   <span class="rvds-select">
 *     <select class="rvds-select__control"><option value="" disabled selected>Modell wählen</option>…</select>
 *     <span class="rvds-select__chevron" aria-hidden="true"><svg …chevron-down, 15px, stroke 2…></svg></span>
 *   </span>
 *   <textarea class="rvds-textarea" rows="4"></textarea>
 *
 * Bordered on a recessed fill, never filled. One chrome for all three:
 * focus warms the border and adds the gold glow, aria-invalid="true" turns
 * the border --danger (and the glow with it), `disabled` dims to 50%. An icon
 * or suffix takes `rvds-input--inset`, which widens both sides so the text
 * keeps equal air. `--sm` on the wrapper is the dense height for table
 * toolbars only.
 * ------------------------------------------------------------------------ */

.rvds-input,
.rvds-select { position: relative; display: block; width: 100%; }

.rvds-input__control,
.rvds-select__control,
.rvds-textarea {
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  height: var(--field-height);
  padding: 0 var(--field-padding-x);
  border-radius: var(--field-radius);
  border: 1px solid var(--field-border);
  background: var(--field-bg);
  box-shadow: var(--control-inset);
  color: var(--field-text);
  font-family: var(--font-sans);
  font-size: var(--text-14);
  outline: none;
  opacity: 1;
  transition: border-color var(--duration-base), box-shadow var(--duration-base), background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-input--sm > .rvds-input__control,
.rvds-select--sm > .rvds-select__control { height: var(--field-height-sm); }

.rvds-input__control:focus,
.rvds-select__control:focus,
.rvds-textarea:focus { border-color: var(--field-border-focus); box-shadow: var(--focus-glow); }
.rvds-input__control[aria-invalid="true"],
.rvds-select__control[aria-invalid="true"],
.rvds-textarea[aria-invalid="true"] { border-color: var(--field-border-error); }
.rvds-input__control[aria-invalid="true"]:focus,
.rvds-select__control[aria-invalid="true"]:focus,
.rvds-textarea[aria-invalid="true"]:focus { box-shadow: var(--focus-glow-error); }
.rvds-input__control:disabled,
.rvds-select__control:disabled,
.rvds-textarea:disabled { background: var(--field-bg-disabled); opacity: 0.5; }

.rvds-input--inset > .rvds-input__control { padding-left: 36px; padding-right: 36px; }
/* 0.14.0: only the side that holds something widens. A suffix alone (CHF)
   left 36px of empty air before the value; an icon alone did the same on the
   right. Browsers without :has keep the symmetric padding. */
.rvds-input--inset:not(:has(> .rvds-input__icon)) > .rvds-input__control { padding-left: var(--field-padding-x); }
.rvds-input--inset:not(:has(> :is(.rvds-input__suffix, .rvds-input__clear, .rvds-input__action))) > .rvds-input__control { padding-right: var(--field-padding-x); }
.rvds-input__icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--dim);
  pointer-events: none;
}
.rvds-input__suffix {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  font-size: var(--text-13);
  color: var(--dim);
  pointer-events: none;
}

.rvds-select__control { padding-right: 36px; appearance: none; cursor: pointer; }
/* The open option list is drawn by the browser. Chrome on Windows takes the
   control's background for it and cannot use a see-through one, so the list
   came out white while the text stayed white (Alex 03.10.2026: «alle dropdowns
   sind weiss im hintergrund und mit unleserlichem weissem text»). Options get an
   opaque ground and the text colour of the theme. */
.rvds-select__control option,
.rvds-select__control optgroup { background-color: var(--bg-2); color: var(--fg); }
.rvds-select__control:disabled { cursor: default; }
.rvds-select__chevron {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--mut-2);
  pointer-events: none;
}

.rvds-textarea {
  height: auto;
  padding: 10px var(--field-padding-x);
  line-height: var(--leading-relaxed);
  resize: vertical;
}

/* ---------------------------------------------------------------------------
 * Checkbox, Radio, Switch  (components/forms/Checkbox.jsx, Radio.jsx, Switch.jsx)
 *
 *   <label class="rvds-checkbox" for="agb">
 *     <input class="rvds-checkbox__input" id="agb" type="checkbox" checked>
 *     <span class="rvds-checkbox__box" aria-hidden="true"><svg …check, 12px, stroke 3.2…></svg></span>
 *     <span class="rvds-checkbox__text">
 *       <span class="rvds-checkbox__label">AGB akzeptieren</span>
 *       <span class="rvds-checkbox__description">Pflicht für die Buchung.</span>
 *     </span>
 *   </label>
 *
 *   <div class="rvds-radio-group" role="radiogroup">
 *     <label class="rvds-radio" for="k-mail">
 *       <input class="rvds-radio__input" id="k-mail" type="radio" name="k" value="mail" checked>
 *       <span class="rvds-radio__box" aria-hidden="true"><span class="rvds-radio__dot"></span></span>
 *       <span class="rvds-radio__text"><span class="rvds-radio__label">E-Mail</span></span>
 *     </label>
 *     …
 *   </div>
 *
 *   <label class="rvds-switch" for="sms">
 *     <input class="rvds-switch__input" id="sms" type="checkbox" role="switch" checked>
 *     <span class="rvds-switch__track" aria-hidden="true"><span class="rvds-switch__knob"></span></span>
 *     <span class="rvds-switch__text"><span class="rvds-switch__label">Terminerinnerung per SMS</span></span>
 *   </label>
 *
 * The native input stays in the markup, invisible, and carries the state:
 * :checked, :focus, :disabled. The tick of a checkbox is always in the box
 * and shows only when checked. A partly-on checkbox takes
 * `rvds-checkbox--indeterminate` and draws a dash instead. Disabled is
 * `disabled` on the input plus `--disabled` on the label (the label cannot
 * see its input without :has). aria-invalid="true" on a checkbox input turns
 * its border --danger. Checked is a tint behind a gold hairline with a gold
 * mark - never a solid gold block.
 * ------------------------------------------------------------------------ */

.rvds-checkbox,
.rvds-radio,
.rvds-switch {
  display: inline-flex;
  align-items: flex-start;
  gap: 10px;
  cursor: pointer;
}
.rvds-switch { gap: 12px; }
.rvds-checkbox--disabled,
.rvds-radio--disabled,
.rvds-switch--disabled { cursor: default; opacity: 0.5; }

.rvds-checkbox__input,
.rvds-radio__input,
.rvds-switch__input { position: absolute; opacity: 0; width: 0; height: 0; }

.rvds-checkbox__text,
.rvds-radio__text,
.rvds-switch__text { min-width: 0; }
.rvds-checkbox__label,
.rvds-radio__label,
.rvds-switch__label { display: block; font-size: var(--text-14); line-height: 1.35; color: var(--fg); }
.rvds-checkbox__description,
.rvds-radio__description,
.rvds-switch__description { display: block; margin-top: 2px; font-size: 12px; line-height: 1.45; color: var(--mut-2); }

.rvds-checkbox__box,
.rvds-radio__box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 18px;
  width: 18px;
  flex: none;
  margin-top: 1px;
  box-sizing: border-box;
  border: 1px solid var(--control-off-border);
  background: var(--control-off-bg);
  box-shadow: var(--control-inset);
}
.rvds-checkbox__box {
  border-radius: var(--radius-sm);
  color: var(--control-on-mark);
  transition: background var(--duration-fast), --rvds-fill var(--duration-fast) var(--ease-settle), border-color var(--duration-base), box-shadow var(--duration-base);
}
.rvds-checkbox__box > svg { display: none; }
.rvds-checkbox__input:checked + .rvds-checkbox__box > svg { display: block; }
.rvds-checkbox--indeterminate > .rvds-checkbox__input + .rvds-checkbox__box > svg { display: none; }
.rvds-checkbox--indeterminate > .rvds-checkbox__input + .rvds-checkbox__box::after {
  content: "";
  height: 2px;
  width: 9px;
  border-radius: 1px;
  background: var(--control-on-mark);
}
.rvds-checkbox__input:checked + .rvds-checkbox__box,
.rvds-checkbox--indeterminate > .rvds-checkbox__input + .rvds-checkbox__box {
  border-color: var(--control-on-border);
  background: var(--control-on-bg);
  box-shadow: var(--control-raised);
}
.rvds-checkbox__input[aria-invalid="true"] + .rvds-checkbox__box { border-color: var(--danger); }
.rvds-checkbox__input:focus + .rvds-checkbox__box { box-shadow: var(--focus-glow); }

.rvds-radio__box {
  border-radius: var(--radius-pill);
  transition: border-color var(--duration-base), box-shadow var(--duration-base);
}
.rvds-radio__dot {
  height: 8px;
  width: 8px;
  border-radius: var(--radius-pill);
  background: var(--control-on-mark);
  transform: scale(0);
  transition: transform var(--duration-fast) var(--ease-settle);
}
.rvds-radio__input:checked + .rvds-radio__box {
  border-color: var(--control-on-border);
  background: var(--control-on-bg);
  box-shadow: var(--control-raised);
}
.rvds-radio__input:checked + .rvds-radio__box > .rvds-radio__dot { transform: scale(1); }
.rvds-radio__input:focus + .rvds-radio__box { box-shadow: var(--focus-glow); }

.rvds-radio-group { display: flex; flex-direction: column; gap: 12px; flex-wrap: wrap; }
.rvds-radio-group--row { flex-direction: row; gap: 20px; }

/* Radio as a card (0.8.0): a choice that needs room - shipping, payment.
   The whole card is the hit area. It is tinted at rest; chosen it takes the
   state treatment, --state-surface behind a gold hairline - never gold fill.
   The dot inside stays the Radio's own. `__aside` is the figure on the right
   (a price). The chosen frame needs :has; without it (Firefox before 121,
   Safari before 15.4) only the dot shows the choice.

   <div class="rvds-radio-group rvds-radio-group--card" role="radiogroup">
     <label class="rvds-radio rvds-radio--card" for="v-post">
       <input class="rvds-radio__input" id="v-post" type="radio" name="versand" value="post" checked>
       <span class="rvds-radio__box" aria-hidden="true"><span class="rvds-radio__dot"></span></span>
       <span class="rvds-radio__text">
         <span class="rvds-radio__label">Versand Schweiz/Liechtenstein</span>
         <span class="rvds-radio__description">Pauschale, Schweizerische Post</span>
       </span>
       <span class="rvds-radio__aside">12.97 CHF</span>
     </label>
   </div> */
.rvds-radio-group--card { gap: var(--space-3); flex-wrap: nowrap; }
.rvds-radio-group--card.rvds-radio-group--row { flex-wrap: wrap; }
.rvds-radio-group--card.rvds-radio-group--row > .rvds-radio--card { flex: 1 1 220px; }
.rvds-radio--card {
  display: flex;
  gap: var(--space-5);
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  padding: var(--space-6) var(--card-padding);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--fill-1);
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant), border-color var(--duration-instant);
}
.rvds-radio--card > .rvds-radio__text { flex: 1 1 auto; }
.rvds-radio--card:not(.rvds-radio--disabled):hover { border-color: var(--line-2); }
.rvds-radio--card:has(> .rvds-radio__input:checked) { background: var(--state-surface); border-color: var(--state-border); }
.rvds-radio__aside {
  flex: none;
  margin-left: auto;
  font-size: var(--text-14);
  line-height: 1.35;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.rvds-switch__track {
  position: relative;
  display: inline-block;
  flex: none;
  margin-top: 1px;
  box-sizing: border-box;
  height: 22px;
  width: 38px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--control-off-border);
  background: var(--control-off-bg);
  box-shadow: var(--control-inset);
  transition: background var(--duration-base), --rvds-fill var(--duration-base), border-color var(--duration-base), box-shadow var(--duration-base);
}
.rvds-switch__knob {
  position: absolute;
  top: 2px;
  left: 2px;
  height: 16px;
  width: 16px;
  border-radius: var(--radius-pill);
  background: var(--mut-2);
  box-shadow: var(--control-raised);
  transform: translateX(0);
  transition: transform var(--duration-fast) var(--ease-settle), background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-switch__input:checked + .rvds-switch__track { border-color: var(--control-on-border); background: var(--control-on-bg); }
.rvds-switch__input:checked + .rvds-switch__track > .rvds-switch__knob { background: var(--control-on-mark); transform: translateX(16px); }
.rvds-switch__input:focus + .rvds-switch__track { box-shadow: var(--focus-glow); }

/* ---------------------------------------------------------------------------
 * Stepper  (components/forms/Stepper.jsx, behaviour in components.js)
 *
 *   <div class="rvds-stepper">
 *     <button type="button" class="rvds-stepper__nudge" tabindex="-1" aria-label="Verringern"><svg …minus, 14px, stroke 2.4…></svg></button>
 *     <input class="rvds-stepper__value" type="number" inputmode="numeric" min="1" max="10" step="1" value="2" name="menge">
 *     <button type="button" class="rvds-stepper__nudge" tabindex="-1" aria-label="Erhöhen"><svg …plus…></svg></button>
 *   </div>
 *
 * Minus, an editable value, plus in one recessed pill. components.js makes
 * the nudges work from the input's min, max and step (default 1, 99, 1, as in
 * React), clamps what is typed and disables a nudge at its bound. The input
 * may be type="number" - its spin buttons are hidden here - so that without
 * the script the arrow keys still step it; with scripting off the nudges are
 * hidden and the field is widened to type in. Disabled is `disabled` on all
 * three plus `--disabled` on the pill; `--sm` is the dense height.
 * ------------------------------------------------------------------------ */

.rvds-stepper {
  display: inline-flex;
  align-items: stretch;
  flex: none;
  overflow: hidden;
  box-sizing: border-box;
  height: var(--field-height);
  border-radius: var(--radius-pill);
  border: 1px solid var(--field-border);
  background: var(--field-bg);
  box-shadow: var(--control-inset);
}
.rvds-stepper--sm { height: var(--field-height-sm); }
.rvds-stepper--disabled { opacity: 0.5; }
.rvds-stepper__nudge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 34px;
  flex: none;
  border: none;
  background: transparent;
  color: var(--mut);
  cursor: pointer;
  transition: background var(--duration-base), --rvds-fill var(--duration-base), color var(--duration-base);
}
.rvds-stepper__nudge:not(:disabled):hover { background: var(--fill-3); }
.rvds-stepper__nudge:disabled { color: var(--dim); cursor: default; }
.rvds-stepper__value {
  width: 40px;
  border: none;
  background: none;
  outline: none;
  text-align: center;
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}
.rvds-stepper__value[type="number"] { appearance: textfield; -moz-appearance: textfield; }
.rvds-stepper__value::-webkit-inner-spin-button,
.rvds-stepper__value::-webkit-outer-spin-button { appearance: none; -webkit-appearance: none; margin: 0; }
@media (scripting: none) {
  .rvds-stepper__nudge { display: none; }
  .rvds-stepper__value { width: 56px; }
}

/* ===========================================================================
 * feedback  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * Alert  (components/feedback/Alert.jsx, dismiss in components.js)
 *
 *   <div class="rvds-alert rvds-alert--danger" role="alert">
 *     <span class="rvds-icon-tile rvds-icon-tile--danger rvds-alert__tile"><svg …triangle-alert, 15px, stroke 2…></svg></span>
 *     <div class="rvds-alert__body">
 *       <b class="rvds-alert__title">Diagnose fehlgeschlagen</b>
 *       <div class="rvds-alert__text">Das Fahrzeug war nicht erreichbar.</div>
 *       <div class="rvds-alert__actions">…buttons…</div>
 *     </div>
 *     <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-alert__dismiss" aria-label="Schliessen"><svg …x, 16px…></svg></button>
 *   </div>
 *
 * No modifier is info; --success, --danger and --neutral are the others. The
 * glyph sits in an IconTile of the same tone: info and neutral take `info`,
 * success `circle-check-big`, danger `triangle-alert`. role="alert" for
 * danger, role="status" otherwise. Capped at --measure-alert. The dismiss
 * button removes the alert in components.js (see Toast).
 * ------------------------------------------------------------------------ */

.rvds-alert {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  box-sizing: border-box;
  width: 100%;
  max-width: var(--measure-alert);
  border-radius: var(--radius-lg);
  border: 1px solid var(--info-border);
  background: var(--info-surface);
  padding: 14px 16px;
}
.rvds-alert--success { border-color: var(--success-border); background: var(--success-surface); }
.rvds-alert--danger { border-color: var(--danger-border); background: var(--danger-surface); }
.rvds-alert--neutral { border-color: var(--line-2); background: var(--fill-2); }
.rvds-alert__tile { --rvds-icon-tile-size: 28px; --rvds-icon-tile-radius: var(--radius-sm); margin-top: 1px; }
.rvds-alert__body { flex: 1; min-width: 0; }
.rvds-alert__title { display: block; font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-alert__text { font-size: var(--text-13); line-height: var(--leading-relaxed); color: var(--mut); }
.rvds-alert__title + .rvds-alert__text { margin-top: 3px; }
.rvds-alert__actions { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 8px; }
.rvds-alert__dismiss { --rvds-icon-button-size: 26px; }

/* Inline (0.15.0): an alert with an action that spans grid tracks. The box
   takes the width of its cell, so the reading cap moves from the box to the
   text (--measure-text); from 560px of its own width the action stands
   beside the text instead of under it. Title and text sit in
   `rvds-alert__content`, so they can share one side.
     <div class="rvds-alert rvds-alert--inline" role="status">
       <span class="rvds-icon-tile …">…</span>
       <div class="rvds-alert__body">
         <div class="rvds-alert__content"><b class="rvds-alert__title">…</b><div class="rvds-alert__text">…</div></div>
         <div class="rvds-alert__actions">…buttons…</div>
       </div>
     </div> */
.rvds-alert--inline { max-width: none; container: rvds-alert / inline-size; }
.rvds-alert--inline .rvds-alert__content { max-width: var(--measure-text); }
@container rvds-alert (width >= 560px) {
  .rvds-alert__body:has(> .rvds-alert__actions) { display: flex; align-items: center; gap: 16px; }
  .rvds-alert__body > .rvds-alert__content { flex: 0 1 auto; min-width: 0; }
  .rvds-alert__body > .rvds-alert__actions { margin: 0 0 0 auto; flex: none; }
}

/* ---------------------------------------------------------------------------
 * EmptyState  (components/feedback/EmptyState.jsx)
 *
 *   <div class="rvds-empty-state">
 *     <span class="rvds-icon-tile rvds-icon-tile--neutral rvds-empty-state__tile"><svg …20px, stroke 1.8…></svg></span>
 *     <h3 class="rvds-empty-state__title">Warenkorb ist leer</h3>
 *     <p class="rvds-empty-state__description">Teile erscheinen hier, sobald du etwas hinzufügst.</p>
 *     <div class="rvds-empty-state__actions"><a class="rvds-button rvds-button--ghost" href="/shop">Zum Shop</a></div>
 *   </div>
 *
 * The one dashed line in the system: "nothing here yet". `--compact` inside a
 * card or a table, without it as page furniture.
 * ------------------------------------------------------------------------ */

.rvds-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  box-sizing: border-box;
  gap: 12px;
  padding: 48px 24px;
  border-radius: var(--radius-lg);
  border: 1px dashed var(--line-2);
  background: var(--fill-1);
}
.rvds-empty-state--compact { padding: 28px 20px; }
.rvds-empty-state__tile { --rvds-icon-tile-size: 44px; }
.rvds-empty-state__title { margin: 0; font-size: var(--text-16); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-empty-state__description { margin: 0; max-width: 360px; font-size: var(--text-13); line-height: var(--leading-relaxed); color: var(--mut-2); }
.rvds-empty-state__actions { margin-top: 6px; display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }

/* ---------------------------------------------------------------------------
 * Toast, ToastStack  (components/feedback/Toast.jsx, dismiss in components.js)
 *
 *   <div class="rvds-toast-stack">
 *     <div class="rvds-toast rvds-toast--success" role="status">
 *       <span class="rvds-toast__icon"><svg …circle-check-big, 16px, stroke 2…></svg></span>
 *       <div class="rvds-toast__body">
 *         <b class="rvds-toast__title">Gespeichert</b>
 *         <div class="rvds-toast__text">Offerte als Entwurf abgelegt.</div>
 *       </div>
 *       <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-toast__dismiss" aria-label="Schliessen"><svg …x…></svg></button>
 *     </div>
 *   </div>
 *
 * A toast keeps the neutral menu surface; only its glyph carries the tone (no
 * modifier is neutral). The stack is fixed bottom-centre above the home
 * indicator; `--end` moves it to the bottom right from sm up. In plain HTML a
 * click on __dismiss removes the toast: components.js fires a cancelable
 * `rvds:dismiss` event on it first, so a page can keep it or animate it.
 * ------------------------------------------------------------------------ */

.rvds-toast {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  box-sizing: border-box;
  width: 100%;
  max-width: var(--measure-toast);
  pointer-events: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-2);
  background: var(--menu-bg);
  -webkit-backdrop-filter: blur(var(--blur-menu));
  backdrop-filter: blur(var(--blur-menu));
  box-shadow: var(--shadow-menu);
  padding: 13px 15px;
}
.rvds-toast__icon { margin-top: 1px; flex: none; color: var(--mut); }
.rvds-toast--info > .rvds-toast__icon { color: var(--info); }
.rvds-toast--success > .rvds-toast__icon { color: var(--success); }
.rvds-toast--danger > .rvds-toast__icon { color: var(--danger); }
.rvds-toast__body { flex: 1; min-width: 0; }
.rvds-toast__title { display: block; font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-toast__text { font-size: var(--text-13); line-height: 1.5; color: var(--mut); }
.rvds-toast__title + .rvds-toast__text { margin-top: 2px; }
.rvds-toast__dismiss { --rvds-icon-button-size: 24px; }

.rvds-toast-stack {
  position: fixed;
  z-index: 700;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
  left: 18px;
  right: 18px;
  bottom: calc(18px + env(safe-area-inset-bottom));
  align-items: center;
}
@media (min-width: 640px) {
  .rvds-toast-stack--end { align-items: flex-end; }
}

/* ---------------------------------------------------------------------------
 * FaqItem  (components/feedback/FaqItem.jsx, behaviour in components.js)
 *
 *   <div class="rvds-faq-item" id="garantie">
 *     <h3 class="rvds-faq-item__heading">
 *       <button type="button" class="rvds-faq-item__toggle" id="garantie-question" aria-expanded="false" aria-controls="garantie-answer">
 *         <span class="rvds-faq-item__question">Verliere ich die Tesla Garantie?</span>
 *         <span class="rvds-faq-item__glyph"><svg …plus, 16px, stroke 2…></svg></span>
 *       </button>
 *     </h3>
 *     <div class="rvds-faq-item__panel" id="garantie-answer"><div class="rvds-faq-item__inner">
 *       <p class="rvds-faq-item__answer">Nein. …</p>
 *     </div></div>
 *   </div>
 *
 * A hairline-divided accordion row. aria-expanded on the toggle is the state:
 * "true" turns the question white, the plus gold and 45°, and opens the
 * panel. The panel only collapses once the item has `--collapsible`, which
 * FaqItem.jsx or components.js sets, never the markup: without the script
 * every answer stays readable. Collapsed, the panel is visibility: hidden
 * once the collapse has run, so assistive technology and Tab skip it
 * (0.9.0). The heading around the toggle is optional; with it the
 * collapse needs :has(), and without :has() the answer simply stays open.
 * The answer keeps the reading measure (--measure-answer) and may hold
 * several paragraphs or a list. In plain HTML opening one item closes the
 * open ones beside it, as the FAQ list in React keeps a single open index.
 * ------------------------------------------------------------------------ */

.rvds-faq-item { border-bottom: 1px solid var(--line); }
.rvds-faq-item__heading { margin: 0; font: inherit; }
.rvds-faq-item__toggle {
  display: flex;
  width: 100%;
  max-width: var(--measure-body);
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 24px 0;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-sans);
  font-size: clamp(16px, 2vw, 18px);
  font-weight: var(--weight-medium);
  color: var(--mut);
  transition: color var(--duration-base);
}
.rvds-faq-item__toggle:hover,
.rvds-faq-item__toggle[aria-expanded="true"] { color: var(--fg); }
.rvds-faq-item__glyph {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  width: 20px;
  flex: none;
  color: var(--mut-2);
  transform: rotate(0deg);
  transition: transform var(--duration-icon) var(--ease), color var(--duration-icon) var(--ease);
}
.rvds-faq-item__toggle[aria-expanded="true"] > .rvds-faq-item__glyph { color: var(--gold); transform: rotate(45deg); }
.rvds-faq-item__panel {
  display: grid;
  overflow: hidden;
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows var(--duration-accordion) var(--ease), visibility 0s;
}
/* Closed: the row collapses, then (after the collapse) leaves the
   accessibility tree and the tab order. Opening shows it at once. */
.rvds-faq-item--collapsible > .rvds-faq-item__toggle[aria-expanded="false"] + .rvds-faq-item__panel,
.rvds-faq-item--collapsible:has(> .rvds-faq-item__heading > .rvds-faq-item__toggle[aria-expanded="false"]) > .rvds-faq-item__panel {
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows var(--duration-accordion) var(--ease), visibility 0s linear var(--duration-accordion);
}
.rvds-faq-item__inner { min-height: 0; }
.rvds-faq-item__answer {
  max-width: var(--measure-answer);
  margin: 0;
  padding-bottom: 28px;
  font-size: var(--text-155);
  line-height: var(--leading-loose);
  color: var(--mut);
}
/* Several paragraphs or a list in one answer. */
.rvds-faq-item__answer > * { margin: 0; }
.rvds-faq-item__answer > * + * { margin-top: var(--space-5); }
.rvds-faq-item__answer > ul,
.rvds-faq-item__answer > ol { padding-left: 1.25em; }

/* ---------------------------------------------------------------------------
 * Disclosure  (components/feedback/Disclosure.jsx) - new in 0.8.0
 *
 *   <details class="rvds-disclosure">
 *     <summary class="rvds-disclosure__summary">
 *       <svg class="rvds-icon" …ticket, 16px, stroke 2…></svg>Gutschein hinzufügen
 *       <span class="rvds-disclosure__chevron"><svg …chevron-down, 16px, stroke 2…></svg></span>
 *     </summary>
 *     <div class="rvds-disclosure__body">…a Field…</div>
 *   </details>
 *
 * The small "add something optional" link: a glyph, a short text and a
 * chevron that turns 180° when open. Native <details>, so it opens and closes
 * without any script. For a question and its answer use FaqItem.
 * ------------------------------------------------------------------------ */

.rvds-disclosure { min-width: 0; }
.rvds-disclosure__summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--control-height);
  cursor: pointer;
  list-style: none;
  font-size: var(--text-14);
  color: var(--mut);
  transition: color var(--duration-instant);
}
.rvds-disclosure__summary::-webkit-details-marker { display: none; }
.rvds-disclosure__summary::marker { content: ""; }
.rvds-disclosure__summary:hover { color: var(--fg); }
.rvds-disclosure__summary > svg { flex: none; opacity: 0.8; }
.rvds-disclosure__chevron { display: inline-flex; transition: transform var(--duration-icon) var(--ease); }
.rvds-disclosure[open] > .rvds-disclosure__summary > .rvds-disclosure__chevron { transform: rotate(180deg); }
.rvds-disclosure__body { display: grid; gap: var(--field-gap); margin-top: var(--space-3); }

/* ---------------------------------------------------------------------------
 * Progress, Spinner  (components/feedback/Progress.jsx)
 *
 *   <div class="rvds-progress">
 *     <div class="rvds-progress__head"><span class="rvds-progress__label">Schritt 2 von 4</span><b class="rvds-progress__value">50%</b></div>
 *     <span class="rvds-progress__track" role="progressbar" aria-valuenow="2" aria-valuemin="0" aria-valuemax="4">
 *       <span class="rvds-progress__fill" style="width: 50%"></span>
 *     </span>
 *   </div>
 *   <span class="rvds-spinner" role="status" aria-label="Lädt"></span>
 *   <span class="rvds-spinner" style="--rvds-spinner-size: 14px" role="status" aria-label="Lädt"></span>
 *
 * The fill width is the value in per cent, set on the fill. `--sm` on the
 * progress is the 3px track. The spinner is 18px by default.
 * ------------------------------------------------------------------------ */

.rvds-progress { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.rvds-progress__head { display: flex; justify-content: space-between; gap: 12px; font-size: var(--text-13); }
.rvds-progress__label { color: var(--mut); }
.rvds-progress__value { font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; color: var(--fg); }
.rvds-progress__track {
  display: block;
  overflow: hidden;
  height: var(--track-height);
  border-radius: var(--radius-pill);
  background: var(--track-bg);
  box-shadow: var(--control-inset);
}
.rvds-progress--sm > .rvds-progress__track { height: 3px; }
.rvds-progress__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--track-fill);
  transition: width var(--duration-base) var(--ease-move);
}

.rvds-spinner {
  display: inline-block;
  flex: none;
  box-sizing: border-box;
  height: var(--rvds-spinner-size, 18px);
  width: var(--rvds-spinner-size, 18px);
  border-radius: var(--radius-pill);
  border: 2px solid var(--line-2);
  border-top-color: var(--gold);
  animation: revoltaris-spin 700ms linear infinite;
}

/* ===========================================================================
 * navigation  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * Steps  (components/navigation/Steps.jsx)
 *
 *   <ol class="rvds-steps">
 *     <li class="rvds-steps__item rvds-steps__item--done">
 *       <span class="rvds-steps__marker"><svg …check, 12px, stroke 2.6…></svg></span>
 *       <span class="rvds-steps__label">Fahrzeug</span>
 *       <span class="rvds-steps__line" aria-hidden="true"></span>
 *     </li>
 *     <li class="rvds-steps__item" aria-current="step">
 *       <span class="rvds-steps__marker">2</span><span class="rvds-steps__label">Termin</span>
 *       <span class="rvds-steps__line" aria-hidden="true"></span>
 *     </li>
 *     <li class="rvds-steps__item"><span class="rvds-steps__marker">3</span><span class="rvds-steps__label">Kontakt</span></li>
 *   </ol>
 *
 * A step behind the current one is `--done` and shows a tick; the current
 * one carries aria-current="step"; the last item has no line.
 * ------------------------------------------------------------------------ */

.rvds-steps { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 4px; margin: 0; padding: 0; list-style: none; }
.rvds-steps__item { display: flex; align-items: center; gap: 9px; flex: 1 1 auto; min-width: 0; }
.rvds-steps__item:last-child { flex: 0 1 auto; }
.rvds-steps__marker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  width: 24px;
  flex: none;
  box-sizing: border-box;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: transparent;
  color: var(--dim);
  font-size: 11.5px;
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.rvds-steps__label {
  flex: none;
  font-size: var(--text-13);
  white-space: nowrap;
  line-height: 1;
  font-weight: var(--weight-medium);
  color: var(--dim);
}
.rvds-steps__line { flex: 1 1 auto; min-width: 16px; margin-left: 3px; height: 1px; background: var(--line-2); }
.rvds-steps__item--done > .rvds-steps__marker { border-color: var(--gold-a40); color: var(--gold-a60); }
.rvds-steps__item--done > .rvds-steps__label { color: var(--mut-2); }
.rvds-steps__item--done > .rvds-steps__line { background: var(--gold-a40); }
.rvds-steps__item[aria-current="step"] > .rvds-steps__marker { border-color: var(--gold); background: var(--gold-a14); color: var(--gold); }
.rvds-steps__item[aria-current="step"] > .rvds-steps__label { font-weight: var(--weight-semibold); color: var(--fg); }

/* ---------------------------------------------------------------------------
 * Breadcrumb  (components/navigation/Breadcrumb.jsx)
 *
 *   <nav class="rvds-breadcrumb" aria-label="Brotkrumen">
 *     <a class="rvds-breadcrumb__link" href="/shop">Shop</a>
 *     <span class="rvds-breadcrumb__separator" aria-hidden="true"><svg …chevron-right, 13px, stroke 2…></svg></span>
 *     <span class="rvds-breadcrumb__current" aria-current="page">Modul 85 kWh</span>
 *   </nav>
 *
 * The last item is the current page and is no link.
 * ------------------------------------------------------------------------ */

.rvds-breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: var(--text-13); }
/* The link keeps its ink on hover, as it always has: the colour was inline
   and beat the global a:hover. */
.rvds-breadcrumb__link,
.rvds-breadcrumb__link:hover { color: var(--mut-2); }
.rvds-breadcrumb__current { color: var(--mut); }
.rvds-breadcrumb__separator { color: var(--dim); display: inline-flex; }

/* ---------------------------------------------------------------------------
 * Tabs  (components/navigation/Tabs.jsx, behaviour in components.js)
 *
 *   <div class="rvds-tabs" role="tablist">
 *     <button type="button" class="rvds-tabs__tab" role="tab" aria-selected="true" aria-controls="p-offen" data-value="offen">
 *       Offen <span class="rvds-tag">3</span><span class="rvds-tabs__line" aria-hidden="true"></span>
 *     </button>
 *     <button type="button" class="rvds-tabs__tab" role="tab" aria-selected="false" aria-controls="p-erledigt" data-value="erledigt">
 *       Erledigt<span class="rvds-tabs__line" aria-hidden="true"></span>
 *     </button>
 *   </div>
 *
 * aria-selected is the state: the label turns white and semibold, the gold
 * line sweeps in. In plain HTML a click selects the tab, fires `rvds:change`
 * on the tablist (detail.value is data-value, else the id) and, where the
 * tabs name their panes with aria-controls, shows the chosen pane and hides
 * the others. Write every pane without `hidden`: the script hides them, so
 * without it every pane stays readable.
 * ------------------------------------------------------------------------ */

.rvds-tabs { display: flex; gap: 2px; overflow-x: auto; overflow-y: hidden; border-bottom: 1px solid var(--line); }
.rvds-tabs__tab {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: none;
  background: none;
  cursor: pointer;
  white-space: nowrap;
  padding: 0 14px;
  height: var(--control-height);
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--mut-2);
  transition: color var(--duration-base);
}
.rvds-tabs__tab[aria-selected="true"] { font-weight: var(--weight-semibold); color: var(--state-ink); }
.rvds-tabs__line {
  position: absolute;
  inset-inline: 10px;
  bottom: 0;
  height: 2px;
  background: var(--state-line);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform var(--duration-base) var(--ease-move);
}
.rvds-tabs__tab[aria-selected="true"] > .rvds-tabs__line { transform: scaleX(1); }

/* ---------------------------------------------------------------------------
 * Pagination  (components/navigation/Pagination.jsx)
 *
 *   <nav class="rvds-pagination" aria-label="Seiten">
 *     <a class="rvds-icon-button" href="?seite=2" aria-label="Vorherige Seite"><svg …chevron-left…></svg></a>
 *     <a class="rvds-pagination__page" href="?seite=1">1</a>
 *     <span class="rvds-pagination__gap">…</span>
 *     <a class="rvds-pagination__page" href="?seite=3" aria-current="page">3</a>
 *     …
 *     <button type="button" class="rvds-icon-button" aria-label="Nächste Seite" disabled><svg …chevron-right…></svg></button>
 *   </nav>
 *
 * A page is a link or a button; the current one carries aria-current="page"
 * and takes the state treatment. At the first or last page the arrow is a
 * disabled <button>, since a link cannot be disabled.
 * ------------------------------------------------------------------------ */

.rvds-pagination { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.rvds-pagination__gap { width: 22px; text-align: center; color: var(--dim); }
.rvds-pagination__page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--control-height);
  height: var(--control-height);
  box-sizing: border-box;
  border-radius: var(--radius-pill);
  cursor: pointer;
  padding: 0 10px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--mut);
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  transition: background var(--duration-base), --rvds-fill var(--duration-base), color var(--duration-base), border-color var(--duration-base);
}
.rvds-pagination__page:hover { color: var(--mut); }
.rvds-pagination__page[aria-current="page"],
.rvds-pagination__page[aria-current="page"]:hover {
  border-color: var(--state-border);
  background: var(--state-surface);
  color: var(--state-ink);
  font-weight: var(--weight-semibold);
}

/* ===========================================================================
 * app  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * Panel  (components/app/Panel.jsx)
 *
 *   <section class="rvds-panel">
 *     <header class="rvds-panel__header">
 *       <div class="rvds-panel__heading">
 *         <h3 class="rvds-panel__title">Offene Aufträge</h3>
 *         <p class="rvds-panel__description">Letzte 30 Tage</p>
 *       </div>
 *       <div class="rvds-panel__actions">…buttons…</div>
 *     </header>
 *     <div class="rvds-panel__body">…</div>
 *     <footer class="rvds-panel__footer">…buttons…</footer>
 *   </section>
 *
 * Header and footer are optional. A body holding a Table or a List of rows
 * takes `rvds-panel__body--flush`: they bring their own rhythm.
 * ------------------------------------------------------------------------ */

.rvds-panel {
  /* A size container, so its parts can take the padding step of its own
     width (0.17.0). Give it a width - a grid cell, a block - never a flex
     row that sizes by content. */
  container: rvds-panel / inline-size;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  min-width: 0;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  --rvds-fill: var(--surface-card);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
}
.rvds-panel__header {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
  padding: var(--card-padding);
  flex: none;
}
.rvds-panel__heading { flex: 1; min-width: 0; }
.rvds-panel__title { margin: 0; font-size: var(--text-15); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-tight); color: var(--fg); }
.rvds-panel__description { margin: 3px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--mut-2); }
.rvds-panel__actions { display: flex; align-items: center; gap: 8px; flex: none; }
.rvds-panel__body { flex: 1; min-width: 0; padding: var(--card-padding); }
.rvds-panel__body--flush { padding: 0; --rvds-list-inset: var(--card-padding); }
@container rvds-panel (width >= 480px) {
  .rvds-panel > * { --card-padding: var(--card-padding-m); --flow-gap: var(--flow-gap-m); }
}
@container rvds-panel (width >= 800px) {
  .rvds-panel > * { --card-padding: var(--card-padding-l); }
  .rvds-panel .rvds-table__head,
  .rvds-panel .rvds-table__cell { padding-inline: var(--card-cell-x-l); }
}
/* A flush body reaches the card's edge, so it clips at the card's curve: the
   last row's line and a row's hover fill never run into the corner (0.17.0).
   Only the flush part clips, never the card - a tooltip from the header may
   still leave it. */
.rvds-panel > .rvds-panel__body--flush:first-child,
.rvds-panel > .rvds-panel__body--flush:last-child { overflow: clip; }
.rvds-panel > .rvds-panel__body--flush:first-child { border-top-left-radius: calc(var(--radius-lg) - 1px); border-top-right-radius: calc(var(--radius-lg) - 1px); }
.rvds-panel > .rvds-panel__body--flush:last-child { border-bottom-left-radius: calc(var(--radius-lg) - 1px); border-bottom-right-radius: calc(var(--radius-lg) - 1px); }
.rvds-panel__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  border-top: 1px solid var(--line);
  padding: var(--card-padding);
  flex: none;
}

/* ---------------------------------------------------------------------------
 * StatusBadge  (components/app/StatusBadge.jsx)
 *
 *   <span class="rvds-status-badge rvds-status-badge--success"><span class="rvds-status-badge__dot" aria-hidden="true"></span>Abgeschlossen</span>
 *
 * Record state with a leading dot. No modifier is neutral; --success,
 * --danger, --info and --gold are the others. Danger and info keep a white
 * label: their hue fails small text, so dot and frame carry the state.
 * `--wrap` (0.8.0) lets a longer state - a delivery sentence in a cart
 * line - break onto a second line instead of running on.
 * ------------------------------------------------------------------------ */

.rvds-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: var(--fill-1);
  padding: 2px 9px;
  font-size: var(--text-105);
  font-weight: var(--weight-medium);
  color: var(--mut);
}
.rvds-status-badge__dot { height: 5px; width: 5px; flex: none; border-radius: var(--radius-pill); background: var(--mut-2); }
.rvds-status-badge--success { border-color: var(--success-line); background: var(--success-surface); color: var(--success-text); }
.rvds-status-badge--success > .rvds-status-badge__dot { background: var(--success); }
.rvds-status-badge--danger { border-color: var(--danger-line); background: var(--danger-surface); color: var(--fg); }
.rvds-status-badge--danger > .rvds-status-badge__dot { background: var(--danger); }
.rvds-status-badge--info { border-color: var(--info-line); background: var(--info-surface); color: var(--fg); }
.rvds-status-badge--info > .rvds-status-badge__dot { background: var(--info); }
.rvds-status-badge--gold { border-color: var(--gold-a22); background: var(--gold-a10); color: var(--gold); }
.rvds-status-badge--gold > .rvds-status-badge__dot { background: var(--gold); }
.rvds-status-badge--wrap {
  align-items: baseline;
  box-sizing: border-box;
  max-width: 100%;
  white-space: normal;
  line-height: 1.45;
}
.rvds-status-badge--wrap > .rvds-status-badge__dot { align-self: center; }

/* ---------------------------------------------------------------------------
 * DescriptionList  (components/app/DescriptionList.jsx)
 *
 *   <dl class="rvds-description-list">
 *     <div class="rvds-description-list__row">
 *       <dt class="rvds-description-list__label">Modell</dt>
 *       <dd class="rvds-description-list__value rvds-description-list__value--strong">Model S 85</dd>
 *     </div>
 *     <div class="rvds-description-list__row">
 *       <dt class="rvds-description-list__label">Laufleistung</dt>
 *       <dd class="rvds-description-list__value rvds-description-list__value--numeric">184'320 km</dd>
 *     </div>
 *   </dl>
 *
 * Label left, value right. Two columns of pairs where there is room for
 * 240px each, one otherwise; `--single` forces one, `--plain` drops the
 * hairlines. A value is `--strong` for the fields that identify the record
 * and `--numeric` for figures.
 * ------------------------------------------------------------------------ */

.rvds-description-list {
  display: grid;
  gap: 0;
  margin: 0;
  min-width: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  column-gap: 32px;
}
.rvds-description-list--single { grid-template-columns: minmax(0, 1fr); }
.rvds-description-list__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 9px 0;
  min-width: 0;
  border-bottom: 1px solid var(--line);
}
.rvds-description-list__row:last-child,
.rvds-description-list--plain > .rvds-description-list__row { border-bottom: none; }
/* 0.8.0: 14px semibold in --mut instead of 12.5px in --mut-2 - the label
   is read, and the old one was hard to read. */
.rvds-description-list__label { flex: none; font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--mut); }
.rvds-description-list__value {
  margin: 0;
  text-align: right;
  min-width: 0;
  font-size: var(--text-13);
  font-weight: var(--weight-regular);
  color: var(--mut);
}
.rvds-description-list__value--strong { font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-description-list__value--numeric { font-variant-numeric: tabular-nums; }

/* Label and value stay together (0.15.0). From 640px of its own width a
   record's fields become a grid of cells, the label over its value - not a
   label at the left edge and its value 900px away at the right. Below that
   the pairs keep label left, value right: a row narrower than 480px may run
   edge to edge, the gap stays short. The summary and `--single` keep their
   own form at every width. Measured on a wrapper, because a grid cannot
   query its own width: `rvds-description-list-frame` around the <dl>, which
   DescriptionList.jsx always renders. Without the frame the list keeps the
   pairs at every width. */
.rvds-description-list-frame { container: rvds-dl / inline-size; min-width: 0; }
@container rvds-dl (width >= 640px) {
  .rvds-description-list:not(.rvds-description-list--summary):not(.rvds-description-list--single) {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
    gap: 16px 24px;
  }
  .rvds-description-list:not(.rvds-description-list--summary):not(.rvds-description-list--single) > .rvds-description-list__row {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 3px;
    padding: 0;
    border-bottom: none;
  }
  .rvds-description-list:not(.rvds-description-list--summary):not(.rvds-description-list--single) .rvds-description-list__label {
    font-size: var(--text-12);
    font-weight: var(--weight-medium);
    color: var(--mut-2);
  }
  .rvds-description-list:not(.rvds-description-list--summary):not(.rvds-description-list--single) .rvds-description-list__value {
    text-align: left;
    font-size: var(--text-14);
    color: var(--fg);
  }
}

/* ---------------------------------------------------------------------------
 * PageHeader  (components/app/PageHeader.jsx)
 *
 *   <header class="rvds-page-header">
 *     <nav class="rvds-breadcrumb" aria-label="Brotkrumen">…</nav>
 *     <div class="rvds-page-header__main">
 *       <div class="rvds-page-header__heading">
 *         <div class="rvds-page-header__title-row">
 *           <h1 class="rvds-page-header__title">Auftrag A-1042</h1>
 *           <span class="rvds-status-badge rvds-status-badge--gold">…</span>
 *         </div>
 *         <p class="rvds-page-header__description">…</p>
 *       </div>
 *       <div class="rvds-page-header__actions">…buttons…</div>
 *     </div>
 *   </header>
 *
 * One per app view. With a Tabs strip as its last child it takes `--tabs` and
 * hands its bottom spacing to the strip.
 * ------------------------------------------------------------------------ */

.rvds-page-header { display: flex; flex-direction: column; gap: 14px; border-bottom: 1px solid var(--line); padding-bottom: 18px; }
.rvds-page-header--tabs { padding-bottom: 0; }
.rvds-page-header__main { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.rvds-page-header__heading { flex: 1; min-width: min(100%, 220px); }
.rvds-page-header__title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rvds-page-header__title { margin: 0; font-size: var(--text-24); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-title); color: var(--fg); }
.rvds-page-header__description { margin: 6px 0 0; max-width: var(--measure-lead); font-size: var(--text-14); line-height: var(--leading-relaxed); color: var(--mut); }
.rvds-page-header__actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
/* The primary action is the last of the group (0.15.0). Below 640px it leaves
   the header for a StickyBar at the bottom, in reach of the thumb:
   PageHeader.jsx does that with `primaryAction`; plain HTML writes it twice,
   in `rvds-responsive--from-sm` inside the actions and in a StickyBar inside
   `rvds-responsive--until-sm`. */

/* ===========================================================================
 * data-display  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * List  (components/data-display/List.jsx)
 *
 *   <section class="rvds-list rvds-list--card">
 *     <div class="rvds-list__head">
 *       <h3 class="rvds-list__caption">Letzte Aufträge</h3>
 *       <span class="rvds-list__action"><a href="/auftraege">Alle anzeigen</a></span>
 *     </div>
 *     <div class="rvds-list__body">
 *       …row…
 *       <span class="rvds-list__divider" aria-hidden="true"></span>
 *       …row…
 *     </div>
 *     <p class="rvds-list__footnote">…</p>
 *   </section>
 *
 * The container owns the frame and the dividers; rows own only their
 * content. No modifier is plain (inside a Panel), `--card` a standalone
 * block, `--inset` the phone's settings group. A divider sits between two
 * rows - in a <ul> body it is an <li class="rvds-list__divider"
 * role="presentation" aria-hidden="true">. The frame sets the inset, rows and
 * dividers take it (0.16.0): 16px in a framed list, --card-padding in a
 * flush Panel body, so text and dividers stand on the edge of the panel's
 * head; 0 in a plain list with no frame around it. --rvds-list-inset on the
 * body sets another inset. Every use reads it with a fallback of 0.
 * Without caption, action and footnote the root is a <div> and holds only
 * the body.
 * ------------------------------------------------------------------------ */

.rvds-list { min-width: 0; }
.rvds-list__body { display: flex; flex-direction: column; min-width: 0; }
.rvds-list--card > .rvds-list__body,
.rvds-list--inset > .rvds-list__body {
  --rvds-list-inset: 16px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  --rvds-fill: var(--surface-card);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
}
.rvds-list--card > .rvds-list__body { -webkit-backdrop-filter: blur(var(--blur-surface)); backdrop-filter: blur(var(--blur-surface)); }
.rvds-list__divider { display: block; height: 1px; margin-left: var(--rvds-list-inset, 0px); background: var(--line); }
.rvds-list__head { display: flex; align-items: baseline; gap: 12px; margin: 0 0 7px; padding-inline: 0; }
.rvds-list__caption {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 12px;
  font-weight: var(--weight-bold);
  letter-spacing: 0.04em;
  color: var(--text-label);
}
.rvds-list__action { flex: none; }
.rvds-list__footnote { margin: 7px 0 0; padding-inline: 0; font-size: 11.5px; line-height: 1.5; color: var(--dim); }
.rvds-list--card > .rvds-list__head,
.rvds-list--inset > .rvds-list__head,
.rvds-list--card > .rvds-list__footnote,
.rvds-list--inset > .rvds-list__footnote { padding-inline: 4px; }

/* ---------------------------------------------------------------------------
 * ListRow  (components/data-display/ListRow.jsx)
 *
 *   <div class="rvds-list-row rvds-list-row--interactive" role="button" tabindex="0">
 *     <span class="rvds-list-row__media"><span class="rvds-icon-tile">…</span></span>
 *     <span class="rvds-list-row__main">
 *       <span class="rvds-list-row__title">Batteriemodul 85 kWh</span>
 *       <span class="rvds-list-row__meta"><span class="rvds-list-row__subtitle">Instandgesetzt · 12 Monate Garantie</span></span>
 *     </span>
 *     <span class="rvds-list-row__aside"><b class="rvds-list-row__value">3'900 CHF</b></span>
 *     <span class="rvds-list-row__chevron"><svg …chevron-right, 16px, stroke 2…></svg></span>
 *   </div>
 *
 * One row: media, stacked text, value, chevron. Text truncates so rows stay
 * one height. A row that opens something is `--interactive` - an <a>, or a
 * <div role="button" tabindex="0">, which components.js answers on Enter and
 * Space as React does. The open row of a master-detail list carries
 * aria-current="true". A badge goes into the meta line before the subtitle,
 * and the meta line then takes `__meta--badge`. Inside a List body the row
 * drops its own bottom hairline; standalone it keeps it (`--undivided` drops
 * it anywhere).
 *
 * The cart line (0.8.0): `--wrap` lets title and subtitle wrap instead
 * of truncating, `__thumb` frames a product image at 64px (56px on a phone),
 * and a row with a quantity takes `--controls`:
 *
 *   <li class="rvds-list-row rvds-list-row--wrap rvds-list-row--controls">
 *     <span class="rvds-list-row__media"><a class="rvds-list-row__thumb" href="…" tabindex="-1" aria-hidden="true"><img src="…" alt=""></a></span>
 *     <span class="rvds-list-row__main">
 *       <a class="rvds-list-row__title" href="…">M14 Gewindeplatte (Nutplate) für Tesla Hochspannungsbatterie, Model S & X, 2 Stück</a>
 *       <span class="rvds-list-row__meta rvds-list-row__meta--badge">
 *         <span class="rvds-list-row__badge"><span class="rvds-status-badge rvds-status-badge--info rvds-status-badge--wrap">…</span></span>
 *         <span class="rvds-list-row__subtitle">48 CHF</span>
 *       </span>
 *     </span>
 *     <span class="rvds-list-row__side">
 *       <span class="rvds-list-row__control"><div class="rvds-stepper">…</div></span>
 *       <span class="rvds-list-row__aside"><b class="rvds-list-row__value">96 CHF</b></span>
 *     </span>
 *     <span class="rvds-list-row__action"><button class="rvds-icon-button rvds-icon-button--danger" aria-label="Entfernen">…</button></span>
 *   </li>
 *
 * Below sm, a `--controls` row moves the side (quantity and amount) to a
 * second line under the text; the action stays top right.
 * ------------------------------------------------------------------------ */

.rvds-list-row {
  display: flex;
  align-items: center;
  gap: 12px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--line);
  /* The inset comes from the frame (0.16.0): a flush Panel body or a framed
     list; the hover fill still runs to the edge. */
  padding: 13px max(2px, var(--rvds-list-inset, 0px));
  background: transparent;
  box-shadow: none;
  outline: none;
  border-radius: 0;
  cursor: default;
  color: inherit;
  text-decoration: none;
  transition: background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-list-row--undivided,
.rvds-list__body > .rvds-list-row { border-bottom: none; }
.rvds-list-row--interactive { cursor: pointer; }
.rvds-list-row--interactive:hover { background: var(--fill-1); color: inherit; }
.rvds-list-row[aria-current="true"] {
  background: var(--state-surface);
  box-shadow: inset 0 0 0 1px var(--state-border);
  border-radius: var(--radius-md);
}
/* Keyboard focus is the system's glow, not the browser ring. */
.rvds-list-row:focus-visible { box-shadow: var(--focus-glow); border-radius: var(--radius-md); }
.rvds-list-row[aria-current="true"]:focus-visible { box-shadow: inset 0 0 0 1px var(--state-border), var(--focus-glow); }

.rvds-list-row__media { flex: none; display: inline-flex; }
.rvds-list-row__main { flex: 1; min-width: 0; }
.rvds-list-row__title {
  display: block;
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rvds-list-row__meta { display: flex; align-items: center; gap: 8px; min-width: 0; margin-top: 2px; }
.rvds-list-row__meta--badge { margin-top: 4px; }
.rvds-list-row__badge { flex: none; display: inline-flex; }
.rvds-list-row__subtitle { min-width: 0; font-size: 12.5px; color: var(--mut-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rvds-list-row__aside { flex: none; text-align: right; }
.rvds-list-row__value { display: block; font-size: var(--text-14); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; color: var(--fg); }
.rvds-list-row__note { display: block; margin-top: 2px; font-size: 11.5px; color: var(--dim); }
.rvds-list-row__chevron { flex: none; color: var(--dim); display: inline-flex; }

/* Cart line. */
.rvds-list-row--wrap .rvds-list-row__title,
.rvds-list-row--wrap .rvds-list-row__subtitle { white-space: normal; overflow-wrap: anywhere; }
.rvds-list-row--wrap .rvds-list-row__title { line-height: var(--leading-snug); }
.rvds-list-row--wrap .rvds-list-row__meta { flex-wrap: wrap; }
.rvds-list-row--wrap .rvds-list-row__badge { flex: 0 1 auto; min-width: 0; max-width: 100%; }
a.rvds-list-row__title:hover { color: var(--gold); }
.rvds-list-row__thumb {
  display: block;
  box-sizing: border-box;
  width: 64px;
  height: 64px;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
}
.rvds-list-row__thumb > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.rvds-list-row__side { flex: none; display: flex; align-items: center; gap: var(--space-7); }
.rvds-list-row__control { flex: none; display: inline-flex; }
.rvds-list-row__action { flex: none; display: inline-flex; }
/* A cart line keeps the card padding, so the image lines up with the card
   edge - or the frame's inset where that is wider. */
.rvds-list-row--controls { padding: var(--card-padding) max(var(--card-padding), var(--rvds-list-inset, 0px)); }
.rvds-list-row--controls > .rvds-list-row__side > .rvds-list-row__aside { min-width: 88px; }
@media (max-width: 640px) {
  .rvds-list-row--controls {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "media main action" ". side side";
    align-items: start;
    row-gap: var(--space-5);
  }
  .rvds-list-row--controls > .rvds-list-row__media { grid-area: media; }
  .rvds-list-row--controls > .rvds-list-row__main { grid-area: main; }
  .rvds-list-row--controls > .rvds-list-row__side { grid-area: side; justify-content: space-between; }
  .rvds-list-row--controls > .rvds-list-row__action { grid-area: action; }
  .rvds-list-row--controls > .rvds-list-row__side > .rvds-list-row__aside { min-width: 0; }
  .rvds-list-row__thumb { width: 56px; height: 56px; }
}

/* ---------------------------------------------------------------------------
 * CheckList  (components/data-display/CheckList.jsx)
 *
 *   <ul class="rvds-check-list">
 *     <li class="rvds-check-list__item">
 *       <span class="rvds-check-list__mark"><svg …check, 13px, stroke 2.6…></svg></span>
 *       <span class="rvds-check-list__text"><b class="rvds-check-list__label">Fahrzeugcheck:</b> <span class="rvds-check-list__copy">Karosserie, Fahrwerk und Hochspannungssystem.</span></span>
 *     </li>
 *   </ul>
 *
 * No modifier is the plain green tick; `--tile` puts the tick (11px) in a
 * neutral square, `--md` is the larger text. The tick is the only colour.
 * ------------------------------------------------------------------------ */

.rvds-check-list { display: flex; flex-direction: column; gap: 7px; margin: 0; padding: 0; list-style: none; }
.rvds-check-list--tile { gap: 8px; }
.rvds-check-list__item { display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; line-height: var(--leading-snug); }
.rvds-check-list--tile > .rvds-check-list__item { gap: 10px; }
.rvds-check-list--md > .rvds-check-list__item { font-size: var(--text-14); }
.rvds-check-list__mark { margin-top: 2px; flex: none; display: inline-flex; color: var(--success); }
.rvds-check-list--tile > .rvds-check-list__item > .rvds-check-list__mark {
  margin-top: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 18px;
  width: 18px;
  box-sizing: border-box;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-2);
  background: var(--fill-2);
}
.rvds-check-list__text { min-width: 0; }
.rvds-check-list__label { font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-check-list__copy { color: var(--mut); }

/* ---------------------------------------------------------------------------
 * Tag  (components/data-display/Tag.jsx)
 *
 *   <span class="rvds-tag">Batteriediagnose</span>
 *   <span class="rvds-tag rvds-tag--caption">Batterie Remanufaktur</span>
 *   <span class="rvds-tag rvds-tag--teal">Motor</span>
 *
 * No modifier is neutral. --gold (one per view), --caption (on photography),
 * the states --success, --danger, --info, and the four categorical tones
 * --lime, --teal, --vio, --mag. As wide as its label, in any container.
 * ------------------------------------------------------------------------ */

.rvds-tag {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  max-width: 100%;
  flex: none;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: var(--fill-1);
  color: var(--dim);
  padding: 2px 8px;
  font-family: var(--font-sans);
  font-size: var(--text-105);
  font-weight: var(--weight-regular);
  letter-spacing: normal;
}
.rvds-tag--gold { border-color: var(--gold-a30); background: var(--gold-a08); color: var(--gold); }
.rvds-tag--caption {
  border-radius: var(--radius-md);
  border-color: var(--caption-line);
  background: var(--caption-bg);
  color: var(--caption-fg);
  padding: 4px 9px;
  font-size: 10px;
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tag);
  -webkit-backdrop-filter: var(--caption-backdrop);
  backdrop-filter: var(--caption-backdrop);
}
.rvds-tag--success { border-color: var(--success-line); background: var(--success-surface); color: var(--success-text); }
.rvds-tag--danger { border-color: var(--danger-line); background: var(--danger-surface); color: var(--danger-text); }
.rvds-tag--info { border-color: var(--info-line); background: var(--info-surface); color: var(--info-text); }
.rvds-tag--lime { border-color: rgb(from var(--hue-45) r g b / 0.30); background: rgb(from var(--hue-45) r g b / 0.10); color: var(--hue-45-text); }
.rvds-tag--teal { border-color: rgb(from var(--hue-135) r g b / 0.30); background: rgb(from var(--hue-135) r g b / 0.10); color: var(--hue-135-text); }
.rvds-tag--vio { border-color: rgb(from var(--hue-225) r g b / 0.30); background: rgb(from var(--hue-225) r g b / 0.10); color: var(--hue-225-text); }
.rvds-tag--mag { border-color: rgb(from var(--hue-270) r g b / 0.30); background: rgb(from var(--hue-270) r g b / 0.10); color: var(--hue-270-text); }

/* ---------------------------------------------------------------------------
 * Rating  (components/data-display/Rating.jsx)
 *
 *   <span class="rvds-rating">
 *     <span class="rvds-rating__stars" role="img" aria-label="5 von 5 Sternen">
 *       <span class="rvds-rating__star"><svg …star, 14px, stroke-width 0…></svg></span>
 *       …
 *       <span class="rvds-rating__star rvds-rating__star--off">…</span>
 *     </span>
 *     <span class="rvds-rating__count"><b>120+</b> Bewertungen</span>
 *   </span>
 *
 * Stars are Lucide `star`, filled here with their colour: gold to the
 * rating, a hairline for the rest (`--off`). Only the stars are the image;
 * the count stays text a screen reader reads (0.9.0).
 * ------------------------------------------------------------------------ */

.rvds-rating { display: inline-flex; align-items: center; gap: 5px; }
.rvds-rating__stars { display: inline-flex; gap: 2px; }
.rvds-rating__star { display: inline-flex; color: var(--gold); }
.rvds-rating__star--off { color: var(--line-2); }
.rvds-rating__star > svg { fill: currentColor; }
.rvds-rating__count { font-size: var(--text-13); color: var(--mut-2); }
.rvds-rating__count > b { font-weight: var(--weight-semibold); color: var(--fg); }

/* ---------------------------------------------------------------------------
 * Table  (components/data-display/Table.jsx)
 *
 *   <div class="rvds-table rvds-table--stack-md">
 *     <table class="rvds-table__table">
 *       <thead><tr>
 *         <th class="rvds-table__head">Auftrag</th><th class="rvds-table__head">Fahrzeug</th>
 *         <th class="rvds-table__head rvds-table__head--right">Total</th>
 *       </tr></thead>
 *       <tbody>
 *         <tr class="rvds-table__row rvds-table__row--priority">
 *           <td class="rvds-table__cell rvds-table__cell--strong rvds-table__cell--lead" data-label="Auftrag">
 *             <span class="rvds-table__lead"><span>A-1042</span><span class="rvds-tag rvds-tag--gold">Priorität</span></span>
 *           </td>
 *           <td class="rvds-table__cell" data-label="Fahrzeug">Model S 85</td>
 *           <td class="rvds-table__cell rvds-table__cell--right" data-label="Total">3'900 CHF</td>
 *         </tr>
 *       </tbody>
 *     </table>
 *   </div>
 *
 * Hairline rows, no vertical rules, no zebra. A cell is `--strong` for the
 * identifying column, `--right` for figures (tabular), `--wrap` to let it
 * wrap. The one row that matters takes `--priority`; a clickable row
 * `--interactive`. `--flush` lines the outer cells up with a card edge.
 *
 * The phone form needs no script: with `--stack-sm`, `--stack-md` (the React
 * default), `--stack-lg` or `--stack-xl`, below that width every row becomes
 * a stacked block - the `--lead` cell as its heading, every other cell a
 * label/value pair, its label taken from data-label. Give a cell one element
 * or plain text, and mark filler columns data-hide-stacked. React swaps to
 * its own stacked markup (the `__stack-*` classes below) instead; the two
 * look the same.
 * ------------------------------------------------------------------------ */

.rvds-table { width: 100%; overflow-x: auto; }
.rvds-table__table { width: 100%; min-width: max-content; border-collapse: collapse; font-size: var(--text-13); }
.rvds-table__head {
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--line-2);
  padding: 0 14px;
  height: var(--control-height-sm);
  font-size: 11.5px;
  font-weight: var(--weight-bold);
  letter-spacing: 0.04em;
  color: var(--text-label);
}
.rvds-table__row { background: transparent; cursor: default; transition: background var(--duration-base), --rvds-fill var(--duration-base); }
.rvds-table__row--priority { background: var(--gold-a05); }
.rvds-table__row--interactive { cursor: pointer; }
.rvds-table__row--interactive:hover { background: var(--fill-2); }
.rvds-table__cell {
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--line);
  padding: 11px 14px;
  color: var(--mut);
  font-weight: var(--weight-regular);
  font-variant-numeric: normal;
}
.rvds-table__head--right,
.rvds-table__cell--right { text-align: right; }
.rvds-table__head--center,
.rvds-table__cell--center { text-align: center; }
.rvds-table__cell--right { font-variant-numeric: tabular-nums; }
.rvds-table__cell--wrap { white-space: normal; }
.rvds-table__cell--strong { color: var(--fg); font-weight: var(--weight-semibold); }
.rvds-table__row--priority > .rvds-table__cell:first-child { box-shadow: inset 3px 0 0 var(--gold); }
.rvds-table--flush .rvds-table__head:first-child,
.rvds-table--flush .rvds-table__cell:first-child { padding-inline-start: var(--card-padding); }
.rvds-table--flush .rvds-table__head:last-child,
.rvds-table--flush .rvds-table__cell:last-child { padding-inline-end: var(--card-padding); }
/* Text and a highlight in one wrapping element, so the tag never breaks onto
   a line of its own. */
.rvds-table__lead { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; }

/* The stacked form React renders below stackAt. */
.rvds-table--stacked { overflow-x: visible; }
.rvds-table__stack-row {
  display: flex;
  flex-direction: column;
  gap: 9px;
  box-sizing: border-box;
  border-bottom: 1px solid var(--line);
  padding: 13px 2px;
  background: transparent;
  cursor: default;
  transition: background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-table--flush .rvds-table__stack-row { padding: 13px var(--card-padding); }
.rvds-table__stack-row--priority { background: var(--gold-a05); box-shadow: inset 3px 0 0 var(--gold); }
.rvds-table__stack-row--interactive { cursor: pointer; }
.rvds-table__stack-row--interactive:hover { background: var(--fill-1); }
.rvds-table__stack-lead { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }
/* Label and value stay a pair: the label may wrap in its own track, the value
   breaks inside itself - neither pushes the other off the row. */
.rvds-table__stack-pairs { display: grid; grid-template-columns: minmax(88px, max-content) minmax(0, 1fr); gap: 5px 12px; margin: 0; }
.rvds-table__stack-label { font-size: 11.5px; color: var(--dim); line-height: 1.4; }
.rvds-table__stack-value {
  margin: 0;
  min-width: 0;
  text-align: right;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--mut);
  overflow-wrap: break-word;
  font-variant-numeric: normal;
}
.rvds-table__stack-value--numeric { font-variant-numeric: tabular-nums; }

/* The same stacked form from the table's own markup, for plain HTML. One
   rule set, repeated per width. */
@media (max-width: 639px) {
  .rvds-table--stack-sm { overflow-x: visible; }
  .rvds-table--stack-sm > .rvds-table__table,
  .rvds-table--stack-sm > .rvds-table__table > tbody { display: block; width: 100%; min-width: 0; }
  .rvds-table--stack-sm > .rvds-table__table > thead { display: none; }
  .rvds-table--stack-sm .rvds-table__row { display: grid; grid-template-columns: minmax(88px, max-content) minmax(0, 1fr); gap: 5px 12px; box-sizing: border-box; border-bottom: 1px solid var(--line); padding: 13px 2px; }
  .rvds-table--stack-sm.rvds-table--flush .rvds-table__row { padding: 13px var(--card-padding); }
  .rvds-table--stack-sm .rvds-table__row--priority { box-shadow: inset 3px 0 0 var(--gold); }
  .rvds-table--stack-sm .rvds-table__row--interactive:hover { background: var(--fill-1); }
  .rvds-table--stack-sm .rvds-table__row > .rvds-table__cell { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; justify-items: end; align-items: start; padding: 0; border: 0; box-shadow: none; white-space: normal; text-align: right; font-size: 12.5px; line-height: 1.45; color: var(--mut); font-weight: var(--weight-regular); overflow-wrap: break-word; min-width: 0; }
  .rvds-table--stack-sm .rvds-table__cell::before { content: attr(data-label); justify-self: start; text-align: left; font-size: 11.5px; line-height: 1.4; color: var(--dim); font-weight: var(--weight-regular); font-variant-numeric: normal; }
  .rvds-table--stack-sm .rvds-table__row > .rvds-table__cell--lead { display: block; order: -1; margin-bottom: 4px; text-align: left; font-size: var(--text-14); line-height: normal; font-weight: var(--weight-semibold); color: var(--fg); }
  .rvds-table--stack-sm .rvds-table__cell--lead::before { content: none; }
  .rvds-table--stack-sm .rvds-table__row > [data-hide-stacked] { display: none; }
}
@media (max-width: 899px) {
  .rvds-table--stack-md { overflow-x: visible; }
  .rvds-table--stack-md > .rvds-table__table,
  .rvds-table--stack-md > .rvds-table__table > tbody { display: block; width: 100%; min-width: 0; }
  .rvds-table--stack-md > .rvds-table__table > thead { display: none; }
  .rvds-table--stack-md .rvds-table__row { display: grid; grid-template-columns: minmax(88px, max-content) minmax(0, 1fr); gap: 5px 12px; box-sizing: border-box; border-bottom: 1px solid var(--line); padding: 13px 2px; }
  .rvds-table--stack-md.rvds-table--flush .rvds-table__row { padding: 13px var(--card-padding); }
  .rvds-table--stack-md .rvds-table__row--priority { box-shadow: inset 3px 0 0 var(--gold); }
  .rvds-table--stack-md .rvds-table__row--interactive:hover { background: var(--fill-1); }
  .rvds-table--stack-md .rvds-table__row > .rvds-table__cell { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; justify-items: end; align-items: start; padding: 0; border: 0; box-shadow: none; white-space: normal; text-align: right; font-size: 12.5px; line-height: 1.45; color: var(--mut); font-weight: var(--weight-regular); overflow-wrap: break-word; min-width: 0; }
  .rvds-table--stack-md .rvds-table__cell::before { content: attr(data-label); justify-self: start; text-align: left; font-size: 11.5px; line-height: 1.4; color: var(--dim); font-weight: var(--weight-regular); font-variant-numeric: normal; }
  .rvds-table--stack-md .rvds-table__row > .rvds-table__cell--lead { display: block; order: -1; margin-bottom: 4px; text-align: left; font-size: var(--text-14); line-height: normal; font-weight: var(--weight-semibold); color: var(--fg); }
  .rvds-table--stack-md .rvds-table__cell--lead::before { content: none; }
  .rvds-table--stack-md .rvds-table__row > [data-hide-stacked] { display: none; }
}
@media (max-width: 1199px) {
  .rvds-table--stack-lg { overflow-x: visible; }
  .rvds-table--stack-lg > .rvds-table__table,
  .rvds-table--stack-lg > .rvds-table__table > tbody { display: block; width: 100%; min-width: 0; }
  .rvds-table--stack-lg > .rvds-table__table > thead { display: none; }
  .rvds-table--stack-lg .rvds-table__row { display: grid; grid-template-columns: minmax(88px, max-content) minmax(0, 1fr); gap: 5px 12px; box-sizing: border-box; border-bottom: 1px solid var(--line); padding: 13px 2px; }
  .rvds-table--stack-lg.rvds-table--flush .rvds-table__row { padding: 13px var(--card-padding); }
  .rvds-table--stack-lg .rvds-table__row--priority { box-shadow: inset 3px 0 0 var(--gold); }
  .rvds-table--stack-lg .rvds-table__row--interactive:hover { background: var(--fill-1); }
  .rvds-table--stack-lg .rvds-table__row > .rvds-table__cell { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; justify-items: end; align-items: start; padding: 0; border: 0; box-shadow: none; white-space: normal; text-align: right; font-size: 12.5px; line-height: 1.45; color: var(--mut); font-weight: var(--weight-regular); overflow-wrap: break-word; min-width: 0; }
  .rvds-table--stack-lg .rvds-table__cell::before { content: attr(data-label); justify-self: start; text-align: left; font-size: 11.5px; line-height: 1.4; color: var(--dim); font-weight: var(--weight-regular); font-variant-numeric: normal; }
  .rvds-table--stack-lg .rvds-table__row > .rvds-table__cell--lead { display: block; order: -1; margin-bottom: 4px; text-align: left; font-size: var(--text-14); line-height: normal; font-weight: var(--weight-semibold); color: var(--fg); }
  .rvds-table--stack-lg .rvds-table__cell--lead::before { content: none; }
  .rvds-table--stack-lg .rvds-table__row > [data-hide-stacked] { display: none; }
}
@media (max-width: 1559px) {
  .rvds-table--stack-xl { overflow-x: visible; }
  .rvds-table--stack-xl > .rvds-table__table,
  .rvds-table--stack-xl > .rvds-table__table > tbody { display: block; width: 100%; min-width: 0; }
  .rvds-table--stack-xl > .rvds-table__table > thead { display: none; }
  .rvds-table--stack-xl .rvds-table__row { display: grid; grid-template-columns: minmax(88px, max-content) minmax(0, 1fr); gap: 5px 12px; box-sizing: border-box; border-bottom: 1px solid var(--line); padding: 13px 2px; }
  .rvds-table--stack-xl.rvds-table--flush .rvds-table__row { padding: 13px var(--card-padding); }
  .rvds-table--stack-xl .rvds-table__row--priority { box-shadow: inset 3px 0 0 var(--gold); }
  .rvds-table--stack-xl .rvds-table__row--interactive:hover { background: var(--fill-1); }
  .rvds-table--stack-xl .rvds-table__row > .rvds-table__cell { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; justify-items: end; align-items: start; padding: 0; border: 0; box-shadow: none; white-space: normal; text-align: right; font-size: 12.5px; line-height: 1.45; color: var(--mut); font-weight: var(--weight-regular); overflow-wrap: break-word; min-width: 0; }
  .rvds-table--stack-xl .rvds-table__cell::before { content: attr(data-label); justify-self: start; text-align: left; font-size: 11.5px; line-height: 1.4; color: var(--dim); font-weight: var(--weight-regular); font-variant-numeric: normal; }
  .rvds-table--stack-xl .rvds-table__row > .rvds-table__cell--lead { display: block; order: -1; margin-bottom: 4px; text-align: left; font-size: var(--text-14); line-height: normal; font-weight: var(--weight-semibold); color: var(--fg); }
  .rvds-table--stack-xl .rvds-table__cell--lead::before { content: none; }
  .rvds-table--stack-xl .rvds-table__row > [data-hide-stacked] { display: none; }
}

/* Stacks when it no longer fits (0.17.0): a table with `rvds-table--fit`
   measures itself in components.js and takes `rvds-table--fit-stacked` while
   it is wider than its box - the same stacked form as the breakpoint classes
   above, decided by the content instead of the screen. */
.rvds-table--fit-stacked { overflow-x: visible; }
.rvds-table--fit-stacked > .rvds-table__table,
.rvds-table--fit-stacked > .rvds-table__table > tbody { display: block; width: 100%; min-width: 0; }
.rvds-table--fit-stacked > .rvds-table__table > thead { display: none; }
.rvds-table--fit-stacked .rvds-table__row { display: grid; grid-template-columns: minmax(88px, max-content) minmax(0, 1fr); gap: 5px 12px; box-sizing: border-box; border-bottom: 1px solid var(--line); padding: 13px 2px; }
.rvds-table--fit-stacked.rvds-table--flush .rvds-table__row { padding: 13px var(--card-padding); }
.rvds-table--fit-stacked .rvds-table__row--priority { box-shadow: inset 3px 0 0 var(--gold); }
.rvds-table--fit-stacked .rvds-table__row--interactive:hover { background: var(--fill-1); }
.rvds-table--fit-stacked .rvds-table__row > .rvds-table__cell { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; justify-items: end; align-items: start; padding: 0; border: 0; box-shadow: none; white-space: normal; text-align: right; font-size: 12.5px; line-height: 1.45; color: var(--mut); font-weight: var(--weight-regular); overflow-wrap: break-word; min-width: 0; }
.rvds-table--fit-stacked .rvds-table__cell::before { content: attr(data-label); justify-self: start; text-align: left; font-size: 11.5px; line-height: 1.4; color: var(--dim); font-weight: var(--weight-regular); font-variant-numeric: normal; }
.rvds-table--fit-stacked .rvds-table__row > .rvds-table__cell--lead { display: block; order: -1; margin-bottom: 4px; text-align: left; font-size: var(--text-14); line-height: normal; font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-table--fit-stacked .rvds-table__cell--lead::before { content: none; }
.rvds-table--fit-stacked .rvds-table__row > [data-hide-stacked] { display: none; }

/* ---------------------------------------------------------------------------
 * StatTile  (components/data-display/StatTile.jsx)
 *
 *   <div class="rvds-stat-tile"><div class="rvds-stat-tile__value">-62%</div><div class="rvds-stat-tile__label">gegenüber Austausch</div></div>
 *
 * A gold showcase figure. Never for a change - that is a KpiCard with a Delta.
 * ------------------------------------------------------------------------ */

.rvds-stat-tile {
  flex: 1;
  min-width: 120px;
  box-sizing: border-box;
  text-align: center;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--fill-2);
  padding: 12px 16px;
}
.rvds-stat-tile__value { font-size: 20px; font-weight: var(--weight-semibold); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--gold); }
.rvds-stat-tile__label { margin-top: 2px; font-size: 11.5px; color: var(--mut-2); }

/* ===========================================================================
 * mobile  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * StickyBar  (components/mobile/StickyBar.jsx)
 *
 *   <div class="rvds-sticky-bar">
 *     <div class="rvds-sticky-bar__info">
 *       <span class="rvds-sticky-bar__label">Ab Preis</span>
 *       <b class="rvds-sticky-bar__value">3'900 CHF</b>
 *       <span class="rvds-sticky-bar__note">nach Diagnose verbindlich</span>
 *     </div>
 *     <div class="rvds-sticky-bar__actions"><a class="rvds-button rvds-button--solid" href="/anfragen">Jetzt anfragen</a></div>
 *   </div>
 *
 * Always glass. No modifier is fixed to the bottom of the viewport (a phone);
 * `--column` floats sticky at the foot of its own column on a desktop,
 * rounded with a hairline all round; `--inline` stays in the flow. The page
 * under a fixed bar takes --sticky-bar-height of bottom padding.
 * ------------------------------------------------------------------------ */

.rvds-sticky-bar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 290;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  box-sizing: border-box;
  min-height: var(--sticky-bar-height);
  border-top: 1px solid var(--glass-line);
  background: var(--glass-bg);
  box-shadow: var(--glass-highlight);
  -webkit-backdrop-filter: blur(var(--blur-surface)) saturate(var(--saturate-nav));
  backdrop-filter: blur(var(--blur-surface)) saturate(var(--saturate-nav));
  padding: 12px 18px;
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
}
.rvds-sticky-bar--inline { position: relative; }
.rvds-sticky-bar--column {
  position: sticky;
  inset-inline: auto;
  bottom: var(--space-5);
  z-index: 20;
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--glass-highlight), var(--shadow-menu);
  padding-bottom: 12px;
}
.rvds-sticky-bar__info { min-width: 0; }
.rvds-sticky-bar__label { display: block; font-size: 11.5px; color: var(--dim); }
.rvds-sticky-bar__value { display: block; font-size: var(--text-17); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; color: var(--fg); }
.rvds-sticky-bar__note { display: block; font-size: 11px; color: var(--mut-2); }
.rvds-sticky-bar__actions { display: flex; align-items: center; gap: 10px; flex: none; }

/* ---------------------------------------------------------------------------
 * TabBar  (components/mobile/TabBar.jsx, selection in components.js)
 *
 *   <nav class="rvds-tab-bar">
 *     <a class="rvds-tab-bar__item" href="/" aria-current="page">
 *       <span class="rvds-tab-bar__icon"><svg …house, 20px…></svg></span>
 *       <span class="rvds-tab-bar__label">Start</span>
 *     </a>
 *     <a class="rvds-tab-bar__item" href="/shop">
 *       <span class="rvds-tab-bar__badge">2</span>
 *       <span class="rvds-tab-bar__icon"><svg …shopping-bag, 20px…></svg></span>
 *       <span class="rvds-tab-bar__label">Shop</span>
 *     </a>
 *   </nav>
 *
 * Three to five destinations, fixed to the bottom edge; `--inline` stays in
 * the flow. aria-current="page" marks the active one: the label turns white,
 * the glyph gold and heavier (the stroke is set here, 1.8 and 2.2). Items are
 * links on a real page; as buttons, components.js moves aria-current on a
 * click and fires `rvds:change` on the bar, as TabBar.jsx does.
 * ------------------------------------------------------------------------ */

.rvds-tab-bar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 300;
  display: flex;
  box-sizing: border-box;
  border-top: 1px solid var(--line);
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(var(--blur-nav)) saturate(var(--saturate-nav));
  backdrop-filter: blur(var(--blur-nav)) saturate(var(--saturate-nav));
  padding-bottom: env(safe-area-inset-bottom);
}
.rvds-tab-bar--inline { position: relative; }
.rvds-tab-bar__item {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  height: var(--tabbar-height);
  border: none;
  background: none;
  cursor: pointer;
  color: var(--mut-2);
  text-decoration: none;
  transition: color var(--duration-base);
}
.rvds-tab-bar__item:hover { color: var(--mut-2); }
.rvds-tab-bar__item[aria-current="page"],
.rvds-tab-bar__item[aria-current="page"]:hover { color: var(--state-ink); }
.rvds-tab-bar__badge {
  position: absolute;
  margin-top: -18px;
  margin-left: 18px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  box-sizing: border-box;
  border-radius: var(--radius-pill);
  background: var(--gold);
  color: var(--on-accent);
  font-size: 10px;
  font-weight: var(--weight-bold);
  line-height: 16px;
  text-align: center;
}
.rvds-tab-bar__icon { color: inherit; }
.rvds-tab-bar__icon > svg { stroke-width: 1.8; }
.rvds-tab-bar__item[aria-current="page"] > .rvds-tab-bar__icon { color: var(--gold); }
.rvds-tab-bar__item[aria-current="page"] > .rvds-tab-bar__icon > svg { stroke-width: 2.2; }
.rvds-tab-bar__label {
  font-size: 10.5px;
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.rvds-tab-bar__item[aria-current="page"] > .rvds-tab-bar__label { font-weight: var(--weight-semibold); }

/* ---------------------------------------------------------------------------
 * Overlay  (components/overlays/Overlay.jsx, behaviour in components.js)
 *
 *   <div class="rvds-overlay rvds-overlay--bottom" id="termin">
 *     <div class="rvds-overlay__backdrop"></div>
 *     …the panel: a BottomSheet…
 *   </div>
 *   <a href="#termin" data-rvds-open="termin">Termin wählen</a>
 *
 * The frame and the one backdrop every overlay shares. Hidden until it is
 * `--open`; `--bottom`, `--right` and no modifier (centred, 24px air) place
 * the panel. `--contained` pins it to a positioned ancestor instead of the
 * viewport (a phone frame in a specimen). Only its look and the plain-HTML
 * behaviour moved in this release; Modal and Drawer still style their panels
 * inline.
 *
 * In plain HTML components.js opens it from any element with
 * data-rvds-open="<id>" and closes it from data-rvds-close inside it, from
 * Escape and from a press on the backdrop, freezes the page underneath,
 * keeps focus inside and returns it on close - as Overlay.jsx does.
 * RevoltarisDS.open(el) and .close(el) do the same from a script. Without
 * the script, a link to #<id> still opens it through :target and a link to
 * "#" closes it, so the content stays reachable.
 * ------------------------------------------------------------------------ */

.rvds-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
/* Shown while --open. Without the script a link to #id opens it through
   :target; once components.js has wired it (--wired, never in markup) only
   --open counts, so closing it is not undone by a hash left in the URL. */
.rvds-overlay:not(.rvds-overlay--open):not(:target),
.rvds-overlay--wired:not(.rvds-overlay--open) { display: none; }
.rvds-overlay--contained { position: absolute; }
.rvds-overlay--right { align-items: stretch; justify-content: flex-end; padding: 0; }
.rvds-overlay--bottom { align-items: flex-end; justify-content: center; padding: 0; }
.rvds-overlay__backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay-backdrop);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* ---------------------------------------------------------------------------
 * BottomSheet  (components/mobile/BottomSheet.jsx)
 *
 *   <div class="rvds-overlay rvds-overlay--bottom" id="termin">
 *     <div class="rvds-overlay__backdrop"></div>
 *     <div class="rvds-bottom-sheet" role="dialog" aria-modal="true" aria-labelledby="termin-titel">
 *       <div class="rvds-bottom-sheet__grip"><span class="rvds-bottom-sheet__handle" aria-hidden="true"></span></div>
 *       <div class="rvds-bottom-sheet__head">
 *         <h2 class="rvds-bottom-sheet__title" id="termin-titel">Termin wählen</h2>
 *         <button type="button" class="rvds-icon-button rvds-icon-button--bare" aria-label="Schliessen" data-rvds-close><svg …x…></svg></button>
 *       </div>
 *       <div class="rvds-bottom-sheet__body">…</div>
 *       <div class="rvds-bottom-sheet__footer"><button type="button" class="rvds-button rvds-button--solid">Jetzt anfragen</button></div>
 *     </div>
 *   </div>
 *
 * The phone's dialog, on the Overlay frame. 32px top corners and a grab
 * handle; the last part clears the home indicator. `__body--flush` drops the
 * body padding for a list of rows. The footer is optional.
 * ------------------------------------------------------------------------ */

.rvds-bottom-sheet {
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 100%;
  max-width: 640px;
  max-height: 88vh;
  border-top-left-radius: var(--sheet-radius);
  border-top-right-radius: var(--sheet-radius);
  border-top: 1px solid var(--line-2);
  border-left: 1px solid var(--line-2);
  border-right: 1px solid var(--line-2);
  background: var(--bg-2);
  box-shadow: var(--shadow-menu);
}
.rvds-bottom-sheet__grip { display: flex; justify-content: center; padding-top: 10px; flex: none; }
.rvds-bottom-sheet__handle { height: 4px; width: var(--sheet-handle); border-radius: var(--radius-pill); background: var(--line-3); }
.rvds-bottom-sheet__head { display: flex; align-items: center; gap: 12px; padding: 8px var(--card-padding) 0; }
.rvds-bottom-sheet__title { flex: 1; min-width: 0; margin: 0; font-size: var(--text-17); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-bottom-sheet__body { overflow-y: auto; padding: 14px var(--card-padding); padding-bottom: 16px; }
.rvds-bottom-sheet__body--flush { padding: 6px 0 0; padding-bottom: 6px; --rvds-list-inset: var(--card-padding); }
.rvds-bottom-sheet__body:last-child { padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
.rvds-bottom-sheet__footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
  border-top: 1px solid var(--line);
  padding: 14px var(--card-padding);
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
  flex: none;
}

/* ===========================================================================
 * cards  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * SymptomCard  (components/cards/SymptomCard.jsx)
 *
 *   <div class="rvds-symptom-card">
 *     <div class="rvds-symptom-card__head">
 *       <span class="rvds-icon-tile rvds-icon-tile--danger rvds-symptom-card__tile"><svg …alert-triangle, 16px, stroke 1.8…></svg></span>
 *       <span class="rvds-symptom-card__index">03</span>
 *     </div>
 *     <h3 class="rvds-symptom-card__title">Fahrzeug fährt nicht</h3>
 *     <p class="rvds-symptom-card__text">…</p>
 *   </div>
 *
 * The fault card: a flat panel with the red spine down the left edge (drawn
 * here, no element), brightening with the border on hover. The only place
 * that spine is used.
 * ------------------------------------------------------------------------ */

.rvds-symptom-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  overflow: hidden;
  box-sizing: border-box;
  padding: var(--card-padding);
  padding-left: calc(var(--card-padding) + 4px);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--bg-2);
  transition: border-color var(--duration-base);
}
.rvds-symptom-card:hover { border-color: var(--danger-border); }
.rvds-symptom-card::before {
  content: "";
  position: absolute;
  inset-block: 0;
  left: 0;
  width: 3px;
  background: var(--danger);
  opacity: 0.8;
  transition: opacity var(--duration-base);
}
.rvds-symptom-card:hover::before { opacity: 1; }
.rvds-symptom-card__head { display: flex; align-items: center; gap: 12px; }
.rvds-symptom-card__tile { --rvds-icon-tile-size: 36px; }
.rvds-symptom-card__index {
  font-family: var(--font-sans);
  font-size: var(--text-11);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-mono);
  color: var(--dim);
}
.rvds-symptom-card__title { margin: 0; font-size: var(--text-17); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-tight); color: var(--fg); }
.rvds-symptom-card__text { margin: 0; font-size: var(--text-14); line-height: var(--leading-relaxed); color: var(--mut); }

/* ---------------------------------------------------------------------------
 * CtaPanel  (components/cards/CtaPanel.jsx)
 *
 *   <div class="rvds-cta-panel">
 *     <div class="rvds-cta-panel__inner">
 *       <h2 class="rvds-cta-panel__title">Bereit für einen ehrlichen Tesla Service?</h2>
 *       <p class="rvds-cta-panel__description">Jetzt anfragen oder direkt anrufen.</p>
 *       <div class="rvds-cta-panel__actions">…two buttons…</div>
 *     </div>
 *   </div>
 *
 * The centred closing block of a page: flat, no wash, exactly two buttons.
 * ------------------------------------------------------------------------ */

.rvds-cta-panel {
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--bg-2);
  padding: clamp(36px, 5vw, 56px) clamp(20px, 4vw, 40px);
  text-align: center;
}
.rvds-cta-panel__inner { position: relative; z-index: 2; }
.rvds-cta-panel__title { margin: 0 auto 14px; max-width: 640px; font-size: var(--text-cta); font-weight: var(--weight-semibold); line-height: 1.08; color: var(--fg); }
.rvds-cta-panel__description { margin: 0 auto 26px; max-width: 520px; font-size: var(--text-18); color: var(--mut); }
.rvds-cta-panel__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; }

/* ---------------------------------------------------------------------------
 * ServiceCard  (components/cards/ServiceCard.jsx)
 *
 *   <div class="rvds-service-card">
 *     <div class="rvds-service-card__head">
 *       <span class="rvds-icon-tile"><svg …clipboard-check, 18px, stroke 1.8…></svg></span>
 *       <h3 class="rvds-service-card__title">End-of-Warranty Inspektion</h3>
 *     </div>
 *     <p class="rvds-service-card__description">Tesla Garantie läuft ab? Wir prüfen und dokumentieren alle Mängel.</p>
 *     <div class="rvds-service-card__foot">
 *       <a class="rvds-service-card__cta" href="/teslaservice-buchen">Inspektion anfragen</a>
 *       <a class="rvds-service-card__link" href="#detail">Mehr Infos <span aria-hidden="true">→</span></a>
 *     </div>
 *   </div>
 *
 * The landing grid's service tile. Hover lightens fill and border, never
 * moves the card. The chip and the text link keep their ink on hover, as
 * they always have. Several text links sit in `__links`; below sm the chip
 * and every link are at least --touch-target (44px) tall (0.9.0).
 * ------------------------------------------------------------------------ */

.rvds-service-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  box-sizing: border-box;
  padding: var(--card-padding);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  --rvds-fill: var(--surface-card);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
  transition: background var(--duration-card), --rvds-fill var(--duration-card), border-color var(--duration-card);
}
.rvds-service-card:hover { border-color: var(--line-2); --rvds-fill: var(--surface-card-hover); background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint); }
.rvds-service-card__head { display: flex; align-items: center; gap: 12px; }
.rvds-service-card__title {
  flex: 1;
  margin: 0;
  font-size: var(--text-16);
  line-height: var(--leading-snug);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--fg);
}
.rvds-service-card__description { flex-grow: 1; margin: 0; font-size: var(--text-13); line-height: var(--leading-relaxed); color: var(--mut); }
.rvds-service-card__foot { margin-top: 6px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
.rvds-service-card__cta {
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  min-height: var(--touch-target);
  border-radius: var(--radius-pill);
  padding: 8px 12px;
  border: 1px solid var(--line-2);
  background: var(--fill-2);
  color: var(--fg);
  font-size: var(--text-12);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
  transition: all var(--duration-chip);
}
.rvds-service-card__cta:hover { border-color: var(--line-3); background: var(--fill-4); color: var(--fg); }
.rvds-service-card__links { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; }
.rvds-service-card__link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: var(--touch-target);
  font-size: var(--text-12);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
  color: var(--mut-2);
  transition: color var(--duration-chip);
}
.rvds-service-card__link:hover { color: var(--mut-2); }

/* ===========================================================================
 * media  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * MediaFrame  (components/media/MediaFrame.jsx)
 *
 *   <div class="rvds-media-frame rvds-media-frame--ratio" style="--rvds-media-frame-ratio: 4 / 3">
 *     <img class="rvds-media-frame__image" src="…" alt="Tesla Antriebsbatterie auf der Werkbank">
 *     <div class="rvds-media-frame__scrim"></div>
 *     <span class="rvds-media-frame__caption"><span class="rvds-tag rvds-tag--caption">Batterie Remanufaktur</span></span>
 *   </div>
 *
 * A photograph in brand chrome: hairline border and the deep photo shadow
 * (`--flat` drops it). A fixed ratio takes `--ratio` plus the ratio as
 * --rvds-media-frame-ratio; another radius goes into
 * --rvds-media-frame-radius (default --radius-lg). The scrim belongs under a
 * caption.
 * ------------------------------------------------------------------------ */

.rvds-media-frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--rvds-media-frame-radius, var(--radius-lg));
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-photo);
}
.rvds-media-frame--flat { box-shadow: none; }
.rvds-media-frame__image { display: block; width: 100%; height: auto; object-fit: cover; }
.rvds-media-frame--ratio > .rvds-media-frame__image { height: 100%; aspect-ratio: var(--rvds-media-frame-ratio); }
.rvds-media-frame__scrim { position: absolute; inset: 0; background: var(--wash-image-foot); pointer-events: none; }
.rvds-media-frame__caption { position: absolute; left: 18px; bottom: 18px; z-index: 2; }

/* ===========================================================================
 * layout  (0.8.0)
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * Section, SectionHead, Container  (components/layout/*.jsx)
 *
 *   <section class="rvds-section rvds-section--alt" id="preise">
 *     <div class="rvds-container">
 *       <div class="rvds-section-head">
 *         <h2 class="rvds-section-head__title">Reparatur statt Austausch</h2>
 *         <p class="rvds-section-head__description">…</p>
 *       </div>
 *       …
 *     </div>
 *   </section>
 *
 * Section owns the 66 / 88 / 120px rhythm; `--alt` and `--panel` are the two
 * flat surfaces, `--rule` the hairline where two equal sections meet. An
 * anchor jump lands on the heading. SectionHead keeps the 60px to the
 * content; `--center` for full-width grids. Container is the measure:
 * no modifier caps at --container, `--spacious` at --container-spacious,
 * `--wide` (nav, footer) is padded by --gutter below lg and 90% of the
 * viewport from lg (--container-width-wide, the same row as NavBar),
 * `--app` (data) takes the full width, `--form` the field column.
 * ------------------------------------------------------------------------ */

.rvds-section {
  position: relative;
  padding-block: clamp(var(--section-y), 9vw, var(--section-y-lg));
  scroll-margin-top: calc(var(--nav-height) + 12px - clamp(var(--section-y), 9vw, var(--section-y-lg)));
}
.rvds-section--rule { border-top: 1px solid var(--line); }
.rvds-section--alt { background: var(--bg-1); }
.rvds-section--panel { background: var(--bg-2); }

.rvds-section-head { margin-bottom: 60px; text-align: left; }
.rvds-section-head--center { text-align: center; margin-inline: auto; }
.rvds-section-head__title {
  margin: 0 0 16px;
  font-size: var(--text-section);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-title);
  color: var(--fg);
  line-height: 1.08;
}
.rvds-section-head__description {
  margin: 0;
  max-width: var(--measure-lead);
  font-size: var(--text-18);
  color: var(--mut);
  text-wrap: pretty;
}
.rvds-section-head--center > .rvds-section-head__description { margin: 0 auto; }

.rvds-container {
  margin-inline: auto;
  box-sizing: border-box;
  padding-inline: var(--gutter);
  max-width: var(--container);
  width: min(100%, var(--container));
}
.rvds-container--wide { max-width: none; width: 100%; }
.rvds-container--spacious { max-width: var(--container-spacious); width: min(100%, var(--container-spacious)); }
.rvds-container--app { max-width: var(--container-app); width: var(--container-width-app); }
.rvds-container--form { max-width: var(--container-form); width: min(100%, var(--container-form)); }
/* At lg (--bp-lg) the default measure swaps its gutters for a percentage width,
   as reference/foundations.md, Layout, states: 80% of the viewport, still capped at --container.
   Without it a plain-HTML page ran almost full width at 1440px and did not line
   up with a nav and footer that sit on the percentage measure. */
@media (min-width: 1200px) {
  .rvds-container:not(.rvds-container--wide):not(.rvds-container--spacious):not(.rvds-container--app):not(.rvds-container--form) {
    width: min(var(--container-width-lg), var(--container));
    padding-inline: 0;
  }
  /* Nav and footer: 90% from lg, as reference/foundations.md, Layout, states (0.9.0;
     until then it ran the full width with an 18px gutter and did not line up
     with the NavBar). */
  .rvds-container--wide { width: var(--container-width-wide); padding-inline: 0; }
}

/* ===========================================================================
 * 0.8.2 - additions from the shop theme
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * DescriptionList as a summary  (components/app/DescriptionList.jsx)
 *
 *   <dl class="rvds-description-list rvds-description-list--summary">
 *     <div class="rvds-description-list__row">
 *       <dt class="rvds-description-list__label">Zwischensumme</dt>
 *       <dd class="rvds-description-list__value">CHF 96.00</dd>
 *     </div>
 *     <div class="rvds-description-list__row">
 *       <dt class="rvds-description-list__label">Versand</dt>
 *       <dd class="rvds-description-list__value">CHF 12.97</dd>
 *     </div>
 *     <div class="rvds-description-list__row rvds-description-list__row--total">
 *       <dt class="rvds-description-list__label">Total</dt>
 *       <dd class="rvds-description-list__value">CHF 108.97
 *         <span class="rvds-description-list__note">inkl. CHF 8.16 MWST</span></dd>
 *     </div>
 *   </dl>
 *
 * The amounts of a cart, a checkout, an order: one column, no hairline
 * between the lines, every amount tabular in --fg. The last row is
 * `__row--total`: a hairline above it, label and amount in --text-17
 * semibold, and an optional note under the amount (the VAT line).
 * ------------------------------------------------------------------------ */

.rvds-description-list--summary { grid-template-columns: minmax(0, 1fr); }
.rvds-description-list--summary > .rvds-description-list__row { padding: 6px 0; border-bottom: none; }
.rvds-description-list--summary .rvds-description-list__label { font-size: var(--text-14); font-weight: var(--weight-regular); color: var(--mut); }
.rvds-description-list--summary .rvds-description-list__value { font-size: var(--text-14); color: var(--fg); font-variant-numeric: tabular-nums; }
.rvds-description-list__row--total {
  margin-top: var(--space-3);
  padding-top: var(--space-7);
  border-top: 1px solid var(--line);
}
.rvds-description-list--summary > .rvds-description-list__row--total { padding-top: var(--space-7); }
.rvds-description-list__row--total > .rvds-description-list__label,
.rvds-description-list__row--total > .rvds-description-list__value {
  font-size: var(--text-17);
  font-weight: var(--weight-semibold);
  color: var(--fg);
}
.rvds-description-list__note {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.45;
  font-weight: var(--weight-regular);
  color: var(--mut-2);
}

/* ---------------------------------------------------------------------------
 * Section, the first one of a page  (components/layout/Section.jsx)
 *
 *   <section class="rvds-section rvds-section--first">…</section>
 *
 * The first section under the fixed nav: the page already clears the nav
 * (--nav-height), so the section's top padding is --section-y-first (40px)
 * instead of the rhythm's 66 to 120px - otherwise the h1 sits about 130px
 * below the bar on a desktop. The bottom keeps the rhythm. Not for a hero,
 * which owns its full-bleed top.
 * ------------------------------------------------------------------------ */

.rvds-section--first { padding-top: var(--section-y-first); }

/* ---------------------------------------------------------------------------
 * ListRow: a count on the image  (components/data-display/ListRow.jsx)
 *
 *   <span class="rvds-list-row__media rvds-list-row__media--count">
 *     <span class="rvds-list-row__thumb"><img src="…" alt=""></span>
 *     <span class="rvds-list-row__count">2</span>
 *   </span>
 *
 * The quantity of a line where it cannot be changed - an order summary, a
 * confirmation: a 20px pill at the image's top right corner, the same chip
 * as a count on a nav glyph.
 * ------------------------------------------------------------------------ */

.rvds-list-row__media--count { position: relative; }
.rvds-list-row__count {
  position: absolute;
  top: -6px;
  right: -6px;
  box-sizing: border-box;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  --rvds-fill: var(--fill-5);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  -webkit-backdrop-filter: blur(var(--blur-surface));
  backdrop-filter: blur(var(--blur-surface));
  color: var(--fg);
  font-size: 11.5px;
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  line-height: 18px;
  text-align: center;
}

/* ---------------------------------------------------------------------------
 * Input with an action inside  (components/forms/Input.jsx)
 *
 *   <span class="rvds-input rvds-input--action">
 *     <input class="rvds-input__control" type="password" autocomplete="current-password">
 *     <span class="rvds-input__action">
 *       <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-icon-button--sm"
 *         aria-label="Passwort anzeigen" aria-pressed="false" data-rvds-reveal><svg …eye…></svg></button>
 *     </span>
 *   </span>
 *
 * A control inside the field - showing a password, clearing a search. The
 * button is a small bare IconButton at the right edge; `--action` widens
 * both sides by its width, so the text keeps equal air. In plain HTML a
 * button with data-rvds-reveal switches the field between password and
 * text and keeps aria-pressed in step (components.js).
 * ------------------------------------------------------------------------ */

.rvds-input--action > .rvds-input__control {
  padding-left: calc(var(--control-height-sm) + var(--space-1));
  padding-right: calc(var(--control-height-sm) + var(--space-1));
}
.rvds-input__action {
  position: absolute;
  right: var(--space-1);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
}

/* ---------------------------------------------------------------------------
 * Panel, collapsible  (components/app/Panel.jsx)
 *
 *   <details class="rvds-panel rvds-panel--collapsible" data-rvds-open-from="md">
 *     <summary class="rvds-panel__header">
 *       <div class="rvds-panel__heading"><h3 class="rvds-panel__title">Bestellübersicht</h3></div>
 *       <span class="rvds-panel__summary">CHF 108.97</span>
 *       <span class="rvds-panel__chevron" aria-hidden="true"><svg …chevron-down, 16px…></svg></span>
 *     </summary>
 *     <div class="rvds-panel__body">…</div>
 *   </details>
 *
 * A panel that folds to its header - the order summary on a phone. A native
 * <details>: the header is the summary, a figure beside the title stays in
 * view while it is closed, the chevron turns 180° when open, as in
 * Disclosure. Closed, the header has no hairline below it. In plain HTML
 * data-rvds-open-from="md" (or sm, lg, xl) opens it from that width up and
 * closes it below (components.js).
 * ------------------------------------------------------------------------ */

.rvds-panel--collapsible > .rvds-panel__header { cursor: pointer; list-style: none; }
.rvds-panel--collapsible > .rvds-panel__header::-webkit-details-marker { display: none; }
.rvds-panel--collapsible > .rvds-panel__header::marker { content: ""; }
.rvds-panel--collapsible:not([open]) > .rvds-panel__header { border-bottom-color: transparent; }
.rvds-panel__summary {
  flex: none;
  font-size: var(--text-15);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}
.rvds-panel__chevron {
  flex: none;
  display: inline-flex;
  color: var(--mut-2);
  transition: transform var(--duration-icon) var(--ease);
}
.rvds-panel--collapsible[open] > .rvds-panel__header > .rvds-panel__chevron { transform: rotate(180deg); }

/* ---------------------------------------------------------------------------
 * Sidebar, SidebarItem  (components/app/Sidebar.jsx)
 *
 *   <aside class="rvds-sidebar rvds-sidebar--in-page">
 *     <nav class="rvds-sidebar__nav">
 *       <div class="rvds-sidebar__section">
 *         <div class="rvds-sidebar__label">Konto</div>
 *         <a class="rvds-sidebar-item" href="/konto/" aria-current="page">
 *           <span class="rvds-sidebar-item__icon"><svg …16px…></svg></span>
 *           <span class="rvds-sidebar-item__label">Übersicht</span>
 *         </a>
 *         <a class="rvds-sidebar-item" href="/konto/bestellungen/">
 *           <span class="rvds-sidebar-item__icon"><svg…></svg></span>
 *           <span class="rvds-sidebar-item__label">Bestellungen</span>
 *           <span class="rvds-sidebar-item__badge">2</span>
 *         </a>
 *       </div>
 *     </nav>
 *     <div class="rvds-sidebar__footer"><a class="rvds-sidebar-item" href="…">…Abmelden…</a></div>
 *   </aside>
 *
 * The app navigation rail: full height on --bg-1 with a hairline on the
 * right. `--collapsed` is the 60px glyph rail (items take
 * `rvds-sidebar-item--collapsed` and a title). `--in-page` is the column
 * beside a page's content under the nav - no fill and no full height, sticky
 * just below the nav. aria-current="page" marks the active row: a lighter
 * fill and a 2px gold marker, never a gold fill. Another width goes into
 * --rvds-sidebar-width (default 236px).
 * ------------------------------------------------------------------------ */

.rvds-sidebar {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  flex: none;
  width: var(--rvds-sidebar-width, 236px);
  height: 100%;
  border-right: 1px solid var(--line);
  background: var(--bg-1);
  transition: width var(--duration-base) var(--ease-move);
}
.rvds-sidebar--collapsed { width: 60px; }
.rvds-sidebar--in-page {
  height: auto;
  align-self: flex-start;
  position: sticky;
  top: var(--nav-height);
  background: transparent;
}
.rvds-sidebar__header {
  display: flex;
  align-items: center;
  height: var(--nav-height);
  padding: 0 14px;
  border-bottom: 1px solid var(--line);
  flex: none;
  overflow: hidden;
}
.rvds-sidebar__nav { flex: 1; overflow-y: auto; padding: 12px 10px; display: flex; flex-direction: column; gap: 18px; }
.rvds-sidebar__section { display: flex; flex-direction: column; gap: 2px; }
.rvds-sidebar__label { padding: 0 10px 6px; font-size: 11px; font-weight: var(--weight-semibold); letter-spacing: 0.01em; color: var(--dim); }
.rvds-sidebar__footer { border-top: 1px solid var(--line); padding: 10px; flex: none; }

.rvds-sidebar-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  height: var(--control-height);
  padding: 0 10px;
  justify-content: flex-start;
  border-radius: var(--radius-md);
  border: none;
  cursor: pointer;
  text-decoration: none;
  background: transparent;
  color: var(--mut);
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  transition: background var(--duration-base), --rvds-fill var(--duration-base), color var(--duration-base);
}
.rvds-sidebar-item--collapsed { padding: 0; justify-content: center; }
.rvds-sidebar-item:hover { background: var(--fill-2); color: var(--fg); }
.rvds-sidebar-item[aria-current="page"],
.rvds-sidebar-item[aria-current="page"]:hover { background: var(--fill-3); color: var(--fg); font-weight: var(--weight-semibold); }
.rvds-sidebar-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 2px;
  border-radius: var(--radius-pill);
  background: var(--gold);
}
.rvds-sidebar-item__icon { flex: none; display: inline-flex; opacity: 0.85; }
.rvds-sidebar-item__icon > svg { stroke-width: 2; }
.rvds-sidebar-item[aria-current="page"] > .rvds-sidebar-item__icon { opacity: 1; }
.rvds-sidebar-item[aria-current="page"] > .rvds-sidebar-item__icon > svg { stroke-width: 2.2; }
.rvds-sidebar-item__label { flex: 1; min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rvds-sidebar-item__badge {
  flex: none;
  min-width: 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  padding: 1px 6px;
  text-align: center;
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  color: var(--mut-2);  /* a count is read, so not --dim (0.17.0) */
}

/* ---------------------------------------------------------------------------
 * Lightbox  (components/overlays/Lightbox.jsx, behaviour in components.js)
 * New in 0.8.3.
 *
 *   <a class="rvds-lightbox-trigger" href="/bilder/manifold-gross.jpg" data-rvds-lightbox="bilder">
 *     <img src="/bilder/manifold.jpg" alt="Coolant Delete Manifold von oben">
 *   </a>
 *   …more triggers with the same data-rvds-lightbox…
 *
 *   <div class="rvds-overlay rvds-lightbox" id="bilder"
 *        role="dialog" aria-modal="true" aria-label="Produktbilder">
 *     <div class="rvds-overlay__backdrop"></div>
 *     <div class="rvds-lightbox__stage"></div>
 *     <span class="rvds-lightbox__counter" aria-live="polite"></span>
 *     <button type="button" class="rvds-icon-button rvds-lightbox__control rvds-lightbox__close"
 *       aria-label="Schliessen" data-rvds-close><svg …x…></svg></button>
 *     <button type="button" class="rvds-icon-button rvds-lightbox__control rvds-lightbox__prev"
 *       aria-label="Vorheriges Bild"><svg …chevron-left…></svg></button>
 *     <button type="button" class="rvds-icon-button rvds-lightbox__control rvds-lightbox__next"
 *       aria-label="Nächstes Bild"><svg …chevron-right…></svg></button>
 *   </div>
 *
 * A photograph at full view, on the Overlay frame: the page is frozen, focus
 * stays inside, Escape closes. It follows the theme of the page: dark is a
 * soft white glow at 30% behind the image on the page colour, light its exact
 * negative - a black glow at 30% on the page colour (--lightbox-*,
 * tokens/colors.css and tokens/light.css). The glow is the one gradient in
 * the system that does not darken a photograph - see
 * reference/foundations.md, Backgrounds and imagery. Close top right and the arrows left and right are round glass IconButtons
 * above everything, the fixed nav included, clear of the safe areas. The
 * stage takes the current image; components.js fills it from the triggers
 * of its group. Fades in and out on opacity; zoomed it shows the image at
 * its natural size. Without the script each trigger is a link to the
 * original.
 * ------------------------------------------------------------------------ */

.rvds-lightbox-trigger { display: block; cursor: zoom-in; }

.rvds-overlay.rvds-lightbox {
  /* Above everything: the fixed nav (400), sheets (500-520), toasts (700). */
  z-index: 1000;
  padding: 0;
  overflow: hidden;
  opacity: 1;
  transition: opacity var(--duration-overlay) var(--ease);
}
@starting-style {
  .rvds-overlay.rvds-lightbox.rvds-overlay--open { opacity: 0; }
}
.rvds-lightbox--closing { opacity: 0 !important; transition: opacity var(--duration-exit) var(--ease-exit) !important; }

.rvds-lightbox > .rvds-overlay__backdrop {
  background: var(--lightbox-bg);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  overflow: hidden;
}
.rvds-lightbox > .rvds-overlay__backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--lightbox-glow-opacity);
  transform: rotate(var(--lightbox-glow-tilt));
  background: var(--lightbox-glow);
}
@media (orientation: portrait) {
  .rvds-lightbox > .rvds-overlay__backdrop::after {
    transform: rotate(calc(-1 * var(--lightbox-glow-tilt)));
    background: var(--lightbox-glow-portrait);
  }
}

.rvds-lightbox__stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  padding:
    calc(var(--control-height) + var(--space-5) + env(safe-area-inset-top))
    calc(var(--control-height) + 2 * var(--space-5) + env(safe-area-inset-right))
    calc(var(--control-height) + var(--space-5) + env(safe-area-inset-bottom))
    calc(var(--control-height) + 2 * var(--space-5) + env(safe-area-inset-left));
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
@media (max-width: 640px) {
  /* On a phone the image takes the width; the arrows float over its edges. */
  .rvds-lightbox__stage { padding-inline: calc(var(--space-3) + env(safe-area-inset-left)) calc(var(--space-3) + env(safe-area-inset-right)); }
}
.rvds-lightbox__image {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius-lg);
  cursor: zoom-in;
  -webkit-user-drag: none;
  box-shadow: var(--lightbox-image-shadow);
  transform-origin: 50% 50%;
  transition: opacity var(--duration-base) var(--ease), transform var(--duration-base) var(--ease-move);
}
@starting-style {
  .rvds-lightbox__image { opacity: 0; }
}
.rvds-lightbox__image--zoomed { cursor: grab; border-radius: 0; }
.rvds-lightbox__image--dragging { transition: none; cursor: grabbing; }
.rvds-lightbox__image--fixed { cursor: default; }

.rvds-lightbox__control {
  position: absolute;
  z-index: 2;
  border-color: var(--glass-line);
  background: var(--glass-bg);
  box-shadow: var(--glass-highlight);
  -webkit-backdrop-filter: blur(var(--blur-surface)) saturate(var(--saturate-nav));
  backdrop-filter: blur(var(--blur-surface)) saturate(var(--saturate-nav));
  color: var(--fg);
}
.rvds-lightbox__control:not(:disabled):hover { border-color: var(--line-3); background: var(--fill-4); color: var(--fg); }
.rvds-lightbox__close {
  top: calc(var(--space-4) + env(safe-area-inset-top));
  right: calc(var(--space-4) + env(safe-area-inset-right));
}
.rvds-lightbox__prev,
.rvds-lightbox__next { top: 50%; transform: translateY(-50%); }
.rvds-lightbox__prev { left: calc(var(--space-4) + env(safe-area-inset-left)); }
.rvds-lightbox__next { right: calc(var(--space-4) + env(safe-area-inset-right)); }
.rvds-lightbox--single .rvds-lightbox__prev,
.rvds-lightbox--single .rvds-lightbox__next,
.rvds-lightbox--single .rvds-lightbox__counter { display: none; }

.rvds-lightbox__counter {
  position: absolute;
  z-index: 2;
  top: calc(var(--space-4) + env(safe-area-inset-top));
  left: calc(var(--space-4) + env(safe-area-inset-left));
  display: flex;
  align-items: center;
  height: var(--control-height);
  font-size: var(--text-13);
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  color: var(--mut);
  pointer-events: none;
}
.rvds-lightbox__counter:empty { display: none; }

@media (prefers-reduced-motion: reduce) {
  .rvds-overlay.rvds-lightbox,
  .rvds-lightbox--closing,
  .rvds-lightbox__image { transition: none !important; }
  @starting-style {
    .rvds-overlay.rvds-lightbox.rvds-overlay--open,
    .rvds-lightbox__image { opacity: 1; }
  }
}

/* ===========================================================================
 * 0.9.0 - navigation, partner logos, responsive branches
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * NavBar  (components/navigation/NavBar.jsx) - React only
 *
 *   <nav class="rvds-nav-bar" aria-label="Hauptnavigation">
 *     <div class="rvds-nav-bar__inner">
 *       <div class="rvds-nav-bar__start"><a class="rvds-nav-bar__brand" href="/"><span class="rvds-brandmark rvds-brandmark--wortmarke" …>…</span></a></div>
 *       <div class="rvds-nav-bar__links">
 *         <div class="rvds-nav-bar__group">
 *           <button class="rvds-nav-bar__trigger" aria-expanded="false" aria-controls="…">Leistungen <span class="rvds-nav-bar__chevron">…</span><span class="rvds-nav-bar__underline"></span></button>
 *           <div class="rvds-nav-bar__panel" id="…"><div class="rvds-nav-bar__menu"><a class="rvds-nav-bar__item" href="…">…</a></div></div>
 *         </div>
 *         <a class="rvds-nav-bar__link" href="…" aria-current="page">Über uns</a>
 *       </div>
 *       <div class="rvds-nav-bar__end">…language, actions, account, CTA…</div>
 *     </div>
 *   </nav>
 *
 * The fixed blurred bar. Its row is the wide measure, like
 * .rvds-container--wide: --gutter at the sides below lg, 90% of the viewport
 * from lg. An open group or the account menu is a fixed panel; NavBar.jsx
 * sets its measured top, left and width inline and adds --open / --placed.
 * The current page (aria-current="page") keeps a white label and shows the
 * gold underline, as the open group does. No plain-HTML behaviour yet: the
 * classes are the React component's, not a markup contract.
 * ------------------------------------------------------------------------ */

.rvds-nav-bar {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 100;
  /* maxWidth is what makes the fit test honest: without it the bar stretches
     its container to its own content and then measures that width, so it
     always "fits" and the page overflows instead of the bar collapsing. */
  max-width: 100%;
  border-bottom: 1px solid var(--line);
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(var(--blur-nav)) saturate(var(--saturate-nav));
  backdrop-filter: blur(var(--blur-nav)) saturate(var(--saturate-nav));
}
.rvds-nav-bar--static { position: relative; }
.rvds-nav-bar__inner {
  margin-inline: auto;
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  padding-inline: var(--gutter);
  height: var(--nav-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
@media (min-width: 1200px) {
  .rvds-nav-bar__inner { width: var(--container-width-wide); padding-inline: 0; }
}
.rvds-nav-bar__start { display: flex; flex: none; }
.rvds-nav-bar__brand { display: flex; align-items: center; gap: 11px; flex: none; }
.rvds-nav-bar__logo { width: 170px; max-height: 48px; height: auto; object-fit: contain; }
.rvds-nav-bar__wordmark { font-size: 19px; font-weight: var(--weight-bold); letter-spacing: 0.02em; color: var(--gold); }
.rvds-nav-bar__links { display: flex; align-items: center; gap: 4px; flex: 1; justify-content: center; min-width: 0; }
.rvds-nav-bar__links--hidden { display: none; }
.rvds-nav-bar__end { display: flex; align-items: center; gap: 12px; flex: none; }
/* A bar never runs out of room (0.13.0): when mark and right-hand group do not
   fit even with the links folded away, the actions marked data-nav-yield step
   aside into the menu, measured by NavBar - no breakpoint. Whatever yields must
   be reachable in the menu. */
.rvds-nav-bar--tight [data-nav-yield] { display: none; }

/* A flat link and a group's button: the same 14px medium label in --mut. */
.rvds-nav-bar__link,
.rvds-nav-bar__trigger {
  position: relative;
  border-radius: var(--radius-md);
  padding: 9px 13px;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--mut);
  background: transparent;
  transition: color var(--duration-base), background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-nav-bar__link:hover { color: var(--fg); background: var(--hover-veil); }
.rvds-nav-bar__link--accent,
.rvds-nav-bar__link--accent:hover,
.rvds-nav-bar__link[aria-current="page"],
.rvds-nav-bar__link[aria-current="page"]:hover { color: var(--state-ink); }
.rvds-nav-bar__trigger {
  display: flex;
  align-items: center;
  gap: 4px;
  border: none;
  cursor: pointer;
}
.rvds-nav-bar__trigger[aria-expanded="true"] { color: var(--state-ink); background: var(--hover-veil); }
.rvds-nav-bar__trigger--current { color: var(--state-ink); }
.rvds-nav-bar__chevron {
  display: inline-flex;
  opacity: 0.7;
  transform: rotate(0);
  transition: transform var(--duration-base);
}
.rvds-nav-bar__trigger[aria-expanded="true"] > .rvds-nav-bar__chevron { transform: rotate(180deg); }
/* The gold mark under an open group, under a group holding the current page,
   and under the current flat link. */
.rvds-nav-bar__underline,
.rvds-nav-bar__link[aria-current="page"]::after {
  position: absolute;
  inset-inline: 12px;
  bottom: -1px;
  height: 1px;
  background: var(--state-line);
  transform-origin: left;
  transform: scaleX(0);
  transition: transform var(--duration-base);
}
.rvds-nav-bar__link[aria-current="page"]::after { content: ""; transform: scaleX(1); }
.rvds-nav-bar__trigger[aria-expanded="true"] > .rvds-nav-bar__underline,
.rvds-nav-bar__trigger--current > .rvds-nav-bar__underline { transform: scaleX(1); }

.rvds-nav-bar__group { position: relative; }
.rvds-nav-bar__panel {
  position: fixed;
  z-index: 400;
  top: 0;
  left: 0;
  padding-top: 10px;
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--duration-base);
}
.rvds-nav-bar__panel--open { visibility: visible; opacity: 1; }
.rvds-nav-bar__menu {
  min-width: 240px;
  border-radius: var(--radius-xl);
  border: 1px solid var(--line-2);
  background: var(--menu-bg);
  padding: 10px;
  box-shadow: var(--shadow-menu);
  -webkit-backdrop-filter: blur(var(--blur-menu));
  backdrop-filter: blur(var(--blur-menu));
}
.rvds-nav-bar__item {
  display: block;
  border-radius: var(--radius-md); /* a row, not a card: one step below the surface radius (0.17.0) */
  padding: 11px 14px;
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  color: var(--fg);
  background: transparent;
  transition: color var(--duration-base), background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-nav-bar__item:hover { color: var(--fg); background: var(--hover-veil); }
.rvds-nav-bar__item[aria-current="page"] { background: var(--state-surface); }

/* Account: the round trigger and its menu. */
.rvds-nav-bar__account { position: relative; flex: none; }
.rvds-nav-bar__account-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: var(--control-height);
  width: var(--control-height);
  flex: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 1px solid var(--line-2);
  background: var(--fill-2);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--text-13);
  font-weight: var(--weight-semibold);
  transition: background var(--duration-base), --rvds-fill var(--duration-base), border-color var(--duration-base);
}
.rvds-nav-bar__account-trigger[aria-expanded="true"] { border-color: var(--line-3); background: var(--fill-4); }
.rvds-nav-bar__account-image { height: 100%; width: 100%; border-radius: var(--radius-pill); object-fit: cover; }
.rvds-nav-bar__account-menu {
  position: fixed;
  z-index: 400;
  top: 0;
  left: 0;
  min-width: 230px;
  max-width: calc(100vw - 16px);
  visibility: hidden;
  box-sizing: border-box;
  border-radius: var(--radius-xl);
  border: 1px solid var(--line-2);
  background: var(--menu-bg);
  -webkit-backdrop-filter: blur(var(--blur-menu));
  backdrop-filter: blur(var(--blur-menu));
  box-shadow: var(--shadow-menu);
  padding: 8px;
}
.rvds-nav-bar__account-menu--placed { visibility: visible; }
.rvds-nav-bar__account-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 12px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 6px;
}
.rvds-nav-bar__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  height: 34px;
  width: 34px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: var(--fill-2);
  overflow: hidden;
  font-size: var(--text-13);
  font-weight: var(--weight-semibold);
  color: var(--fg);
}
.rvds-nav-bar__avatar > img { height: 100%; width: 100%; object-fit: cover; }
.rvds-nav-bar__account-text { min-width: 0; }
.rvds-nav-bar__account-name,
.rvds-nav-bar__account-meta { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rvds-nav-bar__account-name { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-nav-bar__account-meta { margin-top: 1px; font-size: 12px; color: var(--mut-2); }
.rvds-nav-bar__divider { display: block; height: 1px; background: var(--line); margin: 6px 4px; }
.rvds-nav-bar__menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  border-radius: var(--radius-md);
  border: none;
  padding: 9px 10px;
  text-align: left;
  cursor: pointer;
  text-decoration: none;
  background: transparent;
  color: var(--mut);
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  transition: background var(--duration-base), --rvds-fill var(--duration-base), color var(--duration-base);
}
.rvds-nav-bar__menu-item:hover { background: var(--hover-veil); color: var(--fg); }
.rvds-nav-bar__menu-item--destructive { color: var(--mut-2); }
.rvds-nav-bar__menu-item--destructive:hover { background: var(--danger-surface); color: var(--danger-text); }
.rvds-nav-bar__menu-icon { flex: none; display: inline-flex; opacity: 0.85; }
.rvds-nav-bar__menu-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rvds-nav-bar__badge {
  flex: none;
  min-width: 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  padding: 1px 6px;
  text-align: center;
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}

/* ---------------------------------------------------------------------------
 * StatusLink  (components/navigation/StatusLink.jsx)
 *
 *   <a class="rvds-status-link rvds-status-link--success rvds-status-link--pulse" href="/starlink">
 *     <span class="rvds-status-link__dot" aria-hidden="true"></span>Starlink Referral
 *   </a>
 *
 * A link pill whose dot alone carries the state; border and label stay
 * neutral. No tone modifier is the neutral dot; --success, --danger, --info,
 * --gold colour it, --pulse rings it (off under reduced motion). It is a
 * control: --control-height tall, so it clears 44px on a phone.
 * ------------------------------------------------------------------------ */

.rvds-status-link {
  --rvds-status-link-dot: var(--dim);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  box-sizing: border-box;
  min-height: var(--control-height);
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: transparent;
  padding: 4px 16px;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-13);
  font-weight: var(--weight-medium);
  text-decoration: none;
  color: var(--mut);
  transition: color var(--duration-base), border-color var(--duration-base), background var(--duration-base), --rvds-fill var(--duration-base);
}
.rvds-status-link:hover { border-color: var(--line-3); background: var(--fill-2); color: var(--fg); }
.rvds-status-link--success { --rvds-status-link-dot: var(--success); }
.rvds-status-link--danger { --rvds-status-link-dot: var(--danger); }
.rvds-status-link--info { --rvds-status-link-dot: var(--info); }
.rvds-status-link--gold { --rvds-status-link-dot: var(--gold); }
.rvds-status-link__dot {
  height: 7px;
  width: 7px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--rvds-status-link-dot);
  --pulse-colour: var(--rvds-status-link-dot);
}
.rvds-status-link--pulse > .rvds-status-link__dot { animation: revoltaris-pulse 2s var(--ease-linear) infinite; }
@media (prefers-reduced-motion: reduce) {
  .rvds-status-link--pulse > .rvds-status-link__dot { animation: none; }
}
.rvds-status-link__icon { display: inline-flex; opacity: 0.7; transition: opacity var(--duration-base); }
.rvds-status-link:hover > .rvds-status-link__icon { opacity: 1; }

/* ---------------------------------------------------------------------------
 * FooterLinkColumn  (components/navigation/FooterLinkColumn.jsx)
 *
 *   <div class="rvds-footer-links">
 *     <h2 class="rvds-footer-links__heading">Angebote</h2>
 *     <div class="rvds-footer-links__body">
 *       <div>
 *         <div class="rvds-footer-links__group-heading">Reman</div>
 *         <div class="rvds-footer-links__list rvds-footer-links__list--indented">
 *           <a class="rvds-footer-links__link" href="…">Batteriereparatur</a>
 *         </div>
 *       </div>
 *     </div>
 *   </div>
 *
 * One footer column. The heading's level is the page's business; the look is
 * the class. Links nudge 3px right and turn gold. Below sm each link is at
 * least --touch-target (44px) tall and the list drops its gap.
 * ------------------------------------------------------------------------ */

.rvds-footer-links__heading {
  margin: 0 0 20px;
  font-size: var(--text-19);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.01em;
  color: var(--fg);
}
.rvds-footer-links__body { display: flex; flex-direction: column; gap: 24px; }
.rvds-footer-links__list { display: flex; flex-direction: column; gap: 8px; }
.rvds-footer-links__list--indented { border-left: 1px solid var(--line); padding-left: 14px; }
.rvds-footer-links__group-heading {
  margin-bottom: 10px;
  font-size: 12.5px;
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  /* --mut-2 since 0.16.0: --dim stays under 4.5:1 on the footer's --bg-1. */
  color: var(--mut-2);
}
.rvds-footer-links__link {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--touch-target);
  /* Room for the focus glow beside the text; the negative margin keeps the text on its edge. */
  padding-inline: 6px;
  margin-inline: -6px;
  font-size: var(--text-155);
  color: var(--mut);
  transform: translateX(0);
  transition: color var(--duration-fast), transform var(--duration-fast);
}
.rvds-footer-links__link:hover { color: var(--gold); transform: translateX(var(--hover-nudge)); }
/* Focus for the keyboard only (0.14.0), on every link of a footer column. */
.rvds-footer-links__link:focus-visible { outline: none; border-radius: var(--radius-md); box-shadow: var(--focus-glow); }
@media (max-width: 640px) {
  .rvds-footer-links__list { gap: 0; }
}

/* ---------------------------------------------------------------------------
 * Footer  (components/navigation/Footer.jsx) - new in 0.14.0
 *
 *   <footer class="rvds-footer">
 *     <div class="rvds-container rvds-container--wide">
 *       <div class="rvds-footer__grid" style="--rvds-footer-columns: 3">
 *         <div class="rvds-footer__brand">
 *           …BrandMark use="footer"…
 *           <p class="rvds-footer__intro">…</p>
 *           <section class="rvds-footer__newsletter">
 *             <h2 class="rvds-footer-links__heading">…</h2>
 *             <p class="rvds-footer__newsletter-text">…</p>
 *             <a class="rvds-button rvds-button--solid" href="…">…</a>
 *             <span class="rvds-footer__newsletter-note">…</span>
 *           </section>
 *         </div>
 *         <div class="rvds-footer-links rvds-footer__column">…</div>   one per link column
 *         <div class="rvds-footer__stack">                              several columns in one track (0.16.0)
 *           <div class="rvds-footer-links">…</div>
 *           <div class="rvds-footer-links">…</div>
 *         </div>
 *         <div class="rvds-footer__side">
 *           <div class="rvds-footer-links">
 *             <h2 class="rvds-footer-links__heading">…</h2>
 *             <div class="rvds-footer-links__body">
 *               <div class="rvds-footer-links__list">
 *                 <span class="rvds-footer__address">…Icon…<span>…</span></span>   first in the list
 *                 <a class="rvds-footer-links__link" href="tel:…">…Icon…</a>
 *               </div>
 *             </div>
 *           </div>
 *           <div class="rvds-footer__social">…two .rvds-footer-links: follow, review (BrandIcon)…</div>
 *         </div>
 *       </div>
 *       <div class="rvds-footer__bottom">
 *         <nav class="rvds-footer__legal" aria-label="…">….rvds-footer-links__link…</nav>
 *         <p class="rvds-footer__meta">…</p>
 *         …StatusLink…
 *       </div>
 *     </div>
 *   </footer>
 *
 * Arranges itself by its own width, never by the viewport: from 900px the
 * brand column (2fr) and --rvds-footer-columns tracks side by side; from
 * 640px two up with the brand across; below, the phone order (Alex,
 * 14.10.2026): brand, newsletter, the link columns, contact, follow and review
 * side by side, the legal links in one row. A link column with sub-groups puts
 * them side by side, a flat one flows its links into two columns. Every text
 * is the surface's, as given.
 *
 * rvds-footer--spread (0.16.0): for a footer with little content - few, short
 * link columns, no newsletter. From 900px the side stack gives contact a track
 * of its own and follow and review the next one, stacked; count both in
 * --rvds-footer-columns. Below 900px it is the side stack as always.
 *
 * __stack (0.16.0): two or more link columns of equal rank in one track, 32px
 * apart like the side stack. Counts as one column. Never put the side stack's
 * parts (__social, the contact block) into a __column: the phone rule that
 * splits a column's links in two reaches only the column's own list.
 * ------------------------------------------------------------------------ */

.rvds-footer {
  container: rvds-footer / inline-size;
  border-top: 1px solid var(--line);
  background: var(--bg-1);
}
/* The padding sits inside: the container query below can not restyle the container itself. */
.rvds-footer > .rvds-container { padding-block: 72px 34px; }
.rvds-footer__grid { display: grid; gap: 40px; grid-template-columns: minmax(0, 1fr); }
.rvds-footer__brand { display: flex; flex-direction: column; align-items: flex-start; gap: 20px; min-width: 0; }
.rvds-footer__intro { margin: 0; max-width: 320px; font-size: var(--text-155); color: var(--mut-2); }
.rvds-footer__newsletter {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  width: 100%;
  max-width: 360px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-2);
}
.rvds-footer__newsletter > .rvds-footer-links__heading { margin: 0; }
.rvds-footer__newsletter-text { margin: 0; font-size: var(--text-155); line-height: 1.5; color: var(--mut-2); }
.rvds-footer__newsletter-note { font-size: var(--text-13); color: var(--mut-2); }
/* The surface's own sign-up form: fields across the block, the button at its own width. */
.rvds-footer__newsletter-form,
.rvds-footer__newsletter-form > form { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; width: 100%; }
.rvds-footer__newsletter-form .rvds-input { width: 100%; }
.rvds-footer__side,
.rvds-footer__stack { display: flex; flex-direction: column; gap: 32px; min-width: 0; }
.rvds-footer__social { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.rvds-footer__address {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  /* Its first line stands where a link's label stands: centred in a touch
     target on a phone, no extra room where the target is 0. */
  padding-block: max(0px, (var(--touch-target) - 1lh) / 2);
  font-size: var(--text-155);
  line-height: 1.5;
  color: var(--mut-2);
  white-space: pre-line;
}
.rvds-footer__address > svg { flex: none; margin-top: 3px; }
/* BrandIcon (components/core/BrandIcon.jsx): a mark beside a label never shrinks. */
.rvds-brand-icon { flex: none; }
.rvds-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px 24px;
  margin-top: 56px;
  border-top: 1px solid var(--line);
  padding-top: 28px;
  font-size: 14.5px;
  /* --mut-2, not --dim: on the footer's --bg-1 --dim stays under 4.5:1. */
  color: var(--mut-2);
}
.rvds-footer__legal { display: flex; flex-wrap: wrap; column-gap: 20px; }
.rvds-footer__meta { margin: 0; line-height: 1.6; }
/* Every other link in the footer - the mark, a link in the copyright line. */
.rvds-footer a:not([class]):focus-visible,
.rvds-footer__brand > a:focus-visible { outline: none; border-radius: var(--radius-md); box-shadow: var(--focus-glow); }

@container rvds-footer (width >= 640px) {
  .rvds-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rvds-footer__brand { grid-column: 1 / -1; }
}
@container rvds-footer (width >= 900px) {
  .rvds-footer__grid { grid-template-columns: 2fr repeat(var(--rvds-footer-columns, 3), minmax(0, 1fr)); }
  .rvds-footer__brand { grid-column: auto; }
  /* Beside the columns the side stack is one narrow track: follow and review stack. */
  .rvds-footer__social { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  /* spread: contact and follow/review take a track each. */
  .rvds-footer--spread .rvds-footer__side { display: contents; }
}
/* spread below 1200px of footer: beside a 2fr brand column the tracks would
   break a phone number; the brand goes across the top, the tracks share the row. */
@container rvds-footer (width >= 900px) and (width < 1200px) {
  .rvds-footer--spread .rvds-footer__grid { grid-template-columns: repeat(var(--rvds-footer-columns, 3), minmax(0, 1fr)); }
  .rvds-footer--spread .rvds-footer__brand { grid-column: 1 / -1; }
}
@container rvds-footer (width < 640px) {
  .rvds-footer > .rvds-container { padding-block: 48px 28px; }
  .rvds-footer__grid { gap: 32px; }
  /* Only a column's own body: a list nested deeper (follow and review in a
     column by mistake) is never split a second time. */
  :is(.rvds-footer__column, .rvds-footer__stack > .rvds-footer-links) > .rvds-footer-links__body:has(> div + div) { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 16px; }
  :is(.rvds-footer__column, .rvds-footer__stack > .rvds-footer-links) > .rvds-footer-links__body > .rvds-footer-links__list:only-child { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 16px; }
  .rvds-footer__bottom { flex-direction: column; align-items: flex-start; margin-top: 32px; padding-top: 12px; }
}

/* ---------------------------------------------------------------------------
 * PartnerLogo  (components/media/PartnerLogo.jsx)
 *
 *   <img class="rvds-partner-logo" src="…" alt="Partnername" width="400" height="160" loading="lazy">
 *
 * A partner mark in the logo wall: light artwork on a transparent ground.
 * 130px wide at most on a phone, 160 from sm, 200 from lg; a fixed cap goes
 * into --rvds-partner-logo-max. The treatment is per theme
 * (--partner-logo-filter, --partner-logo-filter-hover): greyscale at rest and
 * its own colours on hover in dark, a dark silhouette in light. Hover brings
 * it to full opacity and 1.05 scale - a scale, never a Y-transform.
 * ------------------------------------------------------------------------ */

.rvds-partner-logo {
  width: 100%;
  max-width: var(--rvds-partner-logo-max, 130px);
  height: auto;
  object-fit: contain;
  opacity: 0.7;
  filter: var(--partner-logo-filter);
  transform: scale(1);
  transition: filter var(--duration-base), opacity var(--duration-base), transform var(--duration-base);
}
.rvds-partner-logo:hover { opacity: 1; filter: var(--partner-logo-filter-hover); transform: scale(1.05); }
@media (min-width: 640px) {
  .rvds-partner-logo { max-width: var(--rvds-partner-logo-max, 160px); }
}
@media (min-width: 1200px) {
  .rvds-partner-logo { max-width: var(--rvds-partner-logo-max, 200px); }
}

/* ---------------------------------------------------------------------------
 * Responsive, mode="css"  (components/layout/Responsive.jsx)
 *
 *   <div class="rvds-responsive rvds-responsive--until-md">…phone…</div>
 *   <div class="rvds-responsive rvds-responsive--from-md">…desktop…</div>
 *
 * Both branches are in the markup and CSS shows the right one, so a
 * server-rendered page paints the right branch before any script runs. The
 * wrapper takes no box of its own (display: contents). The widths are the
 * ones Responsive.jsx uses: from = min-width, until = below it.
 * ------------------------------------------------------------------------ */

.rvds-responsive { display: contents; }
@media (max-width: 639px) { .rvds-responsive--from-sm { display: none; } }
@media (max-width: 899px) { .rvds-responsive--from-md { display: none; } }
@media (max-width: 1199px) { .rvds-responsive--from-lg { display: none; } }
@media (max-width: 1559px) { .rvds-responsive--from-xl { display: none; } }
@media (min-width: 640px) { .rvds-responsive--until-sm { display: none; } }
@media (min-width: 900px) { .rvds-responsive--until-md { display: none; } }
@media (min-width: 1200px) { .rvds-responsive--until-lg { display: none; } }
@media (min-width: 1560px) { .rvds-responsive--until-xl { display: none; } }

/* ---------------------------------------------------------------------------
 * Seal  (components/media/Seal.jsx)  - 0.10.0
 *
 *   <span class="rvds-seal" role="img" aria-label="…">
 *     <svg viewBox="0 0 120 120" aria-hidden="true" focusable="false">
 *       <defs><path id="s-t" d="M28.53 91.47 A44.5 44.5 0 1 1 91.47 91.47"/>
 *             <path id="s-b" d="M10.5 60 A49.5 49.5 0 0 0 109.5 60"/></defs>
 *       <circle class="rvds-seal__ring" cx="60" cy="60" r="57"/>
 *       <circle class="rvds-seal__ring rvds-seal__ring--rim" cx="60" cy="60" r="54"/>
 *       <circle class="rvds-seal__ring rvds-seal__ring--inner" cx="60" cy="60" r="39"/>
 *       <circle class="rvds-seal__dot" cx="13" cy="60" r="1.1"/> (and cx="109")
 *       <text class="rvds-seal__text"><textPath href="#s-t" startOffset="50%" text-anchor="middle">…</textPath></text>
 *       <g class="rvds-seal__glyph" transform="translate(38 38) scale(1.8333)">…Lucide paths…</g>
 *     </svg>
 *   </span>
 *
 * A document seal, in one colour: rings, dots, glyph and ring text are all
 * --gold (0.10.2, the one exception to "gold text only on buttons and
 * the StatTile figure"). Nothing is filled. Diameter via --rvds-seal-size. The text size is in viewBox
 * units, so it scales with the seal: 8 of 120, about 6.4px at the 96px floor.
 * Line weights: outer ring 1.2, glyph about 1.2, inner ring 0.6,
 * embossed rim 0.5.
 * ------------------------------------------------------------------------ */

.rvds-seal {
  display: inline-block;
  flex: none;
  width: var(--rvds-seal-size, 120px);
  height: var(--rvds-seal-size, 120px);
}
.rvds-seal > svg { display: block; width: 100%; height: 100%; overflow: visible; }
.rvds-seal__ring { fill: none; stroke: var(--gold); stroke-width: 1.2; }
.rvds-seal__ring--rim { stroke-width: 0.5; }
.rvds-seal__ring--inner { stroke: var(--gold-a60); stroke-width: 0.6; }
.rvds-seal__dot { fill: var(--gold); }
.rvds-seal__text {
  fill: var(--gold);
  font-family: var(--font-sans);
  font-size: 8px;
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
}
/* The glyph group is scaled 1.83 (24 -> 44 units), so 0.65 here draws at
   about 1.2 - as heavy as the outer ring, never heavier. */
.rvds-seal__glyph {
  fill: none;
  stroke: var(--gold);
  stroke-width: 0.65;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* A product's own mark (the DriveCare shield) is a filled shape, not a line:
   gold fill, no stroke. Its group is scaled 44 / its viewBox (0.44 for 100). */
.rvds-seal__glyph--fill { fill: var(--gold); stroke: none; }

/* ---------------------------------------------------------------------------
 * BrandMark - every Revoltaris mark, the company's and a unit's.
 *
 *   <span class="rvds-brandmark rvds-brandmark--lockup-horizontal" role="img" aria-label="…">
 *     <svg class="rvds-brandmark__svg" aria-hidden="true" focusable="false">
 *       <use href="…/assets/marken/automotive-lockup-horizontal.svg#m"/>
 *     </svg>
 *   </span>
 *
 * One file per mark. A theme switch only recolours it through the tokens
 * --brandmark-ink and --brandmark-gold (and, for icon-text, which of its two
 * reliefs shows) - it never loads another file. --brandmark-size is the height
 * of the wordmark inside the mark; each group multiplies it by its factor from
 * the brand kit and takes its width from the file's aspect ratio. For the
 * signet the size is the edge.
 * ------------------------------------------------------------------------ */
.rvds-brandmark {
  --brandmark-size: 20px; --brandmark-factor: 1; --brandmark-ratio: 1;
  /* A block exactly the size of its mark: no line-height strut under it, and
     fit-content keeps a stretching grid or flex parent from growing the box,
     while the parent still decides where it sits. */
  display: flex; align-items: flex-start; width: fit-content; height: fit-content;
  max-width: 100%; min-width: 0; line-height: 0;
}
/* Width from the size and the file's aspect ratio, height follows it: in a
   column narrower than the mark the whole box shrinks, proportions intact. */
.rvds-brandmark__svg {
  display: block;
  width: calc(var(--brandmark-size) * var(--brandmark-factor) * var(--brandmark-ratio));
  max-width: 100%;
  height: auto;
  aspect-ratio: var(--brandmark-ratio);
  overflow: visible;
}
/* The signet in black and white: its gold takes the ink as well. */
.rvds-brandmark--mono { --brandmark-gold: var(--brandmark-ink); }
/* In a bar the size is the system's, never the surface's. */
.rvds-nav-bar__brand .rvds-brandmark,
.rvds-sidebar__header .rvds-brandmark { --brandmark-size: var(--brandmark-bar); }
.rvds-brandmark--wortmarke { --brandmark-factor: 1; --brandmark-ratio: 8.986; }
.rvds-brandmark--gruppenlogo { --brandmark-factor: 4.6; --brandmark-ratio: 1.9542; }
.rvds-brandmark--lockup-horizontal { --brandmark-factor: 1.14; --brandmark-ratio: 11.3016; }
.rvds-brandmark--lockup-vertikal { --brandmark-factor: 1.88; --brandmark-ratio: 4.7596; }
.rvds-brandmark--icon-text { --brandmark-factor: 1.4; --brandmark-ratio: 5.1399; }
.rvds-brandmark--lockup-horizontal-atom { --brandmark-factor: 4.6; --brandmark-ratio: 2.7948; }
.rvds-brandmark--signet { --brandmark-size: 24px; }

/* ===========================================================================
 * App surfaces on wide screens  (0.15.0)
 *
 * The page uses the full width; the width belongs to the element. Every
 * element of an app surface has a width class: S and M have a minimum track
 * (--track-s 320px, --track-m 480px) and grow to under twice that, L fills.
 * Collections of the same kind go into a Grid (auto-fill, so a free track
 * stays free and a lone card is never stretched), a wide view opens a
 * context column, fields stay in their form column. reference/foundations.md,
 * "App surfaces on wide screens", has the rules.
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * Grid  (components/layout/Grid.jsx)
 *
 *   <div class="rvds-grid">                         S tracks (dates, KPIs, contacts)
 *     <div class="rvds-grid__item--span-2">…an inline Alert…</div>
 *     <article class="rvds-panel">…</article>
 *   </div>
 *   <div class="rvds-grid rvds-grid--m">…board columns, form cards…</div>
 *
 * auto-fill with minmax(min(100%, track), 1fr): the grid fills the row with
 * as many tracks as fit and leaves the rest empty. A span applies only once
 * the grid holds that many tracks - measured on the grid itself (container),
 * not on the viewport, so a grid in a narrow column keeps its single track.
 * `--full` runs from edge to edge at every width. The grid is a size
 * container: give it a width (a block, a grid cell), never a flex row that
 * sizes by content, or it collapses.
 * ------------------------------------------------------------------------ */

.rvds-grid {
  container: rvds-grid / inline-size;
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--track-s)), 1fr));
  min-width: 0;
}
.rvds-grid--m { grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--track-m)), 1fr)); }
.rvds-grid > * { min-width: 0; }
.rvds-grid__item--full { grid-column: 1 / -1; }
/* Two S tracks: 2 x 320 + 18. Three: 3 x 320 + 2 x 18. Two M tracks: 2 x 480 + 18. */
@container rvds-grid (width >= 658px) {
  .rvds-grid:not(.rvds-grid--m) > .rvds-grid__item--span-2 { grid-column: span 2; }
}
@container rvds-grid (width >= 996px) {
  .rvds-grid:not(.rvds-grid--m) > .rvds-grid__item--span-3 { grid-column: span 3; }
}
@container rvds-grid (width >= 978px) {
  .rvds-grid--m > .rvds-grid__item--span-2 { grid-column: span 2; }
}

/* ---------------------------------------------------------------------------
 * ContextLayout  (components/layout/ContextLayout.jsx)
 *
 *   <div class="rvds-context-layout">
 *     <div class="rvds-context-layout__inner">
 *       <div class="rvds-context-layout__main">…the view…</div>
 *       <aside class="rvds-context-layout__context">…recent documents, contact…</aside>
 *     </div>
 *   </div>
 *
 * From --context-from (1240px of its own width, what a 1560px screen leaves
 * beside the sidenav) the view gets a second column of 360 to 480px for
 * companion content, instead of one element stretched across the screen.
 * Below it the context follows the main column, in S tracks. `--hide` keeps it out below
 * that width: for a preview that only makes sense beside its list (Table
 * `detail`); the surface then opens the same content in a Drawer.
 * ------------------------------------------------------------------------ */

.rvds-context-layout { container: rvds-context / inline-size; min-width: 0; }
.rvds-context-layout__inner { display: grid; gap: var(--grid-gap); grid-template-columns: minmax(0, 1fr); align-items: start; }
.rvds-context-layout__main,
.rvds-context-layout__context { display: grid; gap: var(--space-7); align-content: start; min-width: 0; }
/* Below the threshold the context follows the view in S tracks, so a lone
   card there is not stretched across the row either. */
.rvds-context-layout__context { grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--track-s)), 1fr)); }
.rvds-context-layout--hide .rvds-context-layout__context { display: none; }
@container rvds-context (width >= 1240px) {
  .rvds-context-layout__inner { grid-template-columns: minmax(0, 1fr) clamp(var(--context-column-min), 28%, var(--context-column-max)); }
  .rvds-context-layout__context { grid-template-columns: minmax(0, 1fr); }
  .rvds-context-layout--hide .rvds-context-layout__context { display: grid; }
}

/* ---------------------------------------------------------------------------
 * Form grid, field group, form actions  (forms; Field.prompt.md)
 *
 *   <div class="rvds-container rvds-container--form">
 *     <div class="rvds-form-grid">
 *       <label class="rvds-field">…Vorname…</label>
 *       <label class="rvds-field">…Nachname…</label>
 *       <div class="rvds-field-group">
 *         <label class="rvds-field">…PLZ…</label><label class="rvds-field">…Ort…</label>
 *       </div>
 *       <div class="rvds-field-group rvds-field-group--end">
 *         <label class="rvds-field">…Strasse…</label><label class="rvds-field">…Nr.…</label>
 *       </div>
 *       <label class="rvds-field rvds-form-grid__wide">…Nachricht…</label>
 *     </div>
 *     <div class="rvds-form-actions">…secondary…, …primary last…</div>
 *   </div>
 *
 * Fields stay in the form column (--container-form, 720px) on every screen:
 * two columns from 616px of row, one below, so a phone gets full-width
 * fields and a desktop never gets a 2000px field. `__wide` takes the whole
 * row (a street, a message). A field group is a short field and its partner
 * on one line that never breaks - the postcode always left of the town, on
 * a phone too. The actions close the column on its right edge, the primary
 * one last.
 * ------------------------------------------------------------------------ */

.rvds-form-grid {
  display: grid;
  gap: 14px 16px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  align-items: start;
  min-width: 0;
}
.rvds-form-grid > * { min-width: 0; }
.rvds-form-grid__wide { grid-column: 1 / -1; }
.rvds-field-group {
  display: grid;
  grid-template-columns: clamp(88px, 30%, 140px) minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  min-width: 0;
}
.rvds-field-group > * { min-width: 0; }
/* The short field on the right: street and house number (0.17.0). */
.rvds-field-group--end { grid-template-columns: minmax(0, 1fr) clamp(72px, 22%, 110px); }
.rvds-form-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

/* ---------------------------------------------------------------------------
 * Sorting: Table heads, SortButton, the sort panel
 * (components/data-display/Table.jsx, components/app/SortButton.jsx,
 *  behaviour for plain HTML in components.js, motion in interactions.css)
 *
 *   <th class="rvds-table__head" aria-sort="descending">
 *     <button type="button" class="rvds-table__sort" data-rvds-sort-key="date" data-first="desc">
 *       Datum
 *       <svg class="rvds-icon rvds-table__sort-idle">…chevrons-up-down…</svg>
 *       <svg class="rvds-icon rvds-table__sort-arrow">…arrow-up…</svg>
 *     </button>
 *   </th>
 *
 * Every list is sortable and always shows how. With column heads the head
 * is the control: the active one carries a gold arrow that turns with the
 * direction, the others chevrons-up-down in --dim, visible without hover.
 * The label keeps its ink. A column that cannot be sorted is a plain head
 * without a glyph. aria-sort sits on the <th>.
 * ------------------------------------------------------------------------ */

.rvds-table__sort {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  white-space: nowrap;
}
.rvds-table__head--right .rvds-table__sort { justify-content: flex-end; }
.rvds-table__head--center .rvds-table__sort { justify-content: center; }
.rvds-table__sort > svg { flex: none; width: 13px; height: 13px; }
.rvds-table__sort-idle { color: var(--dim); transition: color var(--duration-instant); }
.rvds-table__sort-arrow { display: none; color: var(--gold); }
:is([aria-sort="ascending"], [aria-sort="descending"]) > .rvds-table__sort > .rvds-table__sort-idle { display: none; }
:is([aria-sort="ascending"], [aria-sort="descending"]) > .rvds-table__sort > .rvds-table__sort-arrow { display: block; }
[aria-sort="descending"] > .rvds-table__sort > .rvds-table__sort-arrow { transform: rotate(180deg); }
@media (hover: hover) { .rvds-table__sort:hover > .rvds-table__sort-idle { color: var(--mut); } }

/*   <button type="button" class="rvds-button rvds-button--ghost rvds-sort-button"
 *     data-rvds-popover="sortierung" aria-label="Sortiert nach Datum, Neueste zuerst">
 *     <svg class="rvds-icon rvds-sort-button__glyph">…arrow-down-wide-narrow…</svg>
 *     <span class="rvds-sort-button__label">Datum</span>
 *   </button>
 *
 * Without column heads - a list, a card grid, a board, a table that has
 * stacked - one ghost button in the toolbar, as wide as its content: the
 * field as one word, the direction as the glyph before it
 * (arrow-down-wide-narrow descending, arrow-up-narrow-wide ascending), the
 * long form only in the aria-label. From 640px it opens a popover, below an
 * ActionSheet; both hold the sort panel. */
.rvds-sort-button__glyph { flex: none; }
.rvds-sort-button__label { display: inline-block; }
/* Plain HTML carries both glyphs; the button's data-dir (components.js) shows
   one. React renders only the one it needs. */
.rvds-sort-button[data-dir="asc"] > .rvds-sort-button__glyph--desc,
.rvds-sort-button[data-dir="desc"] > .rvds-sort-button__glyph--asc { display: none; }

/*   <div class="rvds-sort-panel">
 *     <div class="rvds-sort-panel__label">Richtung</div>          (optional)
 *     <div class="rvds-sort-dir" data-dir="desc" role="group" aria-label="…">
 *       <span class="rvds-sort-dir__thumb" aria-hidden="true"></span>
 *       <button type="button" class="rvds-sort-dir__option" data-rvds-sort-dir="desc" aria-pressed="true" aria-label="Neueste zuerst">…arrow-down-wide-narrow…</button>
 *       <button type="button" class="rvds-sort-dir__option" data-rvds-sort-dir="asc" aria-pressed="false" aria-label="Älteste zuerst">…arrow-up-narrow-wide…</button>
 *     </div>
 *     <div class="rvds-sort-panel__options" role="menu">
 *       <button type="button" class="rvds-sort-option" role="menuitemradio" aria-checked="true" data-rvds-sort-key="date">Datum<svg class="rvds-icon rvds-sort-option__check">…check…</svg></button>
 *     </div>
 *   </div>
 *
 * The direction on top as two glyph segments, the fields under it, the
 * chosen field with a gold tick drawn from the left. In the ActionSheet the
 * fields are its 52px rows (`rvds-action-sheet__row`) and the direction
 * sits in its header. */
.rvds-sort-panel { display: grid; gap: 6px; min-width: 200px; }
.rvds-sort-panel__label { padding: 4px 10px 0; font-size: 11.5px; font-weight: var(--weight-bold); letter-spacing: 0.04em; color: var(--text-label); }
.rvds-sort-panel__options { display: grid; gap: 2px; }
.rvds-sort-dir {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  box-sizing: border-box;
  height: var(--control-height);
  padding: 3px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--field-bg, var(--fill-1));
  box-shadow: var(--control-inset);
  isolation: isolate;
}
.rvds-sort-dir__thumb {
  position: absolute;
  z-index: -1;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc(50% - 3px);
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  box-sizing: border-box;
  --rvds-fill: var(--fill-4);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  box-shadow: var(--control-raised);
}
.rvds-sort-dir[data-dir="asc"] > .rvds-sort-dir__thumb { transform: translateX(100%); }
.rvds-sort-dir__option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: var(--radius-pill);
  color: var(--mut-2);
  transition: color var(--duration-base);
}
.rvds-sort-dir__option[aria-pressed="true"] { color: var(--fg); }
@media (hover: hover) {
  .rvds-sort-dir__option:hover { color: var(--mut); }
  .rvds-sort-dir__option[aria-pressed="true"]:hover { color: var(--fg); }
}
.rvds-sort-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  min-height: 36px;
  padding: 0 10px;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--mut);
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant), color var(--duration-instant);
}
@media (hover: hover) { .rvds-sort-option:hover { background: var(--hover-veil); color: var(--fg); } }
.rvds-sort-option:focus-visible { background: var(--hover-veil); color: var(--fg); }
.rvds-sort-option[aria-checked="true"] { color: var(--fg); }
.rvds-sort-option__check,
.rvds-action-sheet__check { flex: none; margin-left: auto; color: var(--gold); clip-path: inset(0 100% 0 0); }
.rvds-sort-option[aria-checked="true"] > .rvds-sort-option__check,
.rvds-action-sheet__row[aria-checked="true"] > .rvds-action-sheet__check { clip-path: inset(0 0 0 0); }

/* ---------------------------------------------------------------------------
 * ActionSheet  (components/mobile/ActionSheet.jsx) - migrated in 0.15.0
 *
 *   …inside a .rvds-bottom-sheet with rvds-bottom-sheet__body--flush:
 *   <div class="rvds-action-sheet" role="menu">
 *     <div class="rvds-action-sheet__header">…optional, e.g. the sort direction…</div>
 *     <button type="button" class="rvds-action-sheet__row" role="menuitem">
 *       <span class="rvds-action-sheet__glyph"><svg …17px…></svg></span>
 *       <span class="rvds-action-sheet__text">Offerte senden<span class="rvds-action-sheet__description">…</span></span>
 *     </button>
 *     <span class="rvds-action-sheet__divider" aria-hidden="true"></span>
 *     <button type="button" class="rvds-action-sheet__row rvds-action-sheet__row--danger" role="menuitem">…</button>
 *     <span class="rvds-action-sheet__cancel-divider" aria-hidden="true"></span>
 *     <button type="button" class="rvds-action-sheet__cancel" data-rvds-close>Abbrechen</button>
 *   </div>
 *
 * Rows are 52px and full-width, hairlines inset 16px so they start under the
 * text, the cancel row behind a stronger line. A row of a choice
 * (role="menuitemradio") carries `aria-checked` and a gold tick on the right
 * (`rvds-action-sheet__check`). Pressed, a row takes the hover veil.
 * ------------------------------------------------------------------------ */

.rvds-action-sheet { display: flex; flex-direction: column; }
.rvds-action-sheet__header { padding: 2px 16px 14px; }
.rvds-action-sheet__divider { height: 1px; margin-left: 16px; background: var(--line); }
.rvds-action-sheet__row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  min-height: 52px;
  padding: 0 16px;
  border: none;
  text-align: left;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-15);
  font-weight: var(--weight-medium);
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant);
}
.rvds-action-sheet__row:active { background: var(--hover-veil); }
.rvds-action-sheet__row:disabled { cursor: default; opacity: 0.45; }
.rvds-action-sheet__row--danger { color: var(--danger-text); }
.rvds-action-sheet__glyph { flex: none; display: inline-flex; color: var(--mut-2); }
.rvds-action-sheet__row--danger > .rvds-action-sheet__glyph { color: var(--danger); }
.rvds-action-sheet__text { flex: 1; min-width: 0; }
.rvds-action-sheet__description { display: block; margin-top: 2px; font-size: var(--text-12); font-weight: var(--weight-regular); color: var(--mut-2); }
.rvds-action-sheet__cancel-divider { height: 1px; background: var(--line-2); }
.rvds-action-sheet__cancel {
  min-height: 52px;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-15);
  font-weight: var(--weight-semibold);
  color: var(--mut);
}

/* --- interactions.css (unverändert) --- */
/*
 * Revoltaris Design System - interaction layer (0.14.0).
 *
 * Loaded by styles.css after components.css. It adds how things answer -
 * press, focus, arrival, change - the floating surfaces (glass, the mega
 * panel, tooltips and popovers, the action bar) and the navigation built on
 * them: the NavBar that fits itself, the mobile menu, the sidenav.
 * Behaviour for plain HTML is in interactions.js; the reference is
 * reference/interactions.md.
 *
 * Four rules hold every line below:
 *   1. Nothing moves on the Y axis because of a hover. Hover lightens.
 *   2. Feedback answers the input that caused it: a press seats under the
 *      pointer, a tick draws in the reading direction, a value rolls the way
 *      it changed, a panel slides the way the pointer went.
 *   3. Floating UI grows out of its anchor - scale from the anchor side, never
 *      a slide - and leaves faster than it came (--duration-exit).
 *   4. prefers-reduced-motion keeps every colour change and drops every
 *      transform. Hover effects exist only where a pointer can hover.
 */

/* ===========================================================================
 * 1. Press - the control seats under the finger
 * ======================================================================== */

.rvds-button,
.rvds-icon-button:not(.rvds-lightbox__control),
.rvds-pagination__page,
.rvds-service-card__cta,
.rvds-status-link,
.rvds-stepper__nudge,
.rvds-action-bar__clear {
  --rvds-press-t: var(--duration-release);
  --rvds-press-e: var(--ease-settle);
  -webkit-tap-highlight-color: transparent;
  transform: scale(1);
  transition-property: background, border-color, color, opacity, filter, box-shadow, transform, --rvds-fill;
  transition-duration: var(--duration-base), var(--duration-base), var(--duration-base), var(--duration-base), var(--duration-base), var(--duration-fast), var(--rvds-press-t), var(--duration-base);
  transition-timing-function: ease, ease, ease, ease, ease, var(--ease), var(--rvds-press-e), ease;
}
:is(.rvds-button, .rvds-pagination__page, .rvds-service-card__cta, .rvds-status-link):active:not(:disabled):not([aria-disabled="true"]) {
  --rvds-press-t: var(--duration-press);
  --rvds-press-e: var(--ease);
  transform: scale(var(--press-scale));
}
:is(.rvds-icon-button:not(.rvds-lightbox__control), .rvds-stepper__nudge, .rvds-action-bar__clear):active:not(:disabled) {
  --rvds-press-t: var(--duration-press);
  --rvds-press-e: var(--ease);
  transform: scale(var(--press-scale-sm));
}
/* Solid gold darkens as it seats - the only fill that can show depth. */
.rvds-button--solid:active:not(:disabled):not([aria-disabled="true"]) { filter: brightness(0.92); }

/* Rows and tiles are too large to scale; they take the next fill step. */
.rvds-list-row--interactive:active,
.rvds-table__row--interactive:active,
.rvds-table__stack-row--interactive:active { background: var(--fill-3); transition-duration: var(--duration-press); }

/* ===========================================================================
 * 2. Focus - the glow blooms for the keyboard only
 * ======================================================================== */

:is(.rvds-button, .rvds-icon-button, .rvds-pagination__page, .rvds-tabs__tab, .rvds-sidebar-item,
    .rvds-nav-bar__link, .rvds-nav-bar__trigger, .rvds-nav-bar__item, .rvds-nav-bar__menu-item, .rvds-nav-bar__brand,
    .rvds-tab-bar__item, .rvds-faq-item__toggle, .rvds-disclosure__summary, .rvds-service-card__cta,
    .rvds-status-link, .rvds-stepper__nudge, .rvds-mega__item, .rvds-mega__feature, .rvds-action-bar__clear,
    .rvds-float__item, .rvds-segmented__option, .rvds-toc__link, .rvds-breadcrumb__link):focus { outline: none; }
:is(.rvds-button, .rvds-icon-button, .rvds-pagination__page, .rvds-tabs__tab, .rvds-sidebar-item,
    .rvds-nav-bar__link, .rvds-nav-bar__trigger, .rvds-nav-bar__item, .rvds-nav-bar__menu-item,
    .rvds-tab-bar__item, .rvds-service-card__cta, .rvds-status-link, .rvds-stepper__nudge,
    .rvds-mega__item, .rvds-mega__feature, .rvds-action-bar__clear, .rvds-float__item, .rvds-segmented__option):focus-visible {
  box-shadow: var(--focus-glow);
}
/* Text rows and links without a shell get one for the glow to sit on: the
   NavBar's mark, the table of contents and the breadcrumb (0.16.0). */
:is(.rvds-faq-item__toggle, .rvds-disclosure__summary, .rvds-nav-bar__brand, .rvds-toc__link, .rvds-breadcrumb__link):focus-visible {
  border-radius: var(--radius-md);
  box-shadow: var(--focus-glow);
}
.rvds-tabs__tab { border-radius: var(--radius-md) var(--radius-md) 0 0; }

/* ===========================================================================
 * 3. Hover that tells you where you are going (pointer devices only)
 * ======================================================================== */

.rvds-list-row__chevron,
.rvds-service-card__link > [aria-hidden="true"],
.rvds-mega__arrow {
  display: inline-flex;
  transform: translateX(0);
  transition: transform var(--duration-fast) var(--ease), color var(--duration-fast), opacity var(--duration-fast);
}
@media (hover: hover) and (pointer: fine) {
  .rvds-list-row--interactive:hover > .rvds-list-row__chevron { transform: translateX(2px); color: var(--mut); }
  .rvds-service-card:hover .rvds-service-card__link { color: var(--fg); }
  .rvds-service-card__link:hover > [aria-hidden="true"] { transform: translateX(var(--hover-nudge)); }
  .rvds-input:hover > .rvds-input__control:not(:focus):not(:disabled),
  .rvds-textarea:hover:not(:focus):not(:disabled) { border-color: var(--field-border-hover, var(--line-3)); }
}

/* ===========================================================================
 * 4. Fields - the field gets ready when you arrive
 * ======================================================================== */

.rvds-input__icon,
.rvds-input__suffix { transition: color var(--duration-base); }
.rvds-input:focus-within > .rvds-input__icon { color: var(--gold); }
.rvds-input:focus-within > .rvds-input__suffix { color: var(--mut); }
.rvds-input__control::placeholder,
.rvds-textarea::placeholder { transition: opacity var(--duration-base); }
.rvds-input__control:focus::placeholder,
.rvds-textarea:focus::placeholder { opacity: 0.55; }
/* An error arrives under its field; it does not jump in. */
.rvds-field__hint--error { animation: rvds-arrive var(--duration-base) var(--ease) both; }

/* ===========================================================================
 * 5. Selection controls - a tick is drawn, a knob is grabbed
 * ======================================================================== */

/* The tick is always in the box and is uncovered left to right when checked;
   unchecking takes it away faster than it came. */
.rvds-checkbox__box > svg {
  display: block;
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--duration-instant) var(--ease-exit);
}
.rvds-checkbox__input:checked + .rvds-checkbox__box > svg {
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--duration-draw) var(--ease) 40ms;
}
.rvds-checkbox--indeterminate > .rvds-checkbox__input + .rvds-checkbox__box::after { animation: rvds-dash var(--duration-draw) var(--ease) both; }
.rvds-checkbox__box,
.rvds-radio__box {
  transition:
    background var(--duration-fast) var(--ease-settle),
    border-color var(--duration-base),
    box-shadow var(--duration-base),
    transform var(--duration-release) var(--ease-settle);
}
:is(.rvds-checkbox, .rvds-radio):not(:is(.rvds-checkbox--disabled, .rvds-radio--disabled)):active > :is(.rvds-checkbox__box, .rvds-radio__box) {
  transform: scale(var(--press-scale-sm));
  transition-duration: var(--duration-fast), var(--duration-base), var(--duration-base), var(--duration-press);
}
.rvds-checkbox__input:focus:not(:focus-visible) + .rvds-checkbox__box,
.rvds-radio__input:focus:not(:focus-visible) + .rvds-radio__box { box-shadow: var(--control-inset); }
.rvds-checkbox__input:checked:focus:not(:focus-visible) + .rvds-checkbox__box,
.rvds-radio__input:checked:focus:not(:focus-visible) + .rvds-radio__box { box-shadow: var(--control-raised); }

/* The knob stretches towards its travel while held, like a thumb on a
   slide switch; released it snaps across on --ease-settle. */
.rvds-switch__knob {
  transition:
    transform var(--duration-base) var(--ease-settle),
    width var(--duration-fast) var(--ease),
    background var(--duration-base);
}
.rvds-switch:not(.rvds-switch--disabled):active .rvds-switch__knob { width: 20px; }
.rvds-switch:not(.rvds-switch--disabled):active > .rvds-switch__input:checked + .rvds-switch__track > .rvds-switch__knob { transform: translateX(12px); }
.rvds-switch__input:focus:not(:focus-visible) + .rvds-switch__track { box-shadow: var(--control-inset); }

/* ===========================================================================
 * 6. Values that change - the digit rolls the way it went
 * ======================================================================== */

/*   <b class="rvds-roll" data-value="12">12</b>  +  RevoltarisDS.roll(el, "13")
 * Only the digits that changed move; the rightmost moves first, as in a
 * mechanical counter. Up rolls in from below, down from above. */
.rvds-roll { display: inline-flex; overflow: hidden; vertical-align: bottom; font-variant-numeric: tabular-nums; }
.rvds-roll__slot { position: relative; display: inline-block; }
.rvds-roll__new { display: inline-block; animation: rvds-roll-in var(--duration-base) var(--ease) both; animation-delay: var(--rvds-roll-delay, 0ms); }
.rvds-roll__old { position: absolute; left: 0; top: 0; animation: rvds-roll-out var(--duration-base) var(--ease) both; animation-delay: var(--rvds-roll-delay, 0ms); }
.rvds-roll__slot[data-dir="down"] > .rvds-roll__new { --rvds-roll-from: calc(-1 * var(--roll-offset)); }
.rvds-roll__slot[data-dir="down"] > .rvds-roll__old { --rvds-roll-to: var(--roll-offset); }
.rvds-roll__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Stepper: the value rolls inside its pill, and on reaching a bound the pill
   knocks once against the end stop. */
.rvds-stepper--up > .rvds-stepper__value { animation: rvds-roll-in var(--duration-base) var(--ease); }
.rvds-stepper--down > .rvds-stepper__value { --rvds-roll-from: calc(-1 * var(--roll-offset)); animation: rvds-roll-in-b var(--duration-base) var(--ease); }
.rvds-stepper--stop-max { animation: rvds-stop-max var(--duration-release) var(--ease-settle); }
.rvds-stepper--stop-min { animation: rvds-stop-min var(--duration-release) var(--ease-settle); }
.rvds-stepper__nudge:disabled { transition: background var(--duration-base), --rvds-fill var(--duration-base), color var(--duration-base), opacity var(--duration-base); opacity: 0.6; }

/* ===========================================================================
 * 7. Button: busy and done
 * ======================================================================== */

/*   <button class="rvds-button" aria-busy="true">Speichern</button>
 *   <button class="rvds-button" data-state="done">Speichern</button>
 * The label turns transparent and keeps its width, so nothing beside the
 * button moves; a spinner, then a drawn tick, sit in its place. Set by
 * RevoltarisDS.busy(button, promise), which also holds the spinner for at
 * least 400ms so a fast answer does not flash. */
.rvds-button { --rvds-button-ink: var(--gold); position: relative; }
.rvds-button--ghost { --rvds-button-ink: var(--fg); }
.rvds-button--solid { --rvds-button-ink: var(--on-accent); }
.rvds-button[aria-busy="true"],
.rvds-button[data-state="done"] { color: transparent !important; pointer-events: none; }
.rvds-button[aria-busy="true"] > svg,
.rvds-button[data-state="done"] > svg { opacity: 0 !important; }
.rvds-button[aria-busy="true"]::after,
.rvds-button[data-state="done"]::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
}
.rvds-button[aria-busy="true"]::after {
  border-radius: var(--radius-pill);
  border: 2px solid rgb(from var(--rvds-button-ink) r g b / 0.25);
  border-top-color: var(--rvds-button-ink);
  animation: revoltaris-spin 700ms linear infinite, rvds-fade-in var(--duration-fast) var(--ease) both;
}
.rvds-button[data-state="done"]::after {
  background: var(--rvds-button-ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
  animation: rvds-draw var(--duration-draw) var(--ease) both;
}

/* ===========================================================================
 * 8. Tabs and segments - one line travels
 * ======================================================================== */

/* interactions.js carries the line from the old tab to the new one; here it
   only has to know that a carried line must not also sweep. */
.rvds-tabs__line--carried { transition: none !important; }

/*   <div class="rvds-segmented" role="radiogroup">
 *     <span class="rvds-segmented__thumb" aria-hidden="true"></span>
 *     <button class="rvds-segmented__option" role="radio" aria-checked="true">Monat</button> …
 *   </div>
 * The plain-HTML twin of SegmentedControl: one thumb glides under the
 * chosen option and takes its width. */
.rvds-segmented {
  position: relative;
  display: inline-flex;
  box-sizing: border-box;
  height: var(--control-height);
  padding: 3px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--field-bg, var(--fill-1));
  box-shadow: var(--control-inset);
  isolation: isolate;
}
.rvds-segmented__thumb {
  position: absolute;
  z-index: -1;
  top: 3px;
  bottom: 3px;
  left: 0;
  width: var(--rvds-seg-w, 0px);
  transform: translateX(var(--rvds-seg-x, 0px));
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  --rvds-fill: var(--fill-4);
  background: linear-gradient(var(--rvds-fill), var(--rvds-fill)) var(--surface-tint);
  box-shadow: var(--control-raised);
  transition: transform var(--duration-morph) var(--ease-move), width var(--duration-morph) var(--ease-move);
}
.rvds-segmented--still > .rvds-segmented__thumb { transition: none; }
.rvds-segmented__option {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: none;
  cursor: pointer;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: var(--text-13);
  font-weight: var(--weight-medium);
  color: var(--mut-2);
  white-space: nowrap;
  transition: color var(--duration-base);
}
@media (hover: hover) { .rvds-segmented__option:hover { color: var(--mut); } }
.rvds-segmented__option[aria-checked="true"] { color: var(--state-ink); font-weight: var(--weight-semibold); }

/* ===========================================================================
 * 9. Glass
 * ======================================================================== */

/*   <div class="rvds-glass">…</div>
 * Floating chrome only: the mega panel, popovers, the action bar, a toast. Never a card - cards stay flat.
 * The fill is thin; the blur, a slight lift of what is behind and a sheen
 * at the top carry it. The edge is a gradient hairline (lit above, fading
 * below) drawn by ::before, and ::after is the same hairline lit in gold
 * where the pointer is, set by interactions.js through --rvds-mx / --rvds-my. */
.rvds-glass {
  position: relative;
  isolation: isolate;
  border: 0;
  background: var(--glass-sheen), var(--glass-bg);
  -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(var(--saturate-nav)) brightness(var(--glass-brightness));
  backdrop-filter: blur(var(--blur-glass)) saturate(var(--saturate-nav)) brightness(var(--glass-brightness));
  box-shadow: var(--shadow-float);
}
.rvds-glass::before,
.rvds-glass::after,
.rvds-spotlight::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.rvds-glass::before { background: linear-gradient(180deg, var(--glass-edge-top), var(--glass-edge-bottom)); }
.rvds-glass::after,
.rvds-spotlight::after {
  background: radial-gradient(180px circle at var(--rvds-mx, 50%) var(--rvds-my, -200px), var(--glass-light), transparent 70%);
  opacity: 0;
  transition: opacity var(--duration-image) var(--ease);
}
.rvds-spotlight { position: relative; }
@media (hover: hover) and (pointer: fine) {
  .rvds-glass--lit:hover::after,
  .rvds-spotlight:hover::after { opacity: 1; }
}

/* ===========================================================================
 * 10. Nav bar - sticky, fits itself
 * ======================================================================== */

/*   <nav class="rvds-nav-bar rvds-nav-bar--sticky" data-rvds-mega>…</nav>
 * The bar stays the full-width blurred strip at the top edge. `--sticky`
 * keeps it in the flow and pins it while the page scrolls (a specimen frame,
 * a page whose bar must push content down); without it the bar stays fixed
 * as before.
 *
 * It never overflows (0.13.0 rule). interactions.js measures the row, as
 * NavBar.jsx does - no breakpoint can know the labels - and folds in steps
 * until it fits: the flat links (--fold-flat), then the actions marked
 * data-nav-yield (--tight), then every link (--fold-links; the menu carries
 * them). Before the script runs, a too-long row clips at its end, never at
 * its start. */
.rvds-nav-bar--sticky { position: sticky; top: 0; }
.rvds-nav-bar__links { min-width: 0; overflow: clip; overflow-clip-margin: 12px; justify-content: safe center; }
.rvds-nav-bar--fold-flat .rvds-nav-bar__links > .rvds-nav-bar__link { display: none; }
/* With only flat links, the folded centre is empty: it takes no gaps either. */
.rvds-nav-bar--fold-flat .rvds-nav-bar__links:not(:has(> .rvds-nav-bar__trigger, > .rvds-nav-bar__group)) { display: none; }
/* Before anything else folds, the labels move closer: less padding, no gap. */
.rvds-nav-bar--compact .rvds-nav-bar__links { gap: 0; }
.rvds-nav-bar--compact :is(.rvds-nav-bar__link, .rvds-nav-bar__trigger) { padding-inline: 9px; }
/* Once the flat links and the yielding actions are gone, the groups draw in
   once more before they fold into the menu as well. */
.rvds-nav-bar--snug .rvds-nav-bar__trigger { padding-inline: 6px; gap: 2px; }
.rvds-nav-bar--snug .rvds-nav-bar__end { gap: 6px !important; }
.rvds-nav-bar--fold-links .rvds-nav-bar__links { display: none; }
/* The last step needs its replacement: a bar that can fold all its links
   carries a menu button (MenuButton), shown only once they are gone. */
.rvds-nav-bar__menu-button { display: none !important; }
:is(.rvds-nav-bar--fold-flat, .rvds-nav-bar--tight, .rvds-nav-bar--fold-links) .rvds-nav-bar__menu-button { display: inline-flex !important; }
/* Last of all the booking action shrinks, the mark never does (0.14.0): first
   to its short label (--cta-short), then to its glyph (--cta-icon), each only
   when the surface supplied it. One link with three faces; the link keeps the
   full label as its accessible name.
     <a class="rvds-button rvds-button--solid rvds-nav-bar__cta" href="…" aria-label="Jetzt anfragen">
       <span class="rvds-nav-bar__cta-full">Jetzt anfragen</span>
       <span class="rvds-nav-bar__cta-short" aria-hidden="true">Anfragen</span>
       <span class="rvds-nav-bar__cta-icon" aria-hidden="true"><svg …calendar-plus, 16px…></svg></span>
     </a> */
.rvds-nav-bar__cta-short,
.rvds-nav-bar__cta-icon { display: none; }
/* On a phone - once every link has folded into the menu - the short label
   shows whether or not the long one would fit (Alex, 14.10.2026). */
.rvds-nav-bar:is(.rvds-nav-bar--cta-short, .rvds-nav-bar--fold-links) .rvds-nav-bar__cta:has(> .rvds-nav-bar__cta-short) > .rvds-nav-bar__cta-full { display: none; }
.rvds-nav-bar:is(.rvds-nav-bar--cta-short, .rvds-nav-bar--fold-links):not(.rvds-nav-bar--cta-icon) .rvds-nav-bar__cta-short { display: inline; }
.rvds-nav-bar--cta-icon .rvds-nav-bar__cta :is(.rvds-nav-bar__cta-full, .rvds-nav-bar__cta-short) { display: none; }
.rvds-nav-bar--cta-icon .rvds-nav-bar__cta-icon { display: inline-flex; }
/* A square of the control height - as a minimum, so the glyph face is as wide as its content like any button. */
.rvds-nav-bar--cta-icon .rvds-nav-bar__cta { min-width: var(--control-height); padding-inline: 0; justify-content: center; }
.rvds-nav-bar__yield { display: inline-flex; }

/* Search in the bar of a content surface (0.16.0): the CommandPalette's
   trigger in the right-hand group, as tall as everything there
   (--control-height). Once the bar folds (--fold-flat on), only the glyph
   stays, without a frame, like a bare IconButton. Mark it data-nav-yield and
   put a search into the menu: when even the folded bar runs out of room
   (--tight) it steps aside.
     <button type="button" class="rvds-command-trigger rvds-nav-bar__search" data-rvds-command="suche" aria-label="Suchen">
       <svg …search, 16px…></svg><span class="rvds-command-trigger__text">Suchen</span><kbd class="rvds-kbd" data-rvds-kbd-mod>⌘K</kbd>
     </button> */
.rvds-command-trigger.rvds-nav-bar__search { height: var(--control-height); min-width: 180px; padding-inline: 12px; font-size: var(--text-14); }
.rvds-nav-bar:is(.rvds-nav-bar--fold-flat, .rvds-nav-bar--fold-links) .rvds-nav-bar__search {
  min-width: var(--control-height);
  padding: 0;
  justify-content: center;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  color: var(--fg);
}
.rvds-nav-bar:is(.rvds-nav-bar--fold-flat, .rvds-nav-bar--fold-links) .rvds-nav-bar__search > :is(.rvds-command-trigger__text, .rvds-kbd) { display: none; }
/* Folded to its glyph it keeps the keyboard glow. */
.rvds-nav-bar:is(.rvds-nav-bar--fold-flat, .rvds-nav-bar--fold-links) .rvds-nav-bar__search:focus-visible { box-shadow: var(--focus-glow); }

/* A second row in the bar's glass (0.16.0): a filter that belongs to what
   is in view - the shop's vehicle choice and VIN card while products are on
   screen. It opens by its rows (grid 0fr to 1fr, --duration-accordion), its
   content arrives from opacity and blur without travel, and it leaves faster
   than it came. Same measure as the row above. Closed it is inert.
     <nav class="rvds-nav-bar …">
       <div class="rvds-nav-bar__inner">…</div>
       <div class="rvds-nav-bar__row" data-rvds-nav-row="produkte" inert>
         <div class="rvds-nav-bar__row-clip"><div class="rvds-nav-bar__row-inner">…</div></div>
       </div>
     </nav>
   data-rvds-nav-row="<id>": interactions.js opens the row while that element
   is in view (its top above 60% of the screen, its bottom more than 160px
   below the bar) and sets rvds-nav-bar--row-open on the bar; the next scroll
   decides again. RevoltarisDS.navRow(nav, true) opens it from a script - for
   a row without data-rvds-nav-row it stays until the script closes it. A
   page that starts with the watched element offsets its top by --nav-height
   plus --nav-row-height (tokens/spacing.css), the height of a row of fields;
   a row with taller content is measured by the page. */
.rvds-nav-bar__row { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--duration-accordion) var(--ease-move); }
.rvds-nav-bar__row-clip { min-height: 0; overflow: hidden; }
.rvds-nav-bar__row-inner {
  box-sizing: border-box;
  width: 100%;
  margin-inline: auto;
  padding: var(--space-5) var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5) var(--space-8);
  border-top: 1px solid var(--line);
  opacity: 0;
  filter: blur(8px);
  visibility: hidden;
  transition: opacity var(--duration-exit) var(--ease-exit), filter var(--duration-exit) var(--ease-exit), visibility 0s linear var(--duration-exit);
}
@media (min-width: 1200px) { .rvds-nav-bar__row-inner { width: var(--container-width-wide); padding-inline: 0; } }
@media (max-width: 899px) { .rvds-nav-bar__row-inner { gap: var(--space-3); } }
.rvds-nav-bar--row-open .rvds-nav-bar__row { grid-template-rows: 1fr; }
.rvds-nav-bar--row-open .rvds-nav-bar__row-inner {
  opacity: 1;
  filter: none;
  visibility: visible;
  transition: opacity var(--duration-base) var(--ease) 120ms, filter var(--duration-base) var(--ease) 120ms, visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .rvds-nav-bar__row, .rvds-nav-bar__row-inner { transition: none; }
  .rvds-nav-bar__row-inner { filter: none; }
}

/* An open menu under the bar (0.16.0, Alex 15.10.2026): the bar takes the
   menu's surface (--bg-1), so bar and menu read as one sheet, and fades back
   into glass when it closes. Keyed to the MenuButton's aria-expanded, which
   NavBar.jsx and interactions.js keep, so it holds for a MobileHeader with
   data-rvds-menu-bar as well. */
:is(.rvds-nav-bar, [data-rvds-menu-bar]) { transition: background-color var(--duration-base) var(--ease); }
:is(.rvds-nav-bar, [data-rvds-menu-bar]):has(.rvds-menu-button[aria-expanded="true"]) { background-color: var(--bg-1); }
@media (prefers-reduced-motion: reduce) { :is(.rvds-nav-bar, [data-rvds-menu-bar]) { transition: none; } }
/* A link that leaves the site (the shop is its own site) says so with a
   small arrow up-right that nudges along its own diagonal on hover. */
.rvds-nav-bar__external { display: inline-flex; margin-left: 3px; opacity: 0.6; transform: translate(0, 0); transition: transform var(--duration-fast) var(--ease), opacity var(--duration-fast); }
@media (hover: hover) and (pointer: fine) {
  .rvds-nav-bar__link:hover > .rvds-nav-bar__external { opacity: 1; transform: translate(1.5px, -1.5px); }
}

/* One veil follows the pointer from link to link instead of each link
   lighting on its own - it slides sideways, never up or down. The first
   arrival places it without travel. */
.rvds-nav-bar__links { position: relative; }
.rvds-nav-bar__veil {
  position: absolute;
  z-index: 0;
  left: 0;
  top: 50%;
  height: 36px;
  margin-top: -18px;
  width: var(--rvds-veil-w, 0px);
  transform: translateX(var(--rvds-veil-x, 0px));
  border-radius: var(--radius-md);
  background: var(--hover-veil);
  opacity: 0;
  pointer-events: none;
  transition: transform var(--duration-base) var(--ease-move), width var(--duration-base) var(--ease-move), opacity var(--duration-fast);
}
.rvds-nav-bar__veil--on { opacity: 1; }
.rvds-nav-bar__veil--jump { transition: opacity var(--duration-fast); }
.rvds-nav-bar__links:has(> .rvds-nav-bar__veil) > :is(.rvds-nav-bar__link, .rvds-nav-bar__trigger) { z-index: 1; }
.rvds-nav-bar__links:has(> .rvds-nav-bar__veil) > .rvds-nav-bar__trigger[aria-expanded="true"] { background: transparent; }
@media (hover: hover) {
  .rvds-nav-bar__links:has(> .rvds-nav-bar__veil) > :is(.rvds-nav-bar__link, .rvds-nav-bar__trigger):hover { background: transparent; }
}
.rvds-nav-bar__trigger > .rvds-nav-bar__chevron { transition: transform var(--duration-base) var(--ease); }

/* ===========================================================================
 * 11. Mega panel
 * ======================================================================== */

/*   <nav class="rvds-nav-bar" data-rvds-mega>
 *     <div class="rvds-nav-bar__inner">…
 *       <button class="rvds-nav-bar__trigger" aria-expanded="false" aria-controls="m-leistungen" data-rvds-mega-trigger>…</button>
 *     …</div>
 *     <div class="rvds-mega">
 *       <div class="rvds-mega__surface rvds-glass">
 *         <div class="rvds-mega__viewport">
 *           <div class="rvds-mega__pane" id="m-leistungen">…items, a feature…</div>
 *           <div class="rvds-mega__pane" id="m-shop">…</div>
 *         </div>
 *       </div>
 *     </div>
 *   </nav>
 *
 * One panel for every group. Opening grows it from the bar; moving to
 * another group keeps it open and morphs it: the height glides to the new
 * content, the old content leaves towards where the pointer came from and
 * the new arrives from where it is going. Pointer intent: a pointer heading
 * down into the open panel crosses other triggers without switching. */
.rvds-mega {
  position: absolute;
  z-index: 410;
  left: 0;
  right: 0;
  top: 100%;
  box-sizing: border-box;
  width: 100%;
  margin-inline: auto;
  padding: 8px var(--gutter) 0;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear var(--duration-exit);
}
.rvds-mega--open { visibility: visible; pointer-events: auto; transition: visibility 0s; }
.rvds-mega__surface {
  overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--glass-sheen), var(--menu-bg);
  opacity: 0;
  transform: scale(0.985);
  transform-origin: 50% 0;
  transition: opacity var(--duration-exit) var(--ease-exit), transform var(--duration-exit) var(--ease-exit);
}
.rvds-mega--open > .rvds-mega__surface {
  opacity: 1;
  transform: none;
  transition: opacity var(--duration-float) var(--ease), transform var(--duration-morph) var(--ease);
}
.rvds-mega__viewport { position: relative; transition: height var(--duration-morph) var(--ease-move); }
.rvds-mega__pane {
  position: absolute;
  inset: 0 0 auto 0;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(240px, 0.85fr);
  gap: var(--space-3, 8px) var(--space-6, 18px);
  padding: 14px;
  opacity: 0;
  visibility: hidden;
  transform: translateX(var(--rvds-mega-shift, 0px));
  transition:
    opacity var(--duration-exit) var(--ease-exit),
    transform var(--duration-morph) var(--ease-move),
    visibility 0s linear var(--duration-morph);
}
.rvds-mega__pane--active {
  position: relative;
  opacity: 1;
  visibility: visible;
  transform: none;
  transition:
    opacity var(--duration-base) var(--ease) 60ms,
    transform var(--duration-morph) var(--ease-move),
    visibility 0s;
}
.rvds-mega--still .rvds-mega__pane,
.rvds-mega--still .rvds-mega__viewport { transition: none !important; }
.rvds-mega__column { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rvds-mega__label { padding: 6px 12px 8px; font-size: 11.5px; font-weight: var(--weight-bold); letter-spacing: 0.04em; color: var(--text-label); }
.rvds-mega__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-md); /* a row, not a card: one step below the surface radius (0.17.0) */
  color: var(--fg);
  text-decoration: none;
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant);
}
.rvds-mega__item .rvds-icon-tile { --rvds-icon-tile-size: 34px; transition: border-color var(--duration-base), background var(--duration-base), --rvds-fill var(--duration-base); }
.rvds-mega__item-title { display: block; font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-mega__item-text { display: block; margin-top: 2px; font-size: 12.5px; line-height: 1.4; color: var(--mut-2); }
.rvds-mega__arrow { color: var(--gold); opacity: 0; transform: translateX(-4px); }
@media (hover: hover) and (pointer: fine) {
  .rvds-mega__item:hover { background: var(--hover-veil); }
  .rvds-mega__item:hover .rvds-icon-tile { border-color: var(--gold-a40); background: var(--gold-a14); }
  .rvds-mega__item:hover > .rvds-mega__arrow { opacity: 1; transform: none; }
}
.rvds-mega__item:focus-visible > .rvds-mega__arrow { opacity: 1; transform: none; }
.rvds-mega__feature {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 4px;
  min-height: 220px;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--bg-2);
  padding: 16px;
  color: var(--fg);
  text-decoration: none;
}
.rvds-mega__feature-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.rvds-mega__feature-media > * { width: 100%; height: 100%; object-fit: cover; transform: scale(1); transition: transform var(--duration-image) var(--ease); }
.rvds-mega__feature::after { content: ""; position: absolute; inset: 0; z-index: 0; background: var(--wash-image-foot, linear-gradient(180deg, transparent 30%, rgb(from var(--bg) r g b / 0.9))); }
.rvds-mega__feature > :not(.rvds-mega__feature-media) { position: relative; z-index: 1; }
.rvds-mega__feature-title { font-size: var(--text-16); font-weight: var(--weight-semibold); }
.rvds-mega__feature-text { font-size: 12.5px; color: var(--mut); }
@media (hover: hover) and (pointer: fine) {
  .rvds-mega__feature:hover .rvds-mega__feature-media > * { transform: scale(var(--hover-zoom)); }
}
.rvds-mega__foot {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 4px -14px -14px;
  padding: 12px 26px;
  border-top: 1px solid var(--line);
  background: var(--fill-1);
  font-size: var(--text-13);
  color: var(--mut-2);
}
/* The panel's own children arrive one after another the first time it opens. */
.rvds-mega--opening .rvds-mega__pane--active > * { animation: rvds-arrive var(--duration-morph) var(--ease) both; }
.rvds-mega--opening .rvds-mega__pane--active > :nth-child(2) { animation-delay: var(--stagger-step); }
.rvds-mega--opening .rvds-mega__pane--active > :nth-child(3) { animation-delay: calc(2 * var(--stagger-step)); }
.rvds-mega--opening .rvds-mega__pane--active > :nth-child(4) { animation-delay: calc(3 * var(--stagger-step)); }
@media (min-width: 1200px) { .rvds-mega { width: var(--container-width-wide); padding-inline: 0; } }
@media (max-width: 899px) { .rvds-mega { display: none; } }

/* ===========================================================================
 * 12. Floating UI - tooltip and popover
 * ======================================================================== */

/*   <button class="rvds-icon-button" aria-label="Herunterladen" data-rvds-tip>…</button>
 *   <button class="rvds-button rvds-button--ghost" data-rvds-popover="aktionen" aria-expanded="false">…</button>
 *   <div class="rvds-float rvds-float--menu rvds-glass" id="aktionen" role="menu" hidden>
 *     <button class="rvds-float__item" role="menuitem">…</button>
 *   </div>
 *
 * Placed by interactions.js against its anchor: preferred side first, the
 * opposite side if it does not fit, then shifted inside the viewport (8px
 * margin). It grows out of the side facing the anchor. A tooltip waits for
 * --tip-delay; after one has closed, the next opens without the wait for
 * --tip-warm - a toolbar can be read glyph by glyph. */
.rvds-float {
  position: fixed;
  z-index: 800;
  top: 0;
  left: 0;
  box-sizing: border-box;
  opacity: 0;
  transform: scale(var(--float-scale));
  transform-origin: var(--rvds-float-origin, 50% 0);
  transition: opacity var(--duration-exit) var(--ease-exit), transform var(--duration-exit) var(--ease-exit);
}
.rvds-float[hidden] { display: none; }
.rvds-float--open { opacity: 1; transform: none; transition: opacity var(--duration-float) var(--ease), transform var(--duration-float) var(--ease); }
.rvds-float--instant { transition: none !important; }
.rvds-float--tip {
  max-width: 240px;
  padding: 6px 10px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line-2);
  background: var(--menu-bg);
  box-shadow: var(--shadow-menu);
  font-size: 12px;
  line-height: 1.35;
  font-weight: var(--weight-medium);
  color: var(--fg);
  pointer-events: none;
}
.rvds-float__kbd {
  margin-left: 8px;
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-2);
  font-size: 10.5px;
  color: var(--mut-2);
}
.rvds-float--menu { min-width: 220px; padding: 6px; border-radius: var(--radius-xl); }
.rvds-float__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  padding: 9px 10px;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--mut);
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant), color var(--duration-instant);
}
.rvds-float__item > svg { flex: none; opacity: 0.8; }
.rvds-float__item-hint { margin-left: auto; font-size: 11.5px; color: var(--dim); }
@media (hover: hover) { .rvds-float__item:hover { background: var(--hover-veil); color: var(--fg); } }
.rvds-float__item:focus-visible { background: var(--hover-veil); color: var(--fg); }
.rvds-float__item--danger { color: var(--mut-2); }
@media (hover: hover) { .rvds-float__item--danger:hover { background: var(--danger-surface); color: var(--danger-text); } }
.rvds-float__divider { height: 1px; margin: 6px 4px; background: var(--line); }
.rvds-float__label { padding: 10px 10px 4px; font-size: 11.5px; font-weight: var(--weight-bold); letter-spacing: 0.04em; color: var(--text-label); }
.rvds-float--scroll { max-height: min(70vh, 560px); overflow-y: auto; overscroll-behavior: contain; }

/* ===========================================================================
 * 13. Action bar - selection floats its actions
 * ======================================================================== */

/*   <div data-rvds-select-scope>
 *     <input type="checkbox" data-rvds-select-all …>
 *     …rows with <input type="checkbox" data-rvds-select …>…
 *     <div class="rvds-action-bar rvds-glass" data-rvds-action-bar role="toolbar" aria-label="Auswahl">
 *       <span class="rvds-action-bar__count"><b class="rvds-roll" data-rvds-select-count>0</b> ausgewählt</span>
 *       <span class="rvds-action-bar__actions">…buttons…</span>
 *       <button class="rvds-icon-button rvds-icon-button--bare rvds-action-bar__clear" aria-label="Auswahl aufheben" data-rvds-select-clear>…x…</button>
 *     </div>
 *   </div>
 *
 * Appears with the first selection, leaves with the last. Shift-click
 * selects the range from the last row clicked; "select all" shows the
 * partial state. The count rolls. `--contained` keeps it inside its scope
 * (a specimen, a panel) instead of the viewport. */
.rvds-action-bar {
  position: fixed;
  z-index: 650;
  left: 50%;
  bottom: calc(18px + env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 14px;
  box-sizing: border-box;
  max-width: calc(100% - 24px);
  padding: 6px 6px 6px 18px;
  border-radius: var(--radius-pill);
  opacity: 0;
  visibility: hidden;
  transform: translateX(-50%) translateY(calc(100% + 24px)) scale(0.98);
  transition:
    opacity var(--duration-exit) var(--ease-exit),
    transform var(--duration-exit) var(--ease-exit),
    visibility 0s linear var(--duration-exit);
}
.rvds-action-bar--contained { position: absolute; bottom: 16px; }
.rvds-action-bar--open {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%);
  transition:
    opacity var(--duration-overlay) var(--ease),
    transform var(--duration-morph) var(--ease),
    visibility 0s;
}
.rvds-action-bar__count { display: inline-flex; align-items: baseline; gap: 6px; white-space: nowrap; font-size: var(--text-13); color: var(--mut); }
.rvds-action-bar__count > b { font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-action-bar__actions { display: flex; align-items: center; gap: 8px; }
.rvds-action-bar__sep { width: 1px; align-self: stretch; margin: 6px 0; background: var(--line-2); }
.rvds-select-row--selected { background: var(--state-surface); }
.rvds-list-row.rvds-select-row--selected { box-shadow: inset 2px 0 0 var(--gold); }

/* ===========================================================================
 * 14. Arrivals - toast, sheet, dialog, accordion
 * ======================================================================== */

/* Toast: rises a little and grows into place; a timed one shows its time as
   a hairline that empties, and holds while the pointer or focus is on it. */
.rvds-toast { position: relative; overflow: hidden; animation: rvds-toast-in var(--duration-morph) var(--ease) both; }
.rvds-toast__timer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: currentColor;
  opacity: 0.5;
  transform-origin: left;
  animation: rvds-timer var(--rvds-toast-time, 5s) linear both;
}
.rvds-toast--info > .rvds-toast__timer { color: var(--info); }
.rvds-toast--success > .rvds-toast__timer { color: var(--success); }
.rvds-toast--danger > .rvds-toast__timer { color: var(--danger); }
.rvds-toast:not([class*="rvds-toast--"]) > .rvds-toast__timer { color: var(--gold); }
.rvds-toast:focus-within > .rvds-toast__timer { animation-play-state: paused; }
@media (hover: hover) {
  .rvds-toast:hover > .rvds-toast__timer { animation-play-state: paused; }
}
.rvds-toast--leaving {
  animation: none;
  opacity: 0;
  transform: scale(0.97);
  transition:
    opacity var(--duration-exit) var(--ease-exit),
    transform var(--duration-exit) var(--ease-exit),
    height var(--duration-base) var(--ease-move) 60ms,
    padding var(--duration-base) var(--ease-move) 60ms,
    margin var(--duration-base) var(--ease-move) 60ms;
}

/* Sheets and dialogs on the Overlay frame. The frame switches from none to
   flex, so @starting-style is the first frame. */
.rvds-overlay--open > .rvds-overlay__backdrop { opacity: 1; transition: opacity var(--duration-overlay) var(--ease); }
.rvds-overlay--open > .rvds-bottom-sheet { transform: none; transition: transform var(--duration-morph) var(--ease); }
.rvds-overlay--open:not(.rvds-overlay--bottom):not(.rvds-overlay--right):not(.rvds-lightbox) > [role="dialog"] {
  opacity: 1;
  transform: none;
  transition: opacity var(--duration-overlay) var(--ease), transform var(--duration-overlay) var(--ease);
}
@starting-style {
  .rvds-overlay--open > .rvds-overlay__backdrop { opacity: 0; }
  .rvds-overlay--open > .rvds-bottom-sheet { transform: translateY(var(--sheet-offset)); }
  .rvds-overlay--open:not(.rvds-overlay--bottom):not(.rvds-overlay--right):not(.rvds-lightbox) > [role="dialog"] { opacity: 0; transform: scale(var(--float-scale)); }
}

/* FAQ: the answer fades in once the row has begun to open, and fades out
   before it closes - the text never slides under a moving edge. */
.rvds-faq-item__inner { opacity: 1; transition: opacity var(--duration-base) var(--ease) 90ms; }
.rvds-faq-item--collapsible > .rvds-faq-item__toggle[aria-expanded="false"] + .rvds-faq-item__panel > .rvds-faq-item__inner,
.rvds-faq-item--collapsible:has(> .rvds-faq-item__heading > .rvds-faq-item__toggle[aria-expanded="false"]) > .rvds-faq-item__panel > .rvds-faq-item__inner {
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-exit);
}

/* Disclosure and the collapsible Panel are native <details>; where the
   browser can animate to auto, they open as the FAQ does. */
@supports (interpolate-size: allow-keywords) {
  .rvds-disclosure,
  .rvds-panel--collapsible { interpolate-size: allow-keywords; }
  .rvds-disclosure::details-content,
  .rvds-panel--collapsible::details-content {
    block-size: 0;
    overflow: clip;
    opacity: 0;
    transition:
      block-size var(--duration-accordion) var(--ease),
      opacity var(--duration-fast) var(--ease-exit),
      content-visibility var(--duration-accordion) allow-discrete;
  }
  .rvds-disclosure[open]::details-content,
  .rvds-panel--collapsible[open]::details-content {
    block-size: auto;
    opacity: 1;
    transition:
      block-size var(--duration-accordion) var(--ease),
      opacity var(--duration-base) var(--ease) 90ms,
      content-visibility var(--duration-accordion) allow-discrete;
  }
}

/* ===========================================================================
 * Keyframes
 * ======================================================================== */

@keyframes rvds-arrive { from { opacity: 0; transform: translateY(-3px); } }
@keyframes rvds-fade-in { from { opacity: 0; } }
@keyframes rvds-dash { from { transform: scaleX(0); } }
@keyframes rvds-draw { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes rvds-roll-in { from { transform: translateY(var(--rvds-roll-from, var(--roll-offset))); opacity: 0; } }
@keyframes rvds-roll-in-b { from { transform: translateY(var(--rvds-roll-from, var(--roll-offset))); opacity: 0; } }
@keyframes rvds-roll-out { to { transform: translateY(var(--rvds-roll-to, calc(-1 * var(--roll-offset)))); opacity: 0; } }
@keyframes rvds-stop-max { 35% { transform: translateX(var(--stop-bump)); } }
@keyframes rvds-stop-min { 35% { transform: translateX(calc(-1 * var(--stop-bump))); } }
@keyframes rvds-toast-in { from { opacity: 0; transform: translateY(8px) scale(0.97); } }
@keyframes rvds-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ===========================================================================
 * Reduced motion - colour stays, travel goes
 * ======================================================================== */

@media (prefers-reduced-motion: reduce) {
  .rvds-button, .rvds-icon-button, .rvds-pagination__page, .rvds-service-card__cta, .rvds-status-link,
  .rvds-stepper__nudge, .rvds-action-bar__clear, .rvds-checkbox__box, .rvds-radio__box { transform: none !important; }
  .rvds-roll__new, .rvds-roll__old, .rvds-stepper, .rvds-stepper__value, .rvds-toast,
  .rvds-field__hint--error, .rvds-mega__pane > * { animation: none !important; }
  .rvds-roll__old { display: none; }
  .rvds-checkbox__box > svg { transition: none !important; }
  .rvds-segmented__thumb, .rvds-nav-bar__veil, .rvds-mega__viewport, .rvds-mega__pane, .rvds-mega__surface,
  .rvds-float, .rvds-action-bar, .rvds-switch__knob, .rvds-list-row__chevron, .rvds-mega__arrow, .rvds-nav-bar__external { transition-property: opacity, background, color, border-color !important; }
  .rvds-mega__pane, .rvds-mega__surface, .rvds-float, .rvds-action-bar--open { transform: none; }
  .rvds-action-bar:not(.rvds-action-bar--open) { transform: translateX(-50%); }
  .rvds-toast__timer { animation-duration: var(--rvds-toast-time, 5s) !important; }
}

/* ===========================================================================
 * 15. Second round (0.14.0) - small things people touch every day
 * ======================================================================== */

/* Text link. A faint line at rest says "link"; on hover the full line draws
   in from the left and, on leave, retracts to the right - it never just
   blinks. */
.rvds-link {
  color: var(--fg);
  text-decoration: none;
  padding-bottom: 1px;
  background:
    linear-gradient(currentColor, currentColor) no-repeat 100% 100% / 0 1px,
    linear-gradient(var(--line-3), var(--line-3)) no-repeat 0 100% / 100% 1px;
  transition: background-size var(--duration-base) var(--ease), color var(--duration-base);
}
@media (hover: hover) {
  .rvds-link:hover { color: var(--fg); background-position: 0 100%, 0 100%; background-size: 100% 1px, 100% 1px; }
}
.rvds-link:focus-visible { outline: none; border-radius: 4px; box-shadow: var(--focus-glow); }

/* Copy. The glyph turns into a tick in place - the button never changes
   size - and the tooltip says what happened.
   <button class="rvds-icon-button rvds-copy" data-rvds-copy="CHE-308.960.626" aria-label="UID kopieren" data-rvds-tip data-rvds-tip-done="Kopiert">
     <span class="rvds-copy__glyph">…copy…</span><span class="rvds-copy__glyph rvds-copy__glyph--done">…check…</span>
   </button> */
.rvds-copy { display: inline-grid; place-items: center; }
.rvds-copy__glyph {
  grid-area: 1 / 1;
  display: inline-flex;
  transition: opacity var(--duration-fast) var(--ease), transform var(--duration-base) var(--ease-settle);
}
.rvds-copy__glyph--done { opacity: 0; transform: scale(0.5); color: var(--success); }
.rvds-copy[data-copied] > .rvds-copy__glyph:not(.rvds-copy__glyph--done) { opacity: 0; transform: scale(0.5); }
.rvds-copy[data-copied] > .rvds-copy__glyph--done { opacity: 1; transform: none; }

/* Clearable field. The clear button exists only while there is something to
   clear (pure CSS through :placeholder-shown; give the field a placeholder).
   Escape clears too.
   <span class="rvds-input rvds-input--inset rvds-input--clearable">
     <span class="rvds-input__icon">…</span>
     <input class="rvds-input__control" placeholder="…">
     <button type="button" class="rvds-input__clear" aria-label="Eingabe löschen" data-rvds-clear>…x…</button>
   </span> */
.rvds-input__clear {
  position: absolute;
  right: 7px;
  top: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-top: -12px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--fill-3);
  color: var(--mut);
  cursor: pointer;
  opacity: 0;
  transform: scale(0.6);
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease), transform var(--duration-base) var(--ease-settle), background var(--duration-instant), --rvds-fill var(--duration-instant), color var(--duration-instant);
}
.rvds-input--clearable:has(> .rvds-input__control:not(:placeholder-shown)) > .rvds-input__clear { opacity: 1; transform: none; pointer-events: auto; }
@media (hover: hover) { .rvds-input__clear:hover { background: var(--fill-5); color: var(--fg); } }
.rvds-input__clear:active { transform: scale(var(--press-scale-sm)); }
.rvds-input__clear:focus-visible { outline: none; box-shadow: var(--focus-glow); }

/* Counter under a field with a maxlength. It brightens while you type, turns
   to the error ink in the last tenth and knocks once at the limit.
   <textarea class="rvds-textarea" maxlength="280" data-rvds-count="anliegen-count">…
   <span class="rvds-field__foot"><span class="rvds-field__hint">…</span>
     <span class="rvds-field__count" id="anliegen-count"><b class="rvds-roll">0</b> / 280</span></span> */
.rvds-field__foot { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.rvds-field__count { flex: none; margin-left: auto; font-size: 12px; font-variant-numeric: tabular-nums; color: var(--dim); transition: color var(--duration-base); }
.rvds-field__count > b { font-weight: var(--weight-semibold); }
.rvds-field:focus-within .rvds-field__count { color: var(--mut-2); }
.rvds-field__count--warn,
.rvds-field:focus-within .rvds-field__count--warn { color: var(--field-error-text); }
.rvds-field__count--stop { animation: rvds-stop-max var(--duration-release) var(--ease-settle); }

/* Hold to confirm. A destructive action that has no undo is held, not
   clicked: the fill runs while the pointer or the key is down, drains back
   on release, and confirms when full - no dialog in between.
   <button class="rvds-button rvds-button--ghost rvds-button--hold" data-rvds-hold="900">
     <span class="rvds-button__hold" aria-hidden="true"></span>Halten zum Stornieren
   </button>
   Fires rvds:confirm on the button.
   Red by default (delete, cancel). A final action that is not dangerous -
   post, send, release - holds in gold: `rvds-button--hold-gold` (0.17.0). */
.rvds-button--hold { isolation: isolate; overflow: hidden; user-select: none; -webkit-user-select: none; touch-action: none; }
.rvds-button__hold {
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: rgb(from var(--danger) r g b / 0.30);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duration-morph) var(--ease-move);
}
.rvds-button--holding { border-color: var(--danger-border) !important; }
.rvds-button--holding > .rvds-button__hold { transform: scaleX(1); transition: transform var(--rvds-hold, 900ms) linear; }
.rvds-button--holding:active { transform: none; }
.rvds-button--hold-gold > .rvds-button__hold { background: var(--gold-a30); }
.rvds-button--hold-gold.rvds-button--holding { border-color: var(--gold) !important; }

/* Toast: swipe it away sideways. It follows the finger, fades with the
   distance, and goes past a third of its width or on a flick; otherwise it
   settles back. The timer holds while it is held. */
.rvds-toast { touch-action: pan-y; }
.rvds-toast--dragging { animation: none; cursor: grabbing; transition: none !important; }
.rvds-toast--dragging > .rvds-toast__timer { animation-play-state: paused; }
.rvds-toast--settling { transition: transform var(--duration-release) var(--ease-settle), opacity var(--duration-base) var(--ease); }

/* Arriving by an anchor: the target glows gold for a moment and fades, so
   the eye finds where the jump landed. */
:target:is(.rvds-faq-item, .rvds-section, [data-rvds-flash]) { animation: rvds-flash 1800ms var(--ease) both; }

@keyframes rvds-flash {
  0%, 25% { background-color: var(--gold-a08); box-shadow: inset 2px 0 0 var(--gold); }
  100% { background-color: transparent; box-shadow: inset 2px 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .rvds-copy__glyph, .rvds-input__clear { transition-property: opacity, background, color !important; transform: none !important; }
  .rvds-field__count--stop, :target:is(.rvds-faq-item, .rvds-section, [data-rvds-flash]) { animation: none !important; }
}

/* ===========================================================================
 * 16. ThemeToggle in plain HTML  (components/navigation/ThemeToggle.jsx)
 * ======================================================================== */

/*   <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-theme-toggle"
 *     data-rvds-theme-toggle data-to-light="Helles Design" data-to-dark="Dunkles Design"
 *     aria-label="Helles Design" data-rvds-tip>
 *     <span class="rvds-theme-toggle__glyph rvds-theme-toggle__glyph--sun">…lucide sun…</span>
 *     <span class="rvds-theme-toggle__glyph rvds-theme-toggle__glyph--moon">…lucide moon…</span>
 *   </button>
 *
 * The same control as ThemeToggle.jsx: a bare IconButton, the sun in the dark
 * theme (it switches to light), the moon in the light theme. Which glyph shows
 * is pure CSS on data-theme, so it is right before any script runs. The sun
 * turns away as the moon turns in - a rotation, never a slide. interactions.js
 * keeps the storage rules of the React component (revoltaris-theme, the device
 * setting beside it) and, where the browser has view transitions, lets the
 * page crossfade over --duration-base. A menu row can carry
 * data-rvds-theme-toggle too; its [data-rvds-theme-label] child gets the text. */
.rvds-theme-toggle { display: inline-grid; place-items: center; }
.rvds-theme-toggle__glyph {
  grid-area: 1 / 1;
  display: inline-flex;
  transition: opacity var(--duration-fast) var(--ease), transform var(--duration-morph) var(--ease);
}
.rvds-theme-toggle__glyph--moon { opacity: 0; transform: rotate(-90deg) scale(0.5); }
[data-theme="light"] .rvds-theme-toggle__glyph--sun { opacity: 0; transform: rotate(90deg) scale(0.5); }
[data-theme="light"] .rvds-theme-toggle__glyph--moon { opacity: 1; transform: none; }

/* The page itself changes by a short crossfade (Alex 14.10.2026: the circle
   reveal was far too much). Only the glyph turns. */
html.rvds-theme-vt::view-transition-old(root),
html.rvds-theme-vt::view-transition-new(root) { animation-duration: var(--duration-base); animation-timing-function: var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .rvds-theme-toggle__glyph { transition: opacity var(--duration-fast) !important; transform: none !important; }
}

/* ===========================================================================
 * 17. Mobile menu - levels, not a long list
 * ======================================================================== */

/*   <div class="rvds-overlay rvds-overlay--right rvds-mobile-menu-frame" id="menu">
 *     <div class="rvds-overlay__backdrop"></div>
 *     <div class="rvds-mobile-menu" role="dialog" aria-modal="true" aria-label="Menü">
 *       <div class="rvds-mobile-menu__levels">
 *         <section class="rvds-mobile-menu__level rvds-mobile-menu__level--active" id="m-root">
 *           <header class="rvds-mobile-menu__head">…title… <button data-rvds-close …>x</button></header>
 *           <div class="rvds-mobile-menu__body">
 *             <button class="rvds-mobile-menu__row" data-rvds-drill="m-service">Service <chevron></button>
 *             <a class="rvds-mobile-menu__row" href="…">Wissen</a>
 *           </div>
 *           <footer class="rvds-mobile-menu__foot">…CTA, ThemeToggle…</footer>
 *         </section>
 *         <section class="rvds-mobile-menu__level" id="m-service">
 *           <header class="rvds-mobile-menu__head"><button class="rvds-mobile-menu__back" data-rvds-drill-back>Zurück</button>…</header>
 *           …rows…
 *         </section>
 *       </div>
 *     </div>
 *   </div>
 *
 * On the Overlay frame (open, close, focus trap, Escape: components.js).
 * A group is a level, not an accordion: one decision per screen, big rows,
 * the way back always in the same place. Phone: the full screen. Tablet and
 * up: a 400px panel from the right edge over the page.
 * Two motions, one per platform feel, chosen by modifier:
 *   default      - the level slides in over its parent, which moves back by
 *                  a third and dims (the stack you came from stays visible);
 *   --axis       - the shared axis: the old level leaves 24px to the left as
 *                  the new one arrives 24px from the right, both fading.
 * Edge swipe from the left goes back one level; on the root it closes. */
.rvds-mobile-menu-frame { padding: 0; container: rvds-menu-frame / inline-size; overflow: clip; }
.rvds-mobile-menu {
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  background: var(--bg-1);
  overflow: clip;
}
/* Measured on the frame, not the viewport: a phone frame inside a desktop
   specimen stays a phone. */
@container rvds-menu-frame (min-width: 640px) {
  .rvds-mobile-menu { width: 400px; border-left: 1px solid var(--line-2); box-shadow: var(--shadow-menu); }
}
.rvds-overlay--open > .rvds-mobile-menu { transform: none; transition: transform var(--duration-morph) var(--ease); }
@starting-style { .rvds-overlay--open > .rvds-mobile-menu { transform: translateX(100%); } }
.rvds-mobile-menu__levels { position: relative; flex: 1; min-height: 0; }
.rvds-mobile-menu__level {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-1);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform var(--duration-morph) var(--ease-move), opacity var(--duration-morph) var(--ease-move), filter var(--duration-morph) var(--ease-move), visibility 0s linear var(--duration-morph);
}
.rvds-mobile-menu__level--active { transform: none; visibility: visible; transition-delay: 0s; z-index: 2; }
.rvds-mobile-menu__level--behind { transform: translateX(-30%); visibility: visible; filter: brightness(0.55); transition-delay: 0s; z-index: 1; }
.rvds-mobile-menu--axis .rvds-mobile-menu__level { transform: translateX(24px); opacity: 0; }
.rvds-mobile-menu--axis .rvds-mobile-menu__level--active { transform: none; opacity: 1; }
.rvds-mobile-menu--axis .rvds-mobile-menu__level--behind { transform: translateX(-24px); opacity: 0; filter: none; }
.rvds-mobile-menu__level--dragging { transition: none !important; }
.rvds-mobile-menu__head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
  min-height: 60px;
  padding: calc(8px + env(safe-area-inset-top)) 10px 8px 16px;
  border-bottom: 1px solid var(--line);
}
.rvds-mobile-menu__title { flex: 1; min-width: 0; margin: 0; font-size: var(--text-17); font-weight: var(--weight-semibold); color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rvds-mobile-menu__back {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  min-height: var(--touch-target, 44px);
  margin-left: -8px;
  padding: 0 8px 0 2px;
  border: none;
  border-radius: var(--radius-md);
  background: none;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--mut);
}
.rvds-mobile-menu__back:focus-visible { outline: none; box-shadow: var(--focus-glow); }
.rvds-mobile-menu__back > svg { transition: transform var(--duration-fast) var(--ease); }
.rvds-mobile-menu__back:active > svg { transform: translateX(-3px); }
.rvds-mobile-menu__body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 8px 8px 16px; }
.rvds-mobile-menu__label { padding: 16px 12px 6px; font-size: 11.5px; font-weight: var(--weight-bold); letter-spacing: 0.04em; color: var(--text-label); }
/* A second tier: destinations that are not the offer - the shop on its own
   site, careers. A hairline and air above, smaller rows in --mut, so the
   offer reads as the menu and this as its appendix. */
.rvds-mobile-menu__label--break { margin: 14px 4px 0; padding: 18px 8px 4px; border-top: 1px solid var(--line); color: var(--dim); font-weight: var(--weight-semibold); }
.rvds-mobile-menu__row--secondary { min-height: 44px; font-size: var(--text-15); color: var(--mut); }
.rvds-mobile-menu__row--secondary .rvds-mobile-menu__row-sub { font-size: 12px; }
.rvds-mobile-menu__row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  box-sizing: border-box;
  min-height: 52px;
  padding: 8px 12px;
  border: none;
  border-radius: var(--radius-md); /* a row, not a card: one step below the surface radius (0.17.0) */
  background: transparent;
  cursor: pointer;
  text-align: left;
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: var(--text-16);
  font-weight: var(--weight-medium);
  color: var(--fg);
  -webkit-tap-highlight-color: transparent;
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant);
}
.rvds-mobile-menu__row:active { background: var(--fill-3); }
@media (hover: hover) { .rvds-mobile-menu__row:hover { background: var(--hover-veil); color: var(--fg); } }
.rvds-mobile-menu__row:focus-visible { outline: none; box-shadow: var(--focus-glow); }
.rvds-mobile-menu__row[aria-current="page"] { background: var(--state-surface); }
.rvds-mobile-menu__row .rvds-icon-tile { --rvds-icon-tile-size: 34px; }
.rvds-mobile-menu__row-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.rvds-mobile-menu__row-sub { font-size: 12.5px; font-weight: var(--weight-regular); color: var(--mut-2); }
.rvds-mobile-menu__row-end { flex: none; display: inline-flex; color: var(--dim); }
/* The key action keeps its own width (a button is as wide as its label); the
   ThemeToggle goes to the far end. */
.rvds-mobile-menu__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex: none;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  background: var(--bg-1);
}

/* ===========================================================================
 * 18. Sidenav - folds, nests, and one marker that travels
 * ======================================================================== */

/* Folding: the rail keeps every glyph where it is - item padding never
   changes - and only the labels fade before the width closes over them, so
   nothing jumps sideways. A toggle with data-rvds-sidebar-collapse folds and
   unfolds; a glyph in the folded rail names itself in a tooltip (data-rvds-tip,
   side right). Pressing a group in the folded rail unfolds it and opens the
   group. */
.rvds-sidebar { overflow: hidden; transition: width var(--duration-morph) var(--ease-move); }
.rvds-sidebar--collapsed { width: 60px; }
.rvds-sidebar :is(.rvds-sidebar-item__label, .rvds-sidebar-item__badge, .rvds-sidebar__label, .rvds-sidebar-group__chevron, .rvds-sidebar__switch-text) {
  transition: opacity var(--duration-base) var(--ease) 80ms;
}
.rvds-sidebar--collapsed :is(.rvds-sidebar-item__label, .rvds-sidebar-item__badge, .rvds-sidebar__label, .rvds-sidebar-group__chevron, .rvds-sidebar__switch-text) {
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-exit);
}
.rvds-sidebar--collapsed .rvds-sidebar__label { height: 0; padding-block: 0; overflow: hidden; }
.rvds-sidebar-item { white-space: nowrap; }
.rvds-sidebar-item__icon { position: relative; }
/* A count in the folded rail becomes a dot on its glyph. */
.rvds-sidebar-item__dot { position: absolute; top: -2px; right: -3px; width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--gold); box-shadow: 0 0 0 2px var(--bg-1); opacity: 0; transform: scale(0.4); transition: opacity var(--duration-fast), transform var(--duration-base) var(--ease-settle); }
.rvds-sidebar--collapsed .rvds-sidebar-item__dot { opacity: 1; transform: none; }
.rvds-sidebar__collapse > svg { transition: transform var(--duration-morph) var(--ease-move); }
.rvds-sidebar--collapsed .rvds-sidebar__collapse > svg { transform: rotate(180deg); }

/* One marker for the rail: it travels to the current row instead of one
   blinking out and another in. interactions.js places it; without the script
   each row draws its own (::before, components.css). */
.rvds-sidebar__nav { position: relative; }
.rvds-sidebar__marker {
  position: absolute;
  left: 10px;
  top: 0;
  width: 2px;
  height: 22px;
  border-radius: var(--radius-pill);
  background: var(--gold);
  opacity: 0;
  pointer-events: none;
  transform: translateY(var(--rvds-marker-y, 0px));
  transition: transform var(--duration-morph) var(--ease-move), opacity var(--duration-fast);
}
.rvds-sidebar__marker--on { opacity: 1; }
.rvds-sidebar__marker--jump { transition: opacity var(--duration-fast); }
.rvds-sidebar:has(.rvds-sidebar__marker--on) .rvds-sidebar-item[aria-current="page"]::before { display: none; }

/* Nested group: the parent is a row with a chevron; its children open under
   it on the grid-row accordion, indented on a hairline. */
.rvds-sidebar-group__chevron { margin-left: auto; display: inline-flex; color: var(--dim); transition: transform var(--duration-icon) var(--ease), opacity var(--duration-base); }
.rvds-sidebar-group__toggle[aria-expanded="true"] > .rvds-sidebar-group__chevron { transform: rotate(90deg); }
.rvds-sidebar-group__panel { display: grid; grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows var(--duration-accordion) var(--ease), visibility 0s linear var(--duration-accordion); }
.rvds-sidebar-group__toggle[aria-expanded="true"] + .rvds-sidebar-group__panel { grid-template-rows: 1fr; visibility: visible; transition: grid-template-rows var(--duration-accordion) var(--ease), visibility 0s; }
.rvds-sidebar--collapsed .rvds-sidebar-group__panel { grid-template-rows: 0fr !important; visibility: hidden !important; }
.rvds-sidebar-group__inner { min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: 2px; margin-left: 18px; padding-left: 10px; border-left: 1px solid var(--line); }
.rvds-sidebar-item--sub { height: 34px; font-size: var(--text-13); }

/* Area switcher at the head: which part of the business you are in. */
.rvds-sidebar__switch {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  box-sizing: border-box;
  height: 44px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md); /* a row, not a card: one step below the surface radius (0.17.0) */
  background: var(--fill-1);
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-sans);
  color: var(--fg);
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant), border-color var(--duration-instant);
}
@media (hover: hover) { .rvds-sidebar__switch:hover { background: var(--fill-2); border-color: var(--line-2); } }
.rvds-sidebar__switch:focus-visible { outline: none; box-shadow: var(--focus-glow); }
.rvds-sidebar__switch .rvds-icon-tile { --rvds-icon-tile-size: 26px; --rvds-icon-tile-radius: var(--radius-sm); flex: none; }
.rvds-sidebar__switch-text { flex: 1; min-width: 0; display: flex; flex-direction: column; text-align: left; line-height: 1.2; }
.rvds-sidebar__switch-text > b { font-size: var(--text-13); font-weight: var(--weight-semibold); }
.rvds-sidebar__switch-text > span { font-size: 11.5px; color: var(--mut-2); }
/* Folded - by hand or by the App Shell's width - the switcher is its tile
   alone: no frame around it, no chevron beside it. */
.rvds-sidebar__switch > svg:last-child { flex: none; transition: opacity var(--duration-fast); }
.rvds-sidebar--collapsed .rvds-sidebar__switch { border-color: transparent; background: transparent; }
.rvds-sidebar--collapsed .rvds-sidebar__switch > svg:last-child { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .rvds-mobile-menu__level, .rvds-overlay--open > .rvds-mobile-menu, .rvds-sidebar, .rvds-sidebar__marker { transition-property: opacity, visibility !important; }
  .rvds-mobile-menu__level--behind { transform: none; }
}

/* ===========================================================================
 * 19. MenuButton in plain HTML, and the menu that opens under its bar
 * ======================================================================== */

/*   <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-menu-button"
 *     data-rvds-menu-toggle="menu" aria-expanded="false" aria-controls="menu" aria-label="Menü">
 *     <span class="rvds-menu-button__bar"></span><span class="rvds-menu-button__bar"></span><span class="rvds-menu-button__bar"></span>
 *   </button>
 *
 * The same morph as MenuButton.jsx: the three bars fold into a cross and
 * back, so no frame is ever neither. The button that opens the menu is the
 * one that closes it - it never moves, and nothing in the bar moves with it.
 *
 * .rvds-mobile-menu-frame--below: the menu opens under the bar instead of over
 * it (top at --rvds-menu-top, the bar's height, 56px by default). The bar with
 * its mark and the MenuButton stays where it was; the root level drops its own
 * head, which would only repeat the bar. Deeper levels keep their back row. */
.rvds-menu-button { position: relative; }
.rvds-menu-button__bar {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 17px;
  height: 1.5px;
  margin: -0.75px 0 0 -8.5px;
  border-radius: 1px;
  background: currentColor;
  transition: transform var(--duration-base) var(--ease), opacity var(--duration-fast);
}
.rvds-menu-button__bar:nth-child(1) { transform: translateY(-5px); }
.rvds-menu-button__bar:nth-child(3) { transform: translateY(5px); }
.rvds-menu-button[aria-expanded="true"] > .rvds-menu-button__bar:nth-child(1) { transform: rotate(45deg); }
.rvds-menu-button[aria-expanded="true"] > .rvds-menu-button__bar:nth-child(2) { opacity: 0; transform: scaleX(0.4); }
.rvds-menu-button[aria-expanded="true"] > .rvds-menu-button__bar:nth-child(3) { transform: rotate(-45deg); }
.rvds-menu-button[aria-expanded="true"] { color: var(--fg); }

.rvds-mobile-menu-frame--below { top: var(--rvds-menu-top, 56px); }
.rvds-mobile-menu-frame--below .rvds-mobile-menu__head--root { display: none; }
.rvds-mobile-menu-frame--below .rvds-mobile-menu { border-top: 0; }
/* Under the bar on a phone the menu does not slide or wipe across the
   screen: the surface fades in at once and its rows settle in one after
   another, a few pixels down from the bar - it reads as the bar opening.
   From 640px of frame it stays the panel sliding in from the right. */
@container rvds-menu-frame (max-width: 639px) {
  .rvds-mobile-menu-frame--below.rvds-overlay--open > .rvds-mobile-menu { transform: none; opacity: 1; transition: opacity var(--duration-fast) var(--ease); }
  @starting-style {
    .rvds-mobile-menu-frame--below.rvds-overlay--open > .rvds-mobile-menu { transform: none; opacity: 0; }
  }
  .rvds-mobile-menu--opening .rvds-mobile-menu__level--active > .rvds-mobile-menu__body > * { animation: rvds-arrive var(--duration-base) var(--ease) both; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__level--active > .rvds-mobile-menu__foot { animation: rvds-fade-in var(--duration-base) var(--ease) 160ms both; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__body > :nth-child(2) { animation-delay: 25ms; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__body > :nth-child(3) { animation-delay: 50ms; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__body > :nth-child(4) { animation-delay: 75ms; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__body > :nth-child(5) { animation-delay: 100ms; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__body > :nth-child(6) { animation-delay: 125ms; }
  .rvds-mobile-menu--opening .rvds-mobile-menu__body > :nth-child(n+7) { animation-delay: 150ms; }
}
@media (prefers-reduced-motion: reduce) {
  .rvds-menu-button__bar { transition: opacity var(--duration-fast) !important; }
  .rvds-mobile-menu--opening * { animation: none !important; }
}

/* ===========================================================================
 * 20. KpiRow - the grid a row of KpiCards sits in
 * ======================================================================== */

/*   <div class="rvds-kpi-row">…KpiCards…</div>
 *
 * The row KpiCard.prompt.md described in words, as a class. Measured by the
 * row, not the viewport, with no breakpoint. A card may go down to 136px -
 * the KpiCard's compact form, which it takes on its own below 200px - before
 * the row wraps, and never more than five share a line: three fit from about
 * 450px of row, two from about 290px. A row in a narrow column - beside a
 * sidebar, in a panel - no longer turns into a stack of wide, empty cards.
 *
 * auto-fill since 0.15.0 (was auto-fit): the row keeps its five tracks, so
 * two figures on a 2560px screen stay two cards a fifth wide and the free
 * tracks stay free, instead of two cards each half the screen wide. */
.rvds-kpi-row {
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fill, minmax(max(136px, (100% - 4 * var(--grid-gap)) / 5), 1fr));
}

/*   <div class="rvds-kpi-groups">
 *     <section class="rvds-kpi-group"><h2 class="rvds-kpi-group__title">Heute</h2><div class="rvds-kpi-row">…</div></section>
 *     <section class="rvds-kpi-group"><h2 class="rvds-kpi-group__title">Stand</h2><div class="rvds-kpi-row">…</div></section>
 *   </div>
 *
 * Group figures instead of stretching them (0.15.0). A row holds five cards
 * at most; more figures are grouped by meaning, each group its own row with a
 * heading. Where two groups fit side by side - four cards of 200px and their
 * gaps each, from about 2000px of screen beside a sidenav - they share the
 * line, so no card grows past about twice its minimum. */
.rvds-kpi-groups {
  display: grid;
  gap: 24px var(--grid-gap);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, calc(4 * 200px + 3 * var(--grid-gap))), 1fr));
}
.rvds-kpi-group { display: grid; gap: 10px; align-content: start; min-width: 0; }
.rvds-kpi-group__title { margin: 0; font-size: var(--text-14); font-weight: var(--weight-semibold); color: var(--fg); }

/* ===========================================================================
 * 21. App Shell - one frame, every view
 * ======================================================================== */

/*   <div class="rvds-app-shell" data-rvds-shell>
 *     <aside class="rvds-sidebar">…the areas, once…</aside>
 *     <div class="rvds-app-shell__main">
 *       <header class="rvds-app-shell__status">…global state: live timer, sync, account…</header>
 *       <main class="rvds-app-shell__view" data-view="zeit">
 *         <header class="rvds-page-header">…</header>
 *         <div class="rvds-app-shell__toolbar">…the view's own actions…</div>
 *         …content…
 *       </main>
 *     </div>
 *     <nav class="rvds-tab-bar rvds-app-shell__tabbar">…the same areas…</nav>
 *   </div>
 *
 * The answer to "the second page invents its own navigation": an app has ONE
 * list of areas, rendered by width - the sidenav from 1200px of shell, the
 * folded rail from 640px, the TabBar below (the first four areas, the rest in
 * a «Mehr» sheet). Global state lives in __status and nowhere else; a view
 * only brings its header, its toolbar and its content. Switching a view
 * crossfades the view only - the frame never moves. The shell measures
 * itself (container), not the viewport, so it works inside a panel or a
 * specimen frame too. */
.rvds-app-shell {
  container: rvds-shell / inline-size;
  position: relative;
  display: flex;
  min-height: 100%;
  height: 100%;
  background: var(--bg);
}
.rvds-app-shell__main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.rvds-app-shell__status {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  flex: none;
  height: 52px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
  background: var(--nav-bg);
  -webkit-backdrop-filter: blur(var(--blur-nav)) saturate(var(--saturate-nav));
  backdrop-filter: blur(var(--blur-nav)) saturate(var(--saturate-nav));
}
.rvds-app-shell__status-title { display: none; margin-right: auto; font-size: var(--text-15); font-weight: var(--weight-semibold); color: var(--fg); }
.rvds-app-shell__view {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px clamp(14px, 3cqi, 32px) 40px;
  display: flex;
  flex-direction: column;
  gap: var(--view-gap);
  animation: rvds-fade-in var(--duration-base) var(--ease) both;
}
.rvds-app-shell__view[hidden] { display: none; }
.rvds-app-shell__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* The sort button stands on the right of the toolbar, before any primary action (0.15.0). */
.rvds-app-shell__toolbar .rvds-sort-button { margin-left: auto; }
.rvds-app-shell__tabbar { display: none; }
@container rvds-shell (width < 1200px) {
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) { width: 60px; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) :is(.rvds-sidebar-item__label, .rvds-sidebar-item__badge, .rvds-sidebar__label, .rvds-sidebar-group__chevron, .rvds-sidebar__switch-text) { opacity: 0; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar-item__dot { opacity: 1; transform: none; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__switch { border-color: transparent; background: transparent; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__switch > svg:last-child { opacity: 0; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__label { height: 0; padding-block: 0; overflow: hidden; }
}
@container rvds-shell (width < 640px) {
  .rvds-app-shell > .rvds-sidebar { display: none; }
  .rvds-app-shell__tabbar { display: flex; position: absolute; left: 0; right: 0; bottom: 0; }
  .rvds-app-shell__view { padding-bottom: calc(var(--tabbar-height) + 24px); }
  /* A view's primary action, handed to a StickyBar by its PageHeader
     (0.15.0), sits on the TabBar, not under it, and the view clears both. */
  .rvds-app-shell__view .rvds-sticky-bar:not(.rvds-sticky-bar--inline):not(.rvds-sticky-bar--column) {
    bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom));
    padding-bottom: 12px;
  }
  .rvds-app-shell__view:has(.rvds-sticky-bar:not(.rvds-sticky-bar--inline):not(.rvds-sticky-bar--column)) {
    padding-bottom: calc(var(--tabbar-height) + var(--sticky-bar-height) + 24px);
  }
  .rvds-app-shell__status { justify-content: space-between; }
  .rvds-app-shell__status-title { display: block; }
}
/* Live state in the status zone: a running timer is one control - the dot
   pulses, the figure rolls, the stop is a word. */
.rvds-live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 6px 0 12px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--gold-a40);
  background: var(--gold-a10);
  font-size: var(--text-13);
  color: var(--fg);
  white-space: nowrap;
}
.rvds-live__dot { width: 7px; height: 7px; border-radius: var(--radius-pill); background: var(--gold); --pulse-colour: var(--gold); animation: revoltaris-pulse 2s linear infinite; }
.rvds-live__figure { font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
.rvds-live__stop { height: 24px; padding: 0 10px; border: none; border-radius: var(--radius-pill); background: var(--gold); color: var(--on-accent); font-family: var(--font-sans); font-size: 12px; font-weight: var(--weight-bold); cursor: pointer; }
.rvds-live__stop:focus-visible { outline: none; box-shadow: var(--focus-glow); }
.rvds-sync { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--mut-2); white-space: nowrap; }
.rvds-sync__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--success); }
.rvds-sync--stale .rvds-sync__dot { background: var(--danger); }
@container rvds-shell (width < 640px) { .rvds-sync__text { display: none; } }
@media (prefers-reduced-motion: reduce) { .rvds-live__dot { animation: none; } .rvds-app-shell__view { animation: none; } }

/* TabBar «Mehr»: the fifth slot opens a BottomSheet with the remaining
   areas; it is current when one of them is. */
.rvds-tab-bar__item[aria-expanded="true"] { color: var(--state-ink); }

/* ===========================================================================
 * 22. More navigation: sub-nav, table of contents, breadcrumb overflow,
 *     vertical tabs
 * ======================================================================== */

/* Sub-navigation: the pages of one section, under the main bar. A row of
   links with the same travelling line as Tabs; it scrolls sideways on a
   phone with the current one kept in view, edges fading where there is more.
   <nav class="rvds-subnav" aria-label="Service"><a class="rvds-subnav__link" aria-current="page">…</a>…</nav> */
.rvds-subnav {
  position: relative;
  display: flex;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: 1px solid var(--line);
  -webkit-mask: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
  mask: linear-gradient(90deg, transparent, #000 16px, #000 calc(100% - 16px), transparent);
  padding-inline: 8px;
}
.rvds-subnav::-webkit-scrollbar { display: none; }
.rvds-subnav__link {
  position: relative;
  flex: none;
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 12px;
  font-size: var(--text-14);
  font-weight: var(--weight-medium);
  color: var(--mut-2);
  white-space: nowrap;
  text-decoration: none;
  transition: color var(--duration-base);
}
@media (hover: hover) { .rvds-subnav__link:hover { color: var(--fg); } }
.rvds-subnav__link[aria-current="page"] { color: var(--state-ink); font-weight: var(--weight-semibold); }
.rvds-subnav__link:focus-visible { outline: none; box-shadow: var(--focus-glow); border-radius: var(--radius-md); }
.rvds-subnav__line { position: absolute; left: 0; bottom: 0; height: 2px; width: var(--rvds-sub-w, 0px); transform: translateX(var(--rvds-sub-x, 0px)); background: var(--state-line); transition: transform var(--duration-morph) var(--ease-move), width var(--duration-morph) var(--ease-move); pointer-events: none; }
.rvds-subnav--still > .rvds-subnav__line { transition: none; }

/* Table of contents with scroll-spy: the chapter being read is marked by a
   gold marker that travels down the rail. A chapter counts as read once its
   head passes the upper third of the scroller (fires early, foundations.md).
   <nav class="rvds-toc" data-rvds-toc="<scroller id or empty for the page>">
     <span class="rvds-toc__marker"></span><a class="rvds-toc__link" href="#kap-1">…</a>… */
.rvds-toc { position: relative; display: flex; flex-direction: column; gap: 2px; padding-left: 14px; border-left: 1px solid var(--line); }
.rvds-toc__title { margin: 0 0 8px -14px; padding-left: 14px; font-size: 11.5px; font-weight: var(--weight-bold); letter-spacing: 0.04em; color: var(--text-label); }
.rvds-toc__link { padding: 5px 0; font-size: var(--text-13); line-height: 1.4; color: var(--mut-2); text-decoration: none; transition: color var(--duration-base); }
.rvds-toc__link--sub { padding-left: 12px; font-size: 12.5px; }
@media (hover: hover) { .rvds-toc__link:hover { color: var(--fg); } }
.rvds-toc__link[aria-current="true"] { color: var(--fg); }
.rvds-toc__marker { position: absolute; left: -1px; top: 0; width: 2px; height: var(--rvds-toc-h, 0px); transform: translateY(var(--rvds-toc-y, 0px)); background: var(--gold); border-radius: var(--radius-pill); transition: transform var(--duration-morph) var(--ease-move), height var(--duration-morph) var(--ease-move); }

/* Breadcrumb that keeps first and last: in a narrow row the middle crumbs
   fold into one «…» button opening a popover with the folded ones.
   Add .rvds-breadcrumb--fold; interactions.js measures. */
.rvds-breadcrumb--fold { flex-wrap: nowrap; min-width: 0; overflow: hidden; }
/* Room inside the clip for the focus glow (0.16.0), without moving the row. */
.rvds-breadcrumb--fold { padding: 8px; margin: -8px; }
.rvds-breadcrumb--fold > * { flex: none; }
.rvds-breadcrumb__current { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto !important; }
.rvds-breadcrumb__more { display: none; height: 24px; padding: 0 6px; border: 1px solid var(--line-2); border-radius: var(--radius-md); background: transparent; color: var(--mut-2); cursor: pointer; font: inherit; }
.rvds-breadcrumb--folded > .rvds-breadcrumb__more { display: inline-flex; align-items: center; }
/* The button's own separator (0.16.0): shown only with the button, so every
   middle crumb can fold together with the separator after it. */
.rvds-breadcrumb__separator--more { display: none; }
.rvds-breadcrumb--folded > .rvds-breadcrumb__separator--more { display: inline-flex; }
.rvds-breadcrumb--folded > [data-rvds-crumb-fold] { display: none !important; }
.rvds-breadcrumb__more:focus-visible { outline: none; box-shadow: var(--focus-glow); }

/* Vertical tabs: settings pages, long forms. The same travelling marker as
   the sidenav, on the left; below 640px of their own row they turn into the
   horizontal Tabs strip.
   <div class="rvds-vtabs"><div class="rvds-vtabs__list" role="tablist">
     <span class="rvds-vtabs__marker"></span><button class="rvds-vtabs__tab" role="tab" aria-selected="true">…</button>…
   </div><div class="rvds-vtabs__panel">…</div></div> */
.rvds-vtabs { container: rvds-vtabs / inline-size; }
.rvds-vtabs__inner { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 28px; }
.rvds-vtabs__list { position: relative; display: flex; flex-direction: column; gap: 2px; }
.rvds-vtabs__tab { position: relative; display: flex; align-items: center; gap: 10px; height: var(--control-height); padding: 0 12px; border: none; border-radius: var(--radius-md); background: transparent; cursor: pointer; text-align: left; font-family: var(--font-sans); font-size: var(--text-14); font-weight: var(--weight-medium); color: var(--mut); transition: background var(--duration-instant), --rvds-fill var(--duration-instant), color var(--duration-base); }
@media (hover: hover) { .rvds-vtabs__tab:hover { background: var(--fill-2); color: var(--fg); } }
.rvds-vtabs__tab[aria-selected="true"] { background: var(--fill-3); color: var(--fg); font-weight: var(--weight-semibold); }
.rvds-vtabs__tab:focus-visible { outline: none; box-shadow: var(--focus-glow); }
.rvds-vtabs__marker { position: absolute; left: 0; top: 0; width: 2px; height: 22px; border-radius: var(--radius-pill); background: var(--gold); transform: translateY(var(--rvds-vt-y, 8px)); transition: transform var(--duration-morph) var(--ease-move); z-index: 1; }
@container rvds-vtabs (width < 640px) {
  .rvds-vtabs__inner { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .rvds-vtabs__list { flex-direction: row; overflow-x: auto; border-bottom: 1px solid var(--line); gap: 2px; }
  .rvds-vtabs__tab { flex: none; border-radius: var(--radius-md) var(--radius-md) 0 0; background: transparent !important; }
  .rvds-vtabs__marker { top: auto; bottom: 0; height: 2px; width: var(--rvds-vt-w, 0px); transform: translateX(var(--rvds-vt-x, 0px)); transition: transform var(--duration-morph) var(--ease-move), width var(--duration-morph) var(--ease-move); }
}
@media (prefers-reduced-motion: reduce) {
  .rvds-subnav__line, .rvds-toc__marker, .rvds-vtabs__marker { transition: none; }
}

/* ===========================================================================
 * 23. Platforms - iPhone, Android, installed app (reference/platforms.md)
 * ======================================================================== */

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* `hidden` always hides. A component that sets its own display (a button is
   inline-flex) otherwise beats the browser's [hidden] rule, and a control a
   script hides - «App installieren» before Chrome allows it - stays on screen. */
[hidden]:is(.rvds-button, .rvds-icon-button, .rvds-tag, .rvds-status-badge, .rvds-list-row, .rvds-panel, .rvds-alert, [data-rvds-install]) { display: none !important; }
/* Below 16px iOS zooms into a field on focus and leaves the page zoomed. */
@media (pointer: coarse) {
  .rvds-input__control,
  .rvds-select__control,
  .rvds-textarea,
  .rvds-stepper__value { font-size: max(16px, 1em); }
}
/* The App Shell under the notch, the status bar of an installed app and, in
   landscape, the side insets. */
.rvds-app-shell__status {
  height: calc(52px + env(safe-area-inset-top));
  padding-top: env(safe-area-inset-top);
  padding-inline: max(16px, env(safe-area-inset-left)) max(16px, env(safe-area-inset-right));
}
.rvds-app-shell > .rvds-sidebar { padding-left: env(safe-area-inset-left); box-sizing: content-box; }
.rvds-mobile-menu__head { padding-inline: max(16px, env(safe-area-inset-left)) max(10px, env(safe-area-inset-right)); }
/* Standalone: no browser chrome, so the app brings its own back and refresh. */
.rvds-standalone-only { display: none !important; }
@media (display-mode: standalone) {
  .rvds-standalone-only { display: revert !important; }
  .rvds-browser-only { display: none !important; }
  body { overscroll-behavior: none; }
}

/* ===========================================================================
 * 24. App Shell: the mark top left, and one cross
 * ======================================================================== */

/* Top left of every app surface is the mark - the gold wordmark, or the
   unit's horizontal lockup where the app belongs to one (BrandMark.prompt.md).
   Never the area switcher; that sits under the mark, at the top of the nav.
   Folded, the rail shows the signet in the same place.

   The sidenav's head and the status zone are one height (--app-bar-height
   plus the top inset) with one hairline each, so the two lines meet the
   rail's right edge in a clean cross. */
.rvds-app-shell { --app-bar-height: 56px; }
.rvds-app-shell > .rvds-sidebar > .rvds-sidebar__header,
.rvds-app-shell__status {
  box-sizing: border-box;
  height: calc(var(--app-bar-height) + env(safe-area-inset-top));
  padding-top: env(safe-area-inset-top);
  border-bottom: 1px solid var(--line);
}
.rvds-app-shell > .rvds-sidebar > .rvds-sidebar__header { display: flex; align-items: center; padding-inline: 18px 14px; overflow: hidden; }
.rvds-sidebar__brand { position: relative; display: flex; align-items: center; min-width: 0; text-decoration: none; }
/* The mark as the way back to the website (AppShell homeHref, 0.16.0). */
:is(a.rvds-sidebar__brand, a.rvds-app-shell__status-brand):focus { outline: none; }
:is(a.rvds-sidebar__brand, a.rvds-app-shell__status-brand):focus-visible { border-radius: var(--radius-md); box-shadow: var(--focus-glow); }
.rvds-sidebar__brand-full,
.rvds-sidebar__brand-signet { display: flex; transition: opacity var(--duration-base) var(--ease); }
.rvds-sidebar__brand-signet { position: absolute; left: 0; top: 50%; transform: translateY(-50%); opacity: 0; pointer-events: none; }
.rvds-sidebar--collapsed .rvds-sidebar__brand-full { opacity: 0; }
.rvds-sidebar--collapsed .rvds-sidebar__brand-signet { opacity: 1; }
@container rvds-shell (width < 1200px) {
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__brand-full { opacity: 0; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__brand-signet { opacity: 1; }
}
/* The switcher, under the mark: the first row of the nav. */
.rvds-sidebar__nav > .rvds-sidebar__switch { flex: none; }

/* Phone: the sidenav is gone, so the status zone carries the mark top left -
   at the bar size, in the same 56px. The area's name is the view's own
   PageHeader, not a second title in the bar. */
.rvds-app-shell__status-brand { display: none; margin-right: auto; align-items: center; min-width: 0; }
.rvds-app-shell__status-brand .rvds-brandmark { --brandmark-size: var(--brandmark-bar-phone); }
@container rvds-shell (width < 640px) {
  .rvds-app-shell__status-brand { display: flex; }
  .rvds-app-shell__status-title { display: none !important; }
}

/* ===========================================================================
 * 25. CommandPalette - the search, as classes (components/app/CommandPalette.jsx)
 * ======================================================================== */

/* Migrated from the component's inline styles (0.14.0), values unchanged,
   so plain HTML gets the same palette: on the Overlay frame - the system's
   one backdrop, black at 72% with its 6px blur - top-aligned, scaled out of
   nothing like every floating surface.

   <button type="button" class="rvds-command-trigger" data-rvds-command="suche">
     <svg …search…></svg><span class="rvds-command-trigger__text">Suchen</span><kbd class="rvds-kbd" data-rvds-kbd-mod>⌘K</kbd>
   </button>

   <div class="rvds-overlay rvds-command-frame" id="suche" data-rvds-back>
     <div class="rvds-overlay__backdrop"></div>
     <div class="rvds-command" role="dialog" aria-modal="true" aria-label="Suchen">
       <div class="rvds-command__head">
         <span class="rvds-command__icon"><svg …search…></svg></span>
         <input class="rvds-command__input" role="combobox" aria-expanded="true" aria-controls="suche-list" aria-autocomplete="list" placeholder="Suchen oder Befehl">
         <kbd class="rvds-kbd">esc</kbd>
         <button type="button" class="rvds-icon-button rvds-icon-button--bare rvds-command__close" aria-label="Schliessen" data-rvds-close>…x…</button>
       </div>
       <div class="rvds-command__list" id="suche-list" role="listbox">
         <div class="rvds-command__group" role="presentation">Bereiche</div>
         <div class="rvds-command__option" role="option" id="…" data-rvds-view="zeit" data-keywords="stunden timer">
           <span class="rvds-command__option-icon"><svg…></svg></span>
           <span class="rvds-command__option-text"><span class="rvds-command__option-label">Zeiterfassung</span><span class="rvds-command__option-desc">…</span></span>
           <kbd class="rvds-kbd">G Z</kbd>
         </div>
         <p class="rvds-command__empty" hidden>Keine Treffer</p>
       </div>
       <div class="rvds-command__foot">…hints…</div>
     </div>
   </div>

   Cmd/Ctrl+K opens the palette of the shell the focus is in (else the first);
   typing filters label, description and data-keywords, every word must
   match, accents ignored, the match is marked; Up, Down, Home, End move,
   Enter runs, Escape closes and returns focus. On a phone the palette takes
   the top of the screen at full width - there is no keyboard shortcut there,
   the trigger is a glyph in the status zone. */
.rvds-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 20px;
  height: 20px;
  padding-inline: 5px;
  box-sizing: border-box;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-2);
  background: var(--fill-2);
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: var(--weight-semibold);
  line-height: 1;
  color: var(--mut-2);
}
.rvds-command-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  height: var(--control-height-sm);
  padding-inline: 10px;
  border-radius: var(--radius-md);
  border: 1px solid var(--field-border);
  background: var(--field-bg);
  box-shadow: var(--control-inset);
  color: var(--field-placeholder, var(--dim));
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: var(--text-13);
  transition: border-color var(--duration-base), color var(--duration-base);
}
.rvds-command-trigger > svg { flex: none; }
.rvds-command-trigger__text { flex: 1; min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis; }
@media (hover: hover) { .rvds-command-trigger:hover { border-color: var(--field-border-hover, var(--line-3)); color: var(--mut); } }
.rvds-command-trigger:focus-visible { outline: none; border-color: var(--field-border-focus); box-shadow: var(--focus-glow); }
/* In the sidenav: under the area switcher, full width. Folded, the glyph alone. */
.rvds-sidebar__search { width: 100%; flex: none; }
.rvds-sidebar :is(.rvds-sidebar__search > .rvds-command-trigger__text, .rvds-sidebar__search > .rvds-kbd) { transition: opacity var(--duration-base) var(--ease) 80ms; }
.rvds-sidebar--collapsed :is(.rvds-sidebar__search > .rvds-command-trigger__text, .rvds-sidebar__search > .rvds-kbd) { opacity: 0; }
.rvds-sidebar--collapsed .rvds-sidebar__search { border-color: transparent; background: transparent; box-shadow: none; }
.rvds-sidebar--collapsed .rvds-sidebar__search:focus-visible { box-shadow: var(--focus-glow); }
@container rvds-shell (width < 1200px) {
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) :is(.rvds-sidebar__search > .rvds-command-trigger__text, .rvds-sidebar__search > .rvds-kbd) { opacity: 0; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__search { border-color: transparent; background: transparent; box-shadow: none; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__search:focus-visible { box-shadow: var(--focus-glow); }
}
/* On a phone the search is a glyph in the status zone. */
.rvds-app-shell__status-search { display: none !important; }
@container rvds-shell (width < 640px) { .rvds-app-shell__status-search { display: inline-flex !important; } }

/* Centred in the screen, both ways (Alex 14.10.2026). The list has a fixed
   height, so filtering never moves the field: the palette stays where it
   opened while the results change under the input. */
.rvds-overlay.rvds-command-frame { z-index: 540; align-items: center; justify-content: center; padding: 24px; }
.rvds-command { height: min(70vh, 520px); }
.rvds-command {
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 100%;
  max-width: 560px;
  max-height: min(70vh, 520px);
  border-radius: var(--radius-xl);
  border: 1px solid var(--line-2);
  background: var(--menu-bg);
  -webkit-backdrop-filter: blur(var(--blur-menu));
  backdrop-filter: blur(var(--blur-menu));
  box-shadow: var(--shadow-menu);
  overflow: hidden;
}
.rvds-overlay--open > .rvds-command { opacity: 1; transform: none; transition: opacity var(--duration-float) var(--ease), transform var(--duration-overlay) var(--ease); }
@starting-style { .rvds-overlay--open > .rvds-command { opacity: 0; transform: scale(var(--float-scale)); } }
.rvds-command__head { display: flex; align-items: center; gap: 10px; flex: none; height: 52px; padding: 0 14px; border-bottom: 1px solid var(--line); }
.rvds-command__icon { flex: none; display: inline-flex; color: var(--dim); }
.rvds-command__input { flex: 1; min-width: 0; height: 100%; border: none; background: none; outline: none; font-family: var(--font-sans); font-size: var(--text-15); color: var(--fg); }
.rvds-command__input::placeholder { color: var(--field-placeholder, var(--dim)); }
.rvds-command__input::-webkit-search-cancel-button { display: none; }
.rvds-command__list { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 6px; }
.rvds-command__group { padding: 9px 10px 5px; font-size: 11px; font-weight: var(--weight-bold); letter-spacing: 0.04em; color: var(--text-label); }
.rvds-command__option {
  display: flex;
  align-items: center;
  gap: 11px;
  box-sizing: border-box;
  padding: 9px 10px;
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--mut);
  background: transparent;
  text-decoration: none;
  transition: background var(--duration-instant), --rvds-fill var(--duration-instant), color var(--duration-instant);
}
.rvds-command__option[aria-selected="true"] { background: var(--state-surface); color: var(--fg); }
/* An option that is a link can take the focus: the glow, never the browser's ring. */
.rvds-command__option:focus { outline: none; }
.rvds-command__option:focus-visible { box-shadow: var(--focus-glow); }
.rvds-command__option[hidden], .rvds-command__group[hidden] { display: none; }
.rvds-command__option-icon { flex: none; display: inline-flex; color: var(--dim); transition: color var(--duration-instant); }
.rvds-command__option[aria-selected="true"] > .rvds-command__option-icon { color: var(--gold); }
.rvds-command__option-text { flex: 1; min-width: 0; }
/* Never cut (0.16.0): a long name - a product - wraps onto further lines
   instead of ending in «…»; the text is shown as given. */
.rvds-command__option-label { display: block; font-size: var(--text-14); font-weight: var(--weight-medium); line-height: 1.35; overflow-wrap: anywhere; }
.rvds-command__option-label > mark { background: none; color: var(--fg); font-weight: var(--weight-bold); }
.rvds-command__option-desc { display: block; margin-top: 1px; font-size: 11.5px; color: var(--mut-2); overflow-wrap: anywhere; }
/* A group without a heading: a hairline that sets the rows after it apart,
   such as the search row. */
.rvds-command__group--rule { height: 0; margin: 6px 4px; padding: 0; border-top: 1px solid var(--line); }
/* The search row (0.16.0): always the last row, never filtered; it hands the
   typed term to the surface's full-text search. Its text is the surface's;
   .rvds-command__query inside it shows the term. */
.rvds-command__query { color: var(--fg); font-weight: var(--weight-semibold); }
/* The part of the row's text that names the term, hidden while nothing is typed. */
.rvds-command__option[data-empty] .rvds-command__query-part { display: none; }
.rvds-command__empty { margin: 0; padding: 26px 12px; text-align: center; font-size: var(--text-13); color: var(--mut-2); }
.rvds-command__empty[hidden] { display: none; }
.rvds-command__foot { display: flex; align-items: center; gap: 16px; flex: none; padding: 9px 14px; border-top: 1px solid var(--line); font-size: 11.5px; color: var(--dim); }
.rvds-command__foot > span { display: inline-flex; align-items: center; gap: 6px; }
@container rvds-shell (width < 640px) {
  .rvds-overlay.rvds-command-frame { padding: calc(8px + env(safe-area-inset-top)) 8px 8px; }
  .rvds-command { max-width: none; max-height: 100%; height: auto; }
  .rvds-overlay.rvds-command-frame { align-items: flex-start; }
  .rvds-command__foot { display: none; }
}
@media (max-width: 639px) {
  /* A phone: at the top, under the thumb's reach of the keyboard. */
  .rvds-overlay.rvds-command-frame { padding: calc(8px + env(safe-area-inset-top)) 8px 8px; align-items: flex-start; }
  .rvds-command { max-width: none; height: auto; }
  .rvds-command__foot { display: none; }
}
@media (pointer: coarse) { .rvds-command__input { font-size: max(16px, var(--text-15)); } }

/* ===========================================================================
 * 26. Sidenav: one axis
 * ======================================================================== */

/* Every glyph of the rail - signet, switcher tile, search, rows, footer -
   sits on one vertical axis, the middle of the 60px folded rail (30px).
   The insets that put it there apply open and folded alike, so nothing moves
   sideways when the rail folds: nav and footer 12px, a row's own 10px, the
   glyph 16px wide (12 + 10 + 8 = 30). The wordmark starts on the same line as
   the row glyphs (22px). */
.rvds-sidebar { --rvds-rail-axis: 30px; }
/* The nav scrolls down, never sideways: folded, the faded labels and the
   search cap still sit in the row beyond the 60px and made the rail scroll
   horizontally. */
.rvds-sidebar__nav { padding-inline: 12px; overflow-x: hidden; }
.rvds-sidebar__footer { overflow-x: hidden; }
.rvds-sidebar__footer { padding-inline: 12px; }
.rvds-sidebar__marker { left: 12px; }
.rvds-app-shell > .rvds-sidebar > .rvds-sidebar__header,
.rvds-sidebar > .rvds-sidebar__header:has(.rvds-sidebar__brand) { padding-inline: 22px 14px; }
/* The signet centres on the axis: 22px + 8px. */
.rvds-sidebar__brand-signet { left: 8px; transform: translate(-50%, -50%); }
/* Glyph buttons in the footer are 36px, so their middle is the axis too. */
.rvds-sidebar__footer .rvds-icon-button { --rvds-icon-button-size: 36px; }
/* The search glyph: 12 + 1 border + 10 padding + 7 = 30. */
.rvds-sidebar__search { padding-inline: 10px; }
/* The switcher's tile is 28px; folded it slides onto the axis while the rail
   closes (12 + 1 + 3 + 14 = 30) - the only glyph that moves, by 5px. */
.rvds-sidebar__switch { transition: background var(--duration-instant), --rvds-fill var(--duration-instant), border-color var(--duration-instant), padding var(--duration-morph) var(--ease-move); }
.rvds-sidebar--collapsed .rvds-sidebar__switch { padding-left: 3px; }
@container rvds-shell (width < 1200px) {
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__switch { padding-left: 3px; }
}

/* The palette's way out for a pointer: the close glyph stands at every width
   (Alex 15.10.2026); with a keyboard the esc cap stays beside it. */
.rvds-command__close { display: inline-flex; flex: none; margin-right: -6px; }

/* ===========================================================================
 * 27. Keyboard hints only where there is a keyboard
 * ======================================================================== */

/* A phone and a tablet have no shortcuts (Alex 14.10.2026): every keycap -
   the ⌘K on the search, the shortcuts in the palette and the menus, the
   palette's key foot, the cap in a tooltip - shows only with a fine pointer
   that can hover, and inside an App Shell only from desktop width (1200px)
   up, so a tablet frame is a tablet frame. */
@media not ((hover: hover) and (pointer: fine)) {
  .rvds-kbd, .rvds-float__kbd, .rvds-float__item-hint, .rvds-command__foot { display: none !important; }
}
@container rvds-shell (width < 1200px) {
  .rvds-kbd, .rvds-float__kbd, .rvds-float__item-hint, .rvds-command__foot { display: none !important; }
}

/* ===========================================================================
 * 28. Folding by hand in the App Shell, and the mark that turns into the signet
 * ======================================================================== */

/* The fold toggle (data-rvds-sidebar-collapse) works in the shell too: from
   1200px it folds the open rail (--collapsed), below 1200px - where the shell
   has folded it - it opens it (--pinned) and back. Its chevron points where
   the rail will go. */
@container rvds-shell (width < 1200px) {
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__collapse > svg { transform: rotate(180deg); }
  /* Opened over the view, not beside it: it keeps its 60px in the row and
     reaches over the view by the rest, so the view never jumps. */
  .rvds-app-shell > .rvds-sidebar { position: relative; z-index: 20; }
  .rvds-app-shell > .rvds-sidebar--pinned { width: var(--rvds-sidebar-width, 236px); margin-right: calc(60px - var(--rvds-sidebar-width, 236px)); box-shadow: var(--shadow-menu); }
}

/* The mark: the wordmark closes from the right, in step with the rail, and
   the signet turns in from a quarter turn as it lands - one motion, not a
   swap. Opening runs backwards: the signet turns away, the wordmark draws
   out from the left. */
/* An explicit box: absolutely placed in the narrow header it would shrink to
   fit, and the BrandMark's max-width would clamp it to 16px. */
.rvds-sidebar__brand-signet { width: var(--brandmark-signet-rail); height: var(--brandmark-signet-rail); }
.rvds-sidebar__brand-signet .rvds-brandmark { --brandmark-size: var(--brandmark-signet-rail); }
.rvds-sidebar__brand-signet :is(.rvds-brandmark, .rvds-brandmark__svg) { max-width: none; }
.rvds-sidebar__brand-full {
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--duration-morph) var(--ease-move) 60ms, opacity var(--duration-fast) var(--ease) 60ms;
}
.rvds-sidebar__brand-signet {
  transform: translate(-50%, -50%) rotate(-90deg) scale(0.4);
  transition: opacity var(--duration-fast) var(--ease-exit), transform var(--duration-base) var(--ease-exit);
}
.rvds-sidebar--collapsed .rvds-sidebar__brand-full { clip-path: inset(0 100% 0 0); opacity: 0; transition: clip-path var(--duration-morph) var(--ease-move), opacity var(--duration-base) var(--ease) 120ms; }
.rvds-sidebar--collapsed .rvds-sidebar__brand-signet { transform: translate(-50%, -50%); transition: opacity var(--duration-base) var(--ease) 140ms, transform var(--duration-release) var(--ease-settle) 140ms; }
@container rvds-shell (width < 1200px) {
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__brand-full { clip-path: inset(0 100% 0 0); opacity: 0; transition: clip-path var(--duration-morph) var(--ease-move), opacity var(--duration-base) var(--ease) 120ms; }
  .rvds-app-shell > .rvds-sidebar:not(.rvds-sidebar--pinned) .rvds-sidebar__brand-signet { transform: translate(-50%, -50%); transition: opacity var(--duration-base) var(--ease) 140ms, transform var(--duration-release) var(--ease-settle) 140ms; }
}
@media (prefers-reduced-motion: reduce) {
  .rvds-sidebar__brand-full, .rvds-sidebar__brand-signet { transition-property: opacity !important; clip-path: none !important; }
  .rvds-sidebar__brand-signet { transform: translate(-50%, -50%) !important; }
}

/* ===========================================================================
 * 29. Sorting - the rows move, the page does not  (0.15.0)
 * ======================================================================== */

/* Sorting moves rows, never the page: a row glides from its old place to
 * its new one (FLIP over --duration-morph on --ease-move, 12ms apart, the
 * first eight staggered) - SortButton.jsx and Table.jsx do it with
 * useSortFlip, plain HTML gets it from components.js. The list keeps its
 * height and the scroll position stays where it was (Core rule 10).
 * The arrow of the active head turns with the direction, the label of the
 * sort button rolls the way the order went, the tick of the chosen field is
 * drawn from the left, and the direction thumb travels. Reduced motion: the
 * rows only fade back in, nothing travels. */
.rvds-table__sort-arrow { transition: transform var(--duration-fast) var(--ease-move); }
.rvds-sort-dir__thumb { transition: transform var(--duration-morph) var(--ease-move); }
.rvds-sort-option__check,
.rvds-action-sheet__check { transition: clip-path var(--duration-draw) var(--ease); }
.rvds-sort-button__label[data-roll="asc"] { --rvds-roll-from: calc(-1 * var(--roll-offset)); animation: rvds-roll-in var(--duration-base) var(--ease); }
.rvds-sort-button__label[data-roll="desc"] { --rvds-roll-from: var(--roll-offset); animation: rvds-roll-in-b var(--duration-base) var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .rvds-table__sort-arrow, .rvds-sort-dir__thumb, .rvds-sort-option__check, .rvds-action-sheet__check { transition: none; }
  .rvds-sort-button__label[data-roll] { animation: rvds-fade-in var(--duration-fast) var(--ease); }
}

