/*! fertig v4.0.7 — a classless CSS stylesheet. MIT */

/* Opinions, so you don't have to have them:
   system type · one accent · responsive gutters · a clear content canvas.
   The finish is platform-neutral: soft surfaces, rounded controls, restrained
   depth and a visible accent focus ring.
   Override the tokens marked "yours"; the rest is the point of view. */

/* typed so they interpolate; untyped custom properties cannot animate */
/* Registrations are global: they cannot be layered, scoped or overridden.
   Hence the prefix — a bare --a1 would silently retype a property of yours. */
@property --fertig-a1 { syntax: "<number>"; inherits: true; initial-value: .08 }
@property --fertig-a2 { syntax: "<number>"; inherits: true; initial-value: .10 }

/* Two layers, in this order. Your own unlayered CSS wins over both regardless
   of specificity, so overriding never needs !important — and neither does this
   sheet: fertig-a11y comes last, which is how the forced-colors block below
   beats a .card's box-shadow without one.
   @property stays outside — registrations are global, not layered. */
@layer fertig, fertig-a11y;

@layer fertig {

/* ---- tokens ------------------------------------------------- */
:root {
  color-scheme: light dark;              /* powers light-dark() below */
  /* These two are also re-declared on the elements that consume them, further
     down. They are inherited properties: declared only here, they resolve once
     against the root's accent and every descendant inherits that *computed
     colour* — so a [data-accent] or [data-tone] subtree, or any element that
     sets --fertig-ac, got the root's tint on its checkboxes and its caret
     while everything else in it followed the override. */
  accent-color: var(--fertig-ac);
  caret-color: var(--fertig-ac);

  --fertig-f: Inter, ui-sans-serif, system-ui, "Segoe UI", Roboto, "Helvetica Neue",
        Arial, sans-serif;                                            /* yours */
  --fertig-fm: ui-monospace, "Cascadia Code", "Roboto Mono", Consolas,
        "Liberation Mono", monospace;                                /* code */
  --fertig-w: 72rem;                            /* toolbar content width — yours */
  --fertig-g: clamp(1.15rem, 4vw, 1.9rem);      /* responsive page gutter — yours */
  /* One radius, and the rest derived from it: set --fertig-r and the whole
     sheet rescales together. Four independent numbers meant --fertig-r: 4px
     left tight fields sitting inside cards that were still round. */
  --fertig-r: 10px;                             /* control radius: fields, buttons, menu rows */
  --fertig-rs: calc(var(--fertig-r) * .6);      /* small: inline chips and code */
  --fertig-rw: calc(var(--fertig-r) * 1.6);     /* large: cards, dialogs, menus */
  --fertig-rp: calc(var(--fertig-r) * 1000);    /* pill: badges, switches, meters, joined ends */
  /* --fertig-rp multiplies rather than naming infinity: any non-zero radius
     still clamps to a true pill, while --fertig-r: 0 squares the whole shape
     system consistently. */

  /* ---- palette ------------------------------------------------
     Seven direct, familiar names; five perceptually even OKLCH stops each.
     The chroma rises toward the middle of every ramp while the endpoints stay
     quiet enough for surfaces and text. Every value is inside sRGB. */
  --fertig-gray-100: oklch(97% .005 260); --fertig-gray-300: oklch(82% .01 260);
  --fertig-gray-500: oklch(62% .014 260); --fertig-gray-700: oklch(47% .015 260);
  --fertig-gray-900: oklch(25% .018 260);

  --fertig-blue-100: oklch(96% .018 255); --fertig-blue-300: oklch(80% .095 255);
  --fertig-blue-500: oklch(62% .16 255);  --fertig-blue-700: oklch(47% .15 255);
  --fertig-blue-900: oklch(29% .09 255);

  --fertig-cyan-100: oklch(96% .025 210); --fertig-cyan-300: oklch(82% .085 210);
  --fertig-cyan-500: oklch(64% .11 210);  --fertig-cyan-700: oklch(47% .08 210);
  --fertig-cyan-900: oklch(29% .05 210);

  --fertig-green-100: oklch(96% .025 150); --fertig-green-300: oklch(80% .095 150);
  --fertig-green-500: oklch(62% .13 150);  --fertig-green-700: oklch(46% .10 150);
  --fertig-green-900: oklch(28% .06 150);

  --fertig-amber-100: oklch(97% .025 80); --fertig-amber-300: oklch(84% .10 80);
  --fertig-amber-500: oklch(72% .14 80);  --fertig-amber-700: oklch(50% .10 80);
  --fertig-amber-900: oklch(29% .06 80);

  --fertig-red-100: oklch(96% .018 25); --fertig-red-300: oklch(80% .10 25);
  --fertig-red-500: oklch(62% .18 25);  --fertig-red-700: oklch(47% .15 25);
  --fertig-red-900: oklch(28% .09 25);

  --fertig-violet-100: oklch(96% .018 290); --fertig-violet-300: oklch(80% .095 290);
  --fertig-violet-500: oklch(62% .16 290);  --fertig-violet-700: oklch(47% .15 290);
  --fertig-violet-900: oklch(28% .09 290);

  /* ---- semantic ----------------------------------------------- */
  --fertig-bg:  light-dark(oklch(97% .004 260), oklch(13.5% .014 260));  /* page ground */
  --fertig-el:  light-dark(oklch(99.7% .002 260), oklch(19% .016 260));  /* raised surface */
  --fertig-face:light-dark(oklch(94.5% .006 260), oklch(24% .018 260));  /* quiet controls */
  --fertig-fg:  light-dark(oklch(22% .02 260),  oklch(95% .012 260));
  /* lifted from 50%/70%: the weakest pairing (muted on the page ground in
     light, muted on controls in dark) was 4.96:1 and 5.99:1, a hair over AA.
     46%/72% takes them to 5.7:1 and 6.8:1 while keeping a clear step down
     from --fertig-fg — AAA would need ~41%, which is close enough to
     --fertig-fg that "muted" stops reading as secondary at all. */
  --fertig-mut: light-dark(oklch(46% .018 260),  oklch(72% .02 260));
  --fertig-bd:  light-dark(oklch(22% .02 260 / .10), oklch(95% .012 260 / .12));
  --fertig-tb:  light-dark(oklch(99% .003 260), oklch(18% .016 260));
  /* Blue is the neutral default: distinctive enough for interaction, familiar
     enough not to impose a brand. The 700/300 pair clears AA as link text and
     as a filled control in its respective theme. */
  --fertig-ac:  light-dark(var(--fertig-blue-700), var(--fertig-blue-300)); /* yours */
  --fertig-on-ac: light-dark(#f7f8fb, #07071c); /* off-white / ink text on the accent */

  /* depth: a soft surface shadow and a broader overlay shadow.
     --fertig-sh is an ordinary colour — override it with any notation you like, and
     every shadow in the sheet retints. Lighter than a pure black so the shadows
     read as diffuse atmosphere rather than dark bars. The alphas are taken with
     color-mix against transparent rather than relative colour: same result, and
     it is three years older, which keeps Safari 17.5 inside the floor. */
  --fertig-sh:  oklch(24% .014 270);
  --fertig-a1: .08; --fertig-a2: .10;
  --fertig-sh1: color-mix(in srgb, var(--fertig-sh) calc(var(--fertig-a1) * 100%), transparent);
  --fertig-sh2: color-mix(in srgb, var(--fertig-sh) calc(var(--fertig-a2) * 100%), transparent);
  --fertig-up:  0 1px 2px var(--fertig-sh1), 0 0 0 1px var(--fertig-bd);
  --fertig-up2: 0 2px 4px var(--fertig-sh1), 0 12px 32px -12px var(--fertig-sh2);
  --fertig-dn:  var(--fertig-up);
  --fertig-caps: .74em/1.4 var(--fertig-f);

  /* motion. One decelerating curve for things arriving, one accelerating for
     things leaving, and a symmetric one for state that toggles in place. */
  --fertig-ease-out: cubic-bezier(.22, 1, .36, 1);
  --fertig-ease-in:  cubic-bezier(.55, 0, 1, .45);
  --fertig-ease:     cubic-bezier(.4, 0, .2, 1);
  --fertig-dur-1: .12s;   /* a control reacting under the pointer */
  --fertig-dur-2: .2s;    /* something appearing or leaving */
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme=light]) { --fertig-a1: .34; --fertig-a2: .30 }
}
/* --fertig-on-ac has to survive any --fertig-ac override, or a custom accent ships an
   unreadable label. contrast-color() does that properly but has almost no
   support; relative colour reaches the same light-or-dark decision from the
   accent's own lightness, and has been cross-engine since 2024. The endpoints
   deliberately stop short of absolute black and white. Both are additive —
   the measured light-dark() pair above stands if neither exists. */
@supports (color: oklch(from red l c h)) {
  :root { --fertig-on-ac: oklch(from var(--fertig-ac) clamp(.06, (l - .62) * -100, .96) 0 h) }
}
@supports (color: contrast-color(red)) {
  :root { --fertig-on-ac: color-mix(in oklab, contrast-color(var(--fertig-ac)) 96%, var(--fertig-ac)) }
}
[data-theme=light] { color-scheme: light; --fertig-a1: .08; --fertig-a2: .10 }
[data-theme=dark]  { color-scheme: dark;  --fertig-a1: .34; --fertig-a2: .30 }

/* Named accents. The ramps already exist, so this is a mapping table: put
   data-accent on any element and everything inside it — links, focus rings,
   filled buttons, the caret, the selection — follows. Scoped rather than
   global, so one section of a page can differ from the rest.
   Each pair is measured: the light tone clears AA as link text on the content surface, the
   dark tone clears it on the dark ground. */
[data-accent=blue]   { --fertig-ac: light-dark(var(--fertig-blue-700),   var(--fertig-blue-300)) }
[data-accent=cyan]   { --fertig-ac: light-dark(var(--fertig-cyan-700),   var(--fertig-cyan-300)) }
[data-accent=green]  { --fertig-ac: light-dark(var(--fertig-green-700),  var(--fertig-green-300)) }
[data-accent=amber]  { --fertig-ac: light-dark(var(--fertig-amber-700),  var(--fertig-amber-300)) }
[data-accent=red]    { --fertig-ac: light-dark(var(--fertig-red-700),    var(--fertig-red-300)) }
[data-accent=violet] { --fertig-ac: light-dark(var(--fertig-violet-700), var(--fertig-violet-300)) }
[data-accent=gray]   { --fertig-ac: light-dark(var(--fertig-gray-700),   var(--fertig-gray-300)) }

/* ---- base --------------------------------------------------- */
*, ::before, ::after { box-sizing: border-box }
/* three prefixed/legacy-looking declarations survive in this sheet, and only
   because no engine ships an alternative today: this one (Safari has no
   unprefixed text-size-adjust), the autofill repaint below, and the progress
   pseudo-elements. Everything else here is unprefixed modern CSS. */
html { font: 16px/1.65 var(--fertig-f); -webkit-text-size-adjust: 100%; tab-size: 2 }
body {
  margin: 0; background: var(--fertig-bg); color: var(--fertig-fg);
  overflow-wrap: break-word;                  /* a pasted URL cannot widen the page */
  font-variant-numeric: slashed-zero tabular-nums;
}
::selection { background: color-mix(in srgb, var(--fertig-ac) 28%, transparent) }
/* No border-radius here: an outline already follows the
   element's own corners, and setting one would reshape whatever takes focus —
   a focused dialog would snap from --fertig-rw to --fertig-r for as long as it is focused. */
:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--fertig-ac) 52%, transparent);
  outline-offset: 2px;
}
img, video, iframe { display: block; max-width: 100%; height: auto }
/* svg is left inline: it is as often an icon inside a sentence or a button as
   it is a standalone figure, and display:block drops the icon onto its own row */
svg { max-width: 100%; vertical-align: middle }
img, video { border-radius: var(--fertig-rw); box-shadow: var(--fertig-up2) }

/* ---- layout: full-width page containers ---------------------- */
/* The shell is body's own children — or the children of the single wrapper a
   framework insists on (#root, #app, #__next, or your own [data-fertig]).
   Without this a React page silently loses the content surface and gutters,
   and nothing on screen says why. The wrapper list sits inside
   :where() so every selector keeps the specificity it had as `body > *`. */
body > *,
body > :where(#root, #app, #__next, [data-fertig]) > * {
  padding-inline: var(--fertig-g);
}
/* the wrapper is not itself a shell element; it just gets out of the way */
body > :where(#root, #app, #__next, [data-fertig]) { padding-inline: 0 }
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > :is(header, main, footer) {
  background: var(--fertig-bg);
}
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > header { padding-block: 2.4rem .8rem }
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > main { padding-bottom: 2.8rem }
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > footer {
  padding-block: 1.2rem 2rem; color: var(--fertig-mut); font-size: .85em;
  border-top: 1px solid var(--fertig-bd); border-bottom: 1px solid var(--fertig-bd);
}
/* nav = unified toolbar: full-bleed, opaque, contents on the measure */
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav {
  position: sticky; top: 0; z-index: 9;
  /* the toolbar is full-bleed but its contents line up with the page column.
     --fertig-nw is that column: it follows --fertig-w, so overriding the measure moves the
     toolbar with it, and .wide re-points it at the wide column instead. */
  --fertig-nw: var(--fertig-w);
  padding-inline: max(var(--fertig-g), calc(50% - var(--fertig-nw) / 2 + var(--fertig-g)),
                  env(safe-area-inset-left), env(safe-area-inset-right));
  display: flex; flex-wrap: wrap; gap: 1.1rem; align-items: center;
  padding-block: calc(.6rem + env(safe-area-inset-top, 0px)) .6rem;
  background: var(--fertig-tb); color: var(--fertig-fg);
  border-bottom: 1px solid var(--fertig-bd); font-size: .92em;
  box-shadow: 0 1px 10px color-mix(in srgb, var(--fertig-sh) 4%, transparent);
}
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav a { color: var(--fertig-fg); text-decoration: none; opacity: .75 }
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav a:hover { opacity: 1 }
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav :is(strong, b) a { opacity: 1 }        /* the wordmark is not a link among links */
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav ul {
  display: flex; flex-wrap: wrap; gap: 1.1rem; list-style: none; padding: 0; margin: 0;
  flex: 1;                       /* takes the slack, so a trailing control sits at the end */
}
/* On a narrow screen, keep the identity and trailing control together while
   the links get one stable, horizontally scrollable row of their own. */
@media (width <= 40rem) {
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav { row-gap: .55rem }
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav > ul {
    order: 2; flex: 0 0 100%; flex-wrap: nowrap; overflow-x: auto;
    overscroll-behavior-x: contain; scrollbar-width: none;
  }
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav > ul::-webkit-scrollbar { display: none }
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav > :last-child:not(ul) { margin-inline-start: auto }
}

/* ---- type --------------------------------------------------- */
h1, h2, h3, h4, h5, h6 { line-height: 1.3; margin: 1.4rem 0 .7rem; text-wrap: balance }
h1 { font-size: clamp(2rem, 1.55rem + 1.5vw, 2.8rem); letter-spacing: -.035em }
h2 { font-size: clamp(1.35rem, 1.2rem + .5vw, 1.65rem); letter-spacing: -.02em; margin-top: 2.4rem }
h3 { font-size: 1.12rem; letter-spacing: -.008em; margin-top: 1.9rem }
h4 { font-size: 1rem }
h5, h6 { font: 700 var(--fertig-caps); text-transform: uppercase; letter-spacing: .1em; color: var(--fertig-mut) }
:is(h1,h2,h3,h4,h5,h6):first-child { margin-top: 0 }
@supports (text-box: trim-both cap alphabetic) {
  h1, h2, h3, h4 { text-box: trim-both cap alphabetic }   /* optical, not metric */
}
/* …but a new section still needs air above its heading */
:is(section, article) + :is(section, article) { margin-top: 2.8rem }

p, ul, ol, dl, pre, blockquote, table, figure, details, fieldset, hr { margin: 1.1rem 0 }
p { text-wrap: pretty }
a { color: var(--fertig-ac); text-underline-offset: 3px; text-decoration-thickness: 1px }
a:hover { text-decoration-thickness: 2px }   /* quiet — no invert mid-paragraph */
small { font-size: .85em; color: var(--fertig-mut) }
hr { border: 0; border-top: 1px solid var(--fertig-bd); margin: 2.2rem 0 }
mark { background: light-dark(#ffe58f, #5c4a15); color: inherit; border-radius: 3px; padding: 0 .15em }
abbr[title] { text-decoration: underline dotted; cursor: help }
blockquote {
  border-inline-start: 2px solid var(--fertig-bd);
  padding-block: .15rem; padding-inline: 1.2rem 0; color: var(--fertig-mut);
}
ul, ol { padding-inline-start: 1.5rem }
li::marker { color: var(--fertig-mut) }
/* minmax(0, 1fr), not 1fr: a bare 1fr track has min-width:auto, so a long
   value cannot shrink and pushes the page sideways on a narrow screen. */
dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .35rem 1rem }
dt { color: var(--fertig-mut) }
dd { margin: 0 }
figcaption { font-size: .85em; color: var(--fertig-mut); text-align: center }

/* ---- code --------------------------------------------------- */
code, kbd, samp, pre { font-family: var(--fertig-fm); font-size: .9em }
:not(pre) > code, samp {
  background: var(--fertig-face); border: 1px solid var(--fertig-bd);
  border-radius: var(--fertig-rs); padding: .08em .35em;
}
kbd {
  background: var(--fertig-face); border: 1px solid var(--fertig-bd); border-bottom-width: 2px;
  border-radius: var(--fertig-rs); padding: .08em .4em; box-shadow: var(--fertig-up);
}
pre {
  background: var(--fertig-face); border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw);
  padding: 1rem; overflow-x: auto;
}
pre code { background: none; border: 0; padding: 0; font-size: 1em }

/* Syntax theme. The sheet ships no highlighter — bring highlight.js or Prism
   and the tokens land in this palette, in both themes. */
:root {
  --fertig-syn-comment: light-dark(#6b7280, #8b949e);
  --fertig-syn-key:     light-dark(#7c3aed, #c4a7fb);
  --fertig-syn-str:     light-dark(#15803d, #7ee787);
  --fertig-syn-num:     light-dark(#b45309, #f0b866);
  --fertig-syn-fn:      light-dark(#1d4ed8, #93c5fd);
  --fertig-syn-attr:    light-dark(#b91c1c, #ff9492);
}
:is(.hljs-comment, .hljs-quote, .token.comment, .token.prolog, .token.doctype) {
  color: var(--fertig-syn-comment); font-style: italic;
}
:is(.hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-type, .hljs-built_in,
    .token.keyword, .token.atrule, .token.rule, .token.important, .token.selector) {
  color: var(--fertig-syn-key);
}
:is(.hljs-string, .hljs-meta-string, .token.string, .token.attr-value, .token.char) {
  color: var(--fertig-syn-str);
}
:is(.hljs-number, .hljs-symbol, .hljs-bullet, .token.number, .token.boolean,
    .token.constant, .token.unit) {
  color: var(--fertig-syn-num);
}
:is(.hljs-title, .hljs-name, .hljs-section, .hljs-function,
    .token.tag, .token.function, .token.class-name) {
  color: var(--fertig-syn-fn);
}
:is(.hljs-attr, .hljs-attribute, .hljs-variable, .hljs-template-variable,
    .token.attr-name, .token.property, .token.variable) {
  color: var(--fertig-syn-attr);
}
:is(.hljs-punctuation, .hljs-operator, .token.punctuation, .token.operator) {
  color: var(--fertig-mut);
}
:is(.hljs-emphasis, .token.italic) { font-style: italic }
:is(.hljs-strong, .token.bold) { font-weight: 700 }
:is(.hljs-deletion, .token.deleted) { color: light-dark(#c0392b, #ff6b6b) }
:is(.hljs-addition, .token.inserted) { color: light-dark(#15803d, #7ee787) }

/* ---- table -------------------------------------------------- */
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .92em }
th, td {
  border-bottom: 1px solid var(--fertig-bd); padding-block: .55rem;
  padding-inline: 0 .7rem; text-align: start;
}
th:last-child, td:last-child { padding-inline-end: 0 }
thead th {
  border-bottom: 0; background: var(--fertig-face);
  font: 700 var(--fertig-caps); text-transform: uppercase; letter-spacing: .09em; color: var(--fertig-mut);
  padding: .5rem .7rem;
}
thead th:first-child { border-start-start-radius: var(--fertig-r); border-end-start-radius: var(--fertig-r) }
thead th:last-child {
  padding-inline-end: .7rem;
  border-start-end-radius: var(--fertig-r); border-end-end-radius: var(--fertig-r);
}
tbody tr:last-child td { border-bottom: 0 }
/* a totals row is not just another row */
tfoot td { font-weight: 700; border-top: 1px solid var(--fertig-bd); border-bottom: 0 }
/* our text-align was overriding HTML own alignment; honour it for numbers */
:is(th, td)[align=right]  { text-align: right }
:is(th, td)[align=center] { text-align: center }
tbody tr:hover { background: var(--fertig-face) }
@media (width <= 40rem) {
  table { display: block; overflow-x: auto; overscroll-behavior-x: contain }
  table :is(th, td) { white-space: nowrap }
}
caption { text-align: start; color: var(--fertig-mut); font-size: .85em; padding-bottom: .35rem }

/* ---- forms --------------------------------------------------- */
button, input, select, textarea { font: inherit; color: inherit }
label { display: block; font-size: .85em; color: var(--fertig-mut) }
label:has(input) { font-size: 1rem; color: var(--fertig-fg) }
/* a label following a field belongs to the next one: give it air, except in a
   .row, where the toolbar is horizontal and the margin would knock it askew */
:is(input, select, textarea) + label { margin-top: .9rem }
textarea { width: 100%; min-height: 5rem; resize: vertical }
@supports (field-sizing: content) {
  textarea { field-sizing: content; max-height: 24rem }
}
input:not([type=checkbox],[type=radio],[type=range],[type=color],[type=file],[type=image],[type=submit],[type=button],[type=reset]),
select, textarea {
  display: block; width: 100%; padding: .52em .7em; background: var(--fertig-el);
  border: 1px solid transparent; border-radius: var(--fertig-r); box-shadow: var(--fertig-dn);
}
select {
  appearance: none; padding-inline-end: 2.2rem;
  /* a stroked chevron, drawn as two 1.5px bars, so the engines without
     appearance: base-select show the same mark as the ones with it */
  background-image:
    linear-gradient(45deg,  transparent calc(50% - 1px), currentColor calc(50% - 1px),
                            currentColor calc(50% + .5px), transparent calc(50% + .5px)),
    linear-gradient(-45deg, transparent calc(50% - 1px), currentColor calc(50% - 1px),
                            currentColor calc(50% + .5px), transparent calc(50% + .5px));
  background-position: calc(100% - 17px) 54%, calc(100% - 12px) 54%;
  background-size: 6px 6px; background-repeat: no-repeat;
}
select:dir(rtl) { background-position: 12px 54%, 17px 54% }
select[multiple] { background-image: none; padding-inline-end: .6em }

/* a <select> whose dropdown can finally be styled like the rest of the sheet */
@supports (appearance: base-select) {
  select, ::picker(select) { appearance: base-select }
  select {
    display: flex; align-items: center; justify-content: space-between;
    gap: .6em; background-image: none; padding-inline-end: .7em;
  }
  select::picker-icon {
    content: ""; inline-size: .45em; block-size: .45em; flex: none;
    border: solid currentColor; border-width: 0 1px 1px 0;
    rotate: 45deg; translate: 0 -.15em; opacity: .7;
    transition: rotate var(--fertig-dur-1) var(--fertig-ease), translate var(--fertig-dur-1) var(--fertig-ease);
  }
  select:open::picker-icon { rotate: 225deg; translate: 0 .1em }
  ::picker(select) {
    min-width: anchor-size(width);            /* never narrower than the field */
    border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw); padding: .35rem;
    background: var(--fertig-el); margin-block-start: .35rem;
    box-shadow: var(--fertig-up2), 0 24px 44px -18px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
  }
  option {
    display: flex; align-items: center; gap: .5em; padding: .4em .6em;
    border-radius: calc(var(--fertig-r) - 2px);
  }
  option:hover { background: var(--fertig-face) }
  option:checked { background: var(--fertig-ac); color: var(--fertig-on-ac) }
  option::checkmark { order: 1; margin-inline-start: auto; content: "✓" }
  /* The picker drops out of the field the way the menu popover does. It lives
     in the top layer, so display and overlay have to transition discretely or
     it would pop out on close with the fade never running. */
  @media (prefers-reduced-motion: no-preference) and (update: fast) {
    ::picker(select) {
      opacity: 0; translate: 0 -4px; transform-origin: top center;
      transition: opacity .13s var(--fertig-ease-out), translate .13s var(--fertig-ease-out),
                  display .13s allow-discrete, overlay .13s allow-discrete;
    }
    select:open::picker(select) { opacity: 1; translate: 0 }
    @starting-style { select:open::picker(select) { opacity: 0; translate: 0 -4px } }
  }
}
:is(input,select,textarea):hover:not(:disabled) { background-color: color-mix(in srgb, var(--fertig-face) 55%, var(--fertig-el)) }
:is(input,select,textarea):focus-visible { border-color: color-mix(in srgb, var(--fertig-ac) 55%, transparent) }
:is(input,select,textarea)[data-variant=secondary] {
  background: var(--fertig-face); border-color: transparent; box-shadow: none;
}

/* A compound field keeps its label, control, help and error text together. */
[data-field] { display: grid; gap: .35rem; align-content: start }
[data-field] + [data-field] { margin-top: .9rem }
[data-field] > * { margin-block: 0 }
[data-field] > :is(small, [role=alert]) { font-size: .82em }
[data-field] > [role=alert] { color: light-dark(var(--fertig-red-700), var(--fertig-red-300)) }
[data-field]:has([aria-invalid=true]) > label { color: light-dark(var(--fertig-red-700), var(--fertig-red-300)) }

/* Input group: one focus surface around an input, addons and compact actions. */
[data-input-group] {
  display: flex; align-items: center; inline-size: 100%; overflow: clip;
  background: var(--fertig-el); border: 1px solid transparent;
  border-radius: var(--fertig-r); box-shadow: var(--fertig-dn);
}
[data-input-group] > :is(input, select, textarea) {
  flex: 1 1 auto; min-inline-size: 0; border: 0; border-radius: 0;
  background-color: transparent; box-shadow: none; outline: 0;
}
[data-input-group] > :is(input, select, textarea):hover:not(:disabled) {
  background-color: transparent;
}
[data-input-group] > :where(span, small, output) {
  flex: none; padding-inline: .75em; color: var(--fertig-mut); white-space: nowrap;
}
[data-input-group] > button {
  align-self: stretch; min-width: 0; border: 0; border-radius: 0;
  background: transparent; box-shadow: none;
}
[data-input-group]:focus-within {
  border-color: color-mix(in srgb, var(--fertig-ac) 55%, transparent);
  box-shadow: var(--fertig-up), 0 0 0 3px color-mix(in srgb, var(--fertig-ac) 16%, transparent);
}
[data-input-group]:has([aria-invalid=true]) {
  border-color: light-dark(var(--fertig-red-700), var(--fertig-red-300));
}
[data-input-group]:has(textarea) { align-items: end }

/* One native field keeps OTP autofill, paste and screen-reader behavior. */
input[data-otp] {
  inline-size: min(100%, 16ch); font: 650 1.25rem/1.4 var(--fertig-fm);
  letter-spacing: .55em; text-indent: .55em; text-align: center;
}
input[type=checkbox], input[type=radio] {
  width: 1.15em; height: 1.15em; margin: 0; margin-inline-end: .45em; vertical-align: -.18em;
}
/* re-resolved here rather than inherited from :root, so a native control inside
   a [data-accent] or [data-tone] block is tinted with that block's accent */
:is(input[type=checkbox], input[type=radio], input[type=range], progress, select[multiple]) {
  accent-color: var(--fertig-ac);
}
:is(input, textarea, [contenteditable]) { caret-color: var(--fertig-ac) }
label:has(input[type=checkbox]), label:has(input[type=radio]) { line-height: 1.9 }
input[type=range] { width: 100%; height: 1.15em; margin-inline: 0 }
::placeholder { color: var(--fertig-mut) }
@supports selector(::spelling-error) {
  ::spelling-error { text-decoration: wavy underline light-dark(#c0392b, #ff6b6b) }
  ::grammar-error  { text-decoration: wavy underline light-dark(#9a6700, #d29922) }
}
:is(input, textarea):read-only:not(:disabled) { background: var(--fertig-face); cursor: default }
label:has(+ :required)::after, label:has(:required)::after {
  content: " *"; color: var(--fertig-ac);            /* mark what is actually required */
}
/* the browser paints autofilled fields yellow; repaint them ourselves */
input:autofill, input:autofill:hover, input:autofill:focus {
  -webkit-text-fill-color: var(--fertig-fg);
  box-shadow: var(--fertig-dn), inset 0 0 0 100px var(--fertig-el);
  caret-color: var(--fertig-ac);
}
:disabled { color: var(--fertig-mut); cursor: not-allowed; box-shadow: none }
/* No notched border: the legend becomes the panel title. The float is not
   nostalgia — a legend keeps its magic placement under block, grid and flex
   fieldsets alike, and floating it is still the only way out. */
fieldset {
  border: 1px solid transparent; border-radius: var(--fertig-rw); padding: 1rem;
  background: var(--fertig-el); box-shadow: var(--fertig-up);
}
fieldset:focus-within { border-color: color-mix(in srgb, var(--fertig-ac) 45%, var(--fertig-bd)) }
legend {
  float: left; width: 100%; margin-bottom: .8rem; padding: 0 0 .6rem;
  border-bottom: 1px solid var(--fertig-bd);
  font-size: .95em; font-weight: 700; color: var(--fertig-fg);
}
legend + * { clear: left }

button, [type=submit], [type=button], [type=reset],
:where(a.primary, a[role=button]), ::file-selector-button {
  display: inline-block; text-decoration: none; text-align: center;
  padding: .5em 1.1em; background: var(--fertig-face); color: var(--fertig-fg); cursor: pointer;
  border: 1px solid transparent; border-radius: var(--fertig-r); box-shadow: none;
  font: inherit; font-weight: 550; min-width: 5rem;
}
:is(button,[type=submit],[type=button],[type=reset],a.primary,a[role=button]):hover:not(:disabled) {
  background: color-mix(in srgb, var(--fertig-fg) 7%, var(--fertig-face));
}
[type=submit], .primary { background: var(--fertig-ac); border-color: transparent; color: var(--fertig-on-ac) }
/* Filled controls must not keep their enabled accent after :disabled. This
   follows the filled rule deliberately: equal-specificity declarations later
   in the cascade restore the quiet control surface and muted text. */
:is(button,[type=submit],[type=button],[type=reset]):disabled {
  background: var(--fertig-face); border-color: transparent; color: var(--fertig-mut);
}
/* The filled button lightens on hover. color-mix does that in oklab, which is
   close enough and is Baseline; relative colour does it by adding to L
   directly, which holds the hue exactly when --fertig-ac is a wide-gamut override.
   Both derive from --fertig-ac, so a custom accent never needs a second declaration. */
:is([type=submit], .primary, a.primary):hover:not(:disabled) {
  background: color-mix(in oklab, var(--fertig-ac), #fff 9%);
}
@supports (color: oklch(from red l c h)) {
  :is([type=submit], .primary, a.primary):hover:not(:disabled) {
    background: oklch(from var(--fertig-ac) calc(l + .05) c h);
  }
}

/* button detail: sizes, variants, toggles, icon and block forms */
/* Scoped to controls. data-size, data-block and data-variant are names other
   component libraries and framework wrappers use too, and a bare attribute
   selector would restyle a stranger's <span data-size="sm"> on contact. */
:is(button, a, input, select, label)[data-size=sm] { padding: .2em .7em; font-size: .85em; min-width: 0 }
:is(button, a, input, select, label)[data-size=lg] { padding: .55em 1.5em; font-size: 1.05em }
button[data-icon] { min-width: 0; padding: .38em .62em; line-height: 1 }
:is(button, a, input, select, label)[data-block] { display: block; width: 100%; min-width: 0 }
:is(button, a, input)[data-variant=ghost] { background: none; border-color: transparent; box-shadow: none }
:is(button, a, input)[data-variant=ghost]:hover:not(:disabled) { background: var(--fertig-face); border-color: var(--fertig-bd) }
:is(button, a, input)[data-variant=soft] {
  background: color-mix(in srgb, var(--fertig-ac) 13%, var(--fertig-el));
  border-color: transparent; box-shadow: none; color: var(--fertig-ac);
}
:is(button, a, input)[data-variant=soft]:hover:not(:disabled) {
  background: color-mix(in srgb, var(--fertig-ac) 20%, var(--fertig-el));
}
:is(button, a, input)[data-variant=outline] {
  background: transparent; border-color: var(--fertig-bd); box-shadow: none;
}
:is(button, a, input)[data-variant=link] {
  background: none; border: 0; box-shadow: none; min-width: 0; padding-inline: .2em;
  color: var(--fertig-ac); text-decoration: underline; text-underline-offset: 3px;
}
:is(button, a, input)[data-variant=link]:hover:not(:disabled) { background: none; text-decoration-thickness: 2px }
button[aria-pressed=true] { background: var(--fertig-face); color: var(--fertig-ac); font-weight: 700 }
button[aria-pressed=true]:hover:not(:disabled) {
  background: color-mix(in srgb, var(--fertig-fg) 7%, var(--fertig-face));
}

/* ---- disclosure / misc -------------------------------------- */
details {
  border: 1px solid transparent; border-radius: var(--fertig-rw);
  padding: .8rem 1.1rem; background: var(--fertig-el); box-shadow: var(--fertig-up);
}
summary { cursor: pointer; font-weight: 700; list-style: none }
summary::before { content: "▸ "; color: var(--fertig-mut) }
details[open] > summary::before { content: "▾ " }
details[open] > summary { margin-bottom: .6rem }
dialog {
  --fertig-dialog-inset: 1.25rem;
  background: var(--fertig-el); color: var(--fertig-fg); border: 1px solid transparent;
  border-radius: var(--fertig-rw); padding: var(--fertig-dialog-inset); max-width: min(30rem, 92dvw);
  box-shadow: var(--fertig-up2), 0 28px 70px -24px color-mix(in srgb, var(--fertig-sh) 34%, transparent);
}
dialog > :last-child:not(footer) { margin-bottom: 0 }
dialog, [popover] { overscroll-behavior: contain }
dialog::backdrop { background: color-mix(in srgb, var(--fertig-sh) 42%, transparent) }

/* dialog anatomy: an optional header and footer, pinned while the body scrolls.
   Sticky rather than a grid, because a classless sheet has no wrapper around
   the loose middle children to make a grid row out of.
   No dividing rules: a short dialog cut into three banded strips is the look
   of a decade-old modal. The separation is spacing, and the hairline only
   arrives when there is actually something scrolling under it — see below. */
dialog:has(> :is(header, footer)) { max-block-size: min(44rem, 85dvh); overflow: auto }
dialog > :is(header, footer) {
  position: sticky; z-index: 1;
  margin-inline: calc(0rem - var(--fertig-dialog-inset));
  background: var(--fertig-el);          /* the dialog's own surface, so it reads as one sheet */
}
dialog > header {
  inset-block-start: calc(0rem - var(--fertig-dialog-inset));
  margin-block: calc(0rem - var(--fertig-dialog-inset)) .35rem;
  padding: var(--fertig-dialog-inset) var(--fertig-dialog-inset) .5rem; font-weight: 700;
  border-radius: var(--fertig-rw) var(--fertig-rw) 0 0;
}
/* The title carries the header's weight, so it needs no margins of its own,
   and it is UI text: it takes --fertig-f, the interface face, not whatever
   display font the page gave its headings — a monospaced heading face reads
   as a terminal once it sits on a dialog. :is([open], :modal) is there for
   weight — a page's `.prose h3` outranks a bare element chain, and the title
   would come out sized and lettered for the article column it is nowhere near. */
dialog:is([open], :modal) > header > :is(h1, h2, h3, h4, p) {
  margin: 0; font-family: var(--fertig-f); font-size: 1.1rem; font-weight: 600;
  line-height: 1.3; letter-spacing: -.01em;
}
dialog > footer {
  inset-block-end: calc(0rem - var(--fertig-dialog-inset));
  margin-block: 1.35rem calc(0rem - var(--fertig-dialog-inset));
  padding: .5rem var(--fertig-dialog-inset) var(--fertig-dialog-inset);
  border-radius: 0 0 var(--fertig-rw) var(--fertig-rw);
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  justify-content: flex-end;                  /* actions sit at the end edge */
}
/* The hairline is scroll state, not decoration: it appears under the header
   once the body has scrolled, and leaves the footer once you reach the end.
   A scroll timeline on a container with nothing to scroll is inactive, so a
   dialog that fits shows no rules at all and no script decides that. */
@supports (animation-timeline: scroll()) {
  dialog > header {
    animation: fertig-rule-under linear both;
    animation-timeline: scroll(nearest); animation-range: 0 1.2rem;
  }
  dialog > footer {
    animation: fertig-rule-over linear both;
    animation-timeline: scroll(nearest); animation-range: calc(100% - 1.2rem) 100%;
  }
  @keyframes fertig-rule-under { from { box-shadow: none } to { box-shadow: 0 1px 0 var(--fertig-bd) } }
  @keyframes fertig-rule-over  { from { box-shadow: 0 -1px 0 var(--fertig-bd) } to { box-shadow: none } }
}
/* a note beside the buttons pushes them over, rather than sitting under them */
dialog > footer > :is(p, span):first-child { margin: 0; margin-inline-end: auto; color: var(--fertig-mut) }
/* sheets come up, they do not appear */
@media (prefers-reduced-motion: no-preference) and (update: fast) {
  dialog, dialog::backdrop {
    transition: opacity var(--fertig-dur-2) var(--fertig-ease-out), translate var(--fertig-dur-2) var(--fertig-ease-out),
                display var(--fertig-dur-2) allow-discrete, overlay var(--fertig-dur-2) allow-discrete;
  }
  dialog { opacity: 0; translate: 0 10px }
  dialog[open] { opacity: 1; translate: 0 }
  dialog::backdrop { opacity: 0 }
  dialog[open]::backdrop { opacity: 1 }
  @starting-style { dialog[open] { opacity: 0; translate: 0 10px } }
  @starting-style { dialog[open]::backdrop { opacity: 0 } }
  [popover] {
    opacity: 0; translate: 0 -4px;
    transition: opacity .13s var(--fertig-ease-out), translate .13s var(--fertig-ease-out),
                display .13s allow-discrete, overlay .13s allow-discrete;
  }
  [popover]:popover-open { opacity: 1; translate: 0 }
  @starting-style { [popover]:popover-open { opacity: 0; translate: 0 -4px } }
}
search { display: block; margin-block: 1.1rem }   /* a landmark needs its own rhythm */
::target-text { background: color-mix(in srgb, var(--fertig-ac) 28%, transparent) }
aside {
  border: 1px solid color-mix(in srgb, var(--fertig-ac) 18%, transparent);
  border-radius: var(--fertig-rw);
  background: color-mix(in srgb, var(--fertig-ac) 7%, var(--fertig-el)); padding: .8rem 1.1rem;
  margin-block: 1.1rem;          /* or the next block sits flush against it */
}
progress { width: 100%; height: .5rem; border: 0; border-radius: var(--fertig-rp); background: var(--fertig-face) }
progress::-webkit-progress-bar { background: var(--fertig-face); border-radius: var(--fertig-rp) }
progress::-webkit-progress-value { background: var(--fertig-ac); border-radius: var(--fertig-rp) }
progress::-moz-progress-bar { background: var(--fertig-ac); border-radius: var(--fertig-rp) }

/* details can only animate to auto height once keywords interpolate */
@supports selector(::details-content) {
  @media (prefers-reduced-motion: no-preference) and (update: fast) {
    /* on details, not :root: allow-keywords is inherited, and setting it
       document-wide would quietly change how your own height:auto
       transitions behave */
    details { interpolate-size: allow-keywords }
    details::details-content {
      block-size: 0; overflow: clip;
      transition: block-size .28s var(--fertig-ease), content-visibility .28s allow-discrete;
    }
    details[open]::details-content { block-size: auto }
  }
}

/* ---- components, addressed by ARIA rather than by class ----- */

/* segmented control: <div role=group> of buttons */
/* max-inline-size + scroll, or a group of four or more buttons pushes the
   whole page sideways on a narrow screen. It scrolls rather than wraps:
   wrapping would break the joined-pill run of radii into separate rows.
   The block axis is clipped rather than left to compute to auto alongside it:
   Safari paints a vertical scrollbar for a sub-pixel of overflow, and the row
   has no vertical overflow worth scrolling to in the first place. */
[role=group] {
  display: inline-flex; vertical-align: middle; max-inline-size: 100%; padding: .2rem;
  overflow: auto clip; overscroll-behavior-x: contain;
  border-radius: var(--fertig-rp); background: var(--fertig-face);
}
[role=group] > * { border: 0; border-radius: var(--fertig-rp); margin: 0; box-shadow: none; min-width: 0 }
/* The buttons fill the group exactly and the group clips on the block axis,
   so an outset ring would be sliced off. Inside the edge, it is all there. */
[role=group] > :focus-visible { outline-offset: -3px }
[role=group] > [aria-pressed=true] {
  background: var(--fertig-el); color: var(--fertig-fg); font-weight: 650; box-shadow: var(--fertig-up);
}
/* The press nudge would push a button half a pixel past the scroll port and
   flash a scrollbar, so joined buttons stay put and read the press by shadow. */
[role=group] > :active:not(:disabled) { translate: none }

/* tabs: <div role=tablist> of <button role=tab aria-selected> */
[role=tablist] {
  display: flex; gap: .15rem; max-inline-size: 100%; overflow-x: auto;
  margin: 1.1rem 0; padding: .25rem; border: 0; border-radius: var(--fertig-rp);
  background: var(--fertig-face); scrollbar-width: none;
}
[role=tablist]::-webkit-scrollbar { display: none }
[role=tab] {
  flex: none; background: none; border: 0; box-shadow: none; min-width: 0; color: var(--fertig-mut);
  padding: .4em .9em; border-radius: var(--fertig-rp); margin: 0;
}
[role=tab]:hover:not(:disabled) { background: var(--fertig-face); color: var(--fertig-fg) }
[role=tab][aria-selected=true] { background: var(--fertig-el); color: var(--fertig-fg); box-shadow: var(--fertig-up) }

/* switch: <input type=checkbox role=switch> */
input[type=checkbox][switch],
input[type=checkbox][role=switch] {
  appearance: none; width: 2.5em; height: 1.45em; border-radius: var(--fertig-rp); border: 0;
  background: color-mix(in srgb, var(--fertig-fg) 20%, transparent);
  position: relative; cursor: pointer; vertical-align: -.3em; margin-inline-end: .5em;
}
input[switch]::after, input[role=switch]::after {
  content: ""; position: absolute; inset-block-start: .14em; inset-inline-start: .14em;
  width: 1.17em; height: 1.17em; border-radius: 50%; background: #fff; box-shadow: var(--fertig-up);
  transition: translate .16s var(--fertig-ease-out);
}
input[switch]:checked, input[role=switch]:checked { background: var(--fertig-ac) }
/* translate is physical, so the knob is told which way to travel */
input[switch]:checked::after, input[role=switch]:checked::after { translate: 1.05em 0 }
:is(input[switch], input[role=switch]):checked:dir(rtl)::after { translate: -1.05em 0 }

/* menu: any [popover] */
[popover] {
  background: var(--fertig-el); color: var(--fertig-fg); border: 1px solid transparent;
  border-radius: var(--fertig-rw); padding: .35rem; min-width: 12rem;
  max-block-size: 60dvh; overflow: auto; font-size: .95em;
  box-shadow: var(--fertig-up2), 0 24px 44px -18px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
}
/* anchor to the invoking button; without support it centres, which is fine.
   Deliberately NOT scoped to :popover-open — the close is transitioned, so a
   closing popover still paints for 130ms. Scoped, it would drop back to the
   UA's inset:0 centring on that frame and visibly fly across the viewport. */
@supports (position-area: block-end span-inline-end) {
  [popover] {
    position: fixed;
    inset: auto;                           /* clear the UA inset:0 centring */
    position-anchor: auto;                 /* the button that opened it */
    position-area: block-end span-inline-end;   /* keywords must share an axis */
    position-try-fallbacks: flip-block, flip-inline;
    margin: .35rem 0 0;
  }
  @supports (min-width: anchor-size(width)) {
    [popover] {
      min-width: max(12rem, anchor-size(width));
      position-visibility: anchors-visible;   /* hide with its anchor */
    }
  }
}
[popover] :is(ul, menu) { list-style: none; padding: 0; margin: 0 }
/* specificity has to clear the generic button rule (0,3,0) — hence :not(:disabled) */
[popover] :is(a, button):not(:disabled) {
  display: block; width: 100%; text-align: start; background: none; border: 0;
  box-shadow: none; min-width: 0; padding: .4em .6em; text-decoration: none;
  color: inherit; border-radius: calc(var(--fertig-r) - 2px); font-weight: 400;
}
[popover] :is(a, button):hover:not(:disabled) { background: var(--fertig-ac); color: var(--fertig-on-ac) }
[popover] hr { margin: .3rem 0 }

/* listbox: a selectable collection, whether standalone or inside a popover */
[role=listbox] {
  list-style: none; margin: 1rem 0; padding: .35rem;
  background: var(--fertig-el); border: 1px solid transparent;
  border-radius: var(--fertig-rw); box-shadow: var(--fertig-up);
}
[role=option] {
  display: flex; align-items: center; gap: .7rem; padding: .5rem .7rem;
  border-radius: var(--fertig-r); cursor: default;
}
[role=option]:is(:hover, :focus-visible) { background: var(--fertig-face) }
[role=option][aria-selected=true] {
  background: color-mix(in srgb, var(--fertig-ac) 13%, var(--fertig-el)); color: var(--fertig-ac);
}
[popover] [role=listbox] { margin: 0; padding: 0; border: 0; box-shadow: none }

/* breadcrumbs: <nav aria-label="Breadcrumb"><ol> */
nav[aria-label] ol { display: flex; flex-wrap: wrap; gap: .5em; list-style: none; padding: 0; font-size: .9em }
nav[aria-label] ol li + li::before { content: "/"; margin-inline-end: .5em; color: var(--fertig-mut) }

/* busy: [aria-busy=true] */
[aria-busy=true] { cursor: progress }
[aria-busy=true]::before {
  content: ""; display: inline-block; width: .85em; height: .85em; margin-inline-end: .55em;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  vertical-align: -.1em;
}
@media (prefers-reduced-motion: no-preference) and (update: fast) {
  [aria-busy=true]::before { animation: fertig-spin .6s linear infinite }
}
@keyframes fertig-spin { to { rotate: 360deg } }

/* tooltip: [data-tooltip]. Decorative only, and deliberately so: generated
   content is not reliably exposed to assistive tech and there is no way to
   dismiss it without script (WCAG 1.4.13), so put the same words in aria-label
   or aria-describedby and never say anything here that is said nowhere else.
   A scroll container clips it — [role=group] is one. */
[data-tooltip] { position: relative }
[data-tooltip]:is(:hover, :focus-visible, :active)::after {
  content: attr(data-tooltip); position: absolute;
  inset-block-end: 100%; inset-inline-start: 50%;
  translate: -50% -.45em; z-index: 10; white-space: nowrap; pointer-events: none;
  background: light-dark(#1d1d1f, #3a3a40); color: #f7f8fb; font-size: .8em;
  padding: .25em .55em; border-radius: var(--fertig-r); box-shadow: var(--fertig-up2);
}
/* translate is physical; the offset has to be told which way to go */
[data-tooltip]:dir(rtl)::after { translate: 50% -.45em }

/* tones: retint any component by redefining its accent */
[data-tone=ok]   { --fertig-ac: light-dark(var(--fertig-green-700), var(--fertig-green-300)) }
[data-tone=warn] { --fertig-ac: light-dark(var(--fertig-amber-700), var(--fertig-amber-300)) }
[data-tone=err]  { --fertig-ac: light-dark(var(--fertig-red-700),   var(--fertig-red-300)) }
[data-tone] .badge, .badge[data-tone] { color: var(--fertig-ac); border-color: var(--fertig-ac) }

/* ---- parity with the component libraries --------------------- */

/* avatar: works on an <img> or on initials in a <span> */
[data-avatar] {
  display: inline-grid; place-items: center; flex: none; overflow: hidden;
  inline-size: 2.5rem; aspect-ratio: 1; border-radius: 50%;
  background: var(--fertig-face); border: 1px solid var(--fertig-bd);
  font-size: .8em; font-weight: 700; color: var(--fertig-mut); object-fit: cover;
}
[data-avatar=sm] { inline-size: 1.75rem; font-size: .7em }
[data-avatar=lg] { inline-size: 3.5rem; font-size: 1em }

/* skeleton: put it on the element that is still loading */
[data-skeleton] {
  color: transparent; border-radius: var(--fertig-r); pointer-events: none;
  user-select: none; min-height: 1em;
  background: linear-gradient(90deg, var(--fertig-face),
              color-mix(in srgb, var(--fertig-fg) 9%, var(--fertig-face)), var(--fertig-face));
  background-size: 200% 100%;
}
[data-skeleton] * { visibility: hidden }
@media (prefers-reduced-motion: no-preference) and (update: fast) {
  [data-skeleton] { animation: fertig-shimmer 1.4s linear infinite }
  @keyframes fertig-shimmer { to { background-position: -200% 0 } }
}

/* surface: the same elevation language as a card, without card anatomy */
[data-surface] {
  background: var(--fertig-el); border: 1px solid transparent;
  border-radius: var(--fertig-rw); padding: 1rem; box-shadow: var(--fertig-up);
}
[data-surface=flat] { background: var(--fertig-face); box-shadow: none }
[data-surface=outline] { background: transparent; border-color: var(--fertig-bd); box-shadow: none }
[data-surface] > :first-child { margin-top: 0 }
[data-surface] > :last-child { margin-bottom: 0 }

/* item: avatar or icon, flexible content, and an optional trailing action */
[data-item] {
  display: flex; align-items: center; gap: .8rem; min-inline-size: 0;
  padding: .7rem .8rem; border-radius: var(--fertig-r); background: var(--fertig-el);
}
[data-item] + [data-item] { margin-top: .35rem }
[data-item] > :where(div, p) { flex: 1; min-inline-size: 0; margin: 0 }
[data-item] > :where(div, p) > * { display: block; margin-block: 0 }
:is(a, button)[data-item] {
  inline-size: 100%; min-width: 0; text-align: start; color: inherit; text-decoration: none;
}
:is(a, button)[data-item]:hover:not(:disabled) { background: var(--fertig-face) }

/* empty: a deliberate endpoint rather than a lonely paragraph in a panel */
[data-empty] {
  display: grid; justify-items: center; gap: .6rem; text-align: center;
  padding: clamp(2rem, 7vw, 4rem); border: 1px dashed var(--fertig-bd);
  border-radius: var(--fertig-rw); background: color-mix(in srgb, var(--fertig-face) 45%, transparent);
}
[data-empty] > * { max-inline-size: 32rem; margin-block: 0 }

/* sheet / drawer: the same <dialog>, docked to an edge.
   Docked to a physical edge on purpose — "right" means the right edge of the
   screen, not the end of the writing direction. inset is physical, so a drawer
   slides in from the edge its name promises no matter the direction. The
   translate is physical for the same reason: 100% of the drawer's own inline
   width moves it off the edge it came from. RTL readers still get the same
   right/left drawer as anyone else; direction would make "right" ambiguous. */
dialog[data-side] { max-width: none; margin: 0; border-radius: 0 }
dialog[data-side=right] {
  inset: 0 0 0 auto; block-size: 100dvh; inline-size: min(28rem, 92dvw);
  border-block: 0; border-inline-end: 0;
}
dialog[data-side=left] {
  inset: 0 auto 0 0; block-size: 100dvh; inline-size: min(28rem, 92dvw);
  border-block: 0; border-inline-start: 0;
}
dialog[data-side=bottom] {
  inset: auto 0 0 0; inline-size: 100%; max-block-size: 85dvh;
  border-radius: var(--fertig-rw) var(--fertig-rw) 0 0; border-bottom: 0;
}
@media (prefers-reduced-motion: no-preference) and (update: fast) {
  dialog[data-side=right] { translate: 100% 0 }
  dialog[data-side=left]  { translate: -100% 0 }
  dialog[data-side=bottom]{ translate: 0 100% }
  dialog[data-side][open] { translate: 0 0 }
  @starting-style {
    dialog[data-side=right][open] { translate: 100% 0 }
    dialog[data-side=left][open]  { translate: -100% 0 }
    dialog[data-side=bottom][open]{ translate: 0 100% }
  }
}

/* toast: a non-modal popover parked in a corner */
[popover][data-toast] {
  position: fixed; inset: auto 1rem 1rem auto; margin: 0;
  min-width: 16rem; max-width: min(24rem, 92dvw); padding: .8rem 1rem;
}
@supports (position-area: block-end span-inline-end) {
  /* base selector, for the same reason as the menu above: a toast that only
     un-anchored while open would jump to the anchor as it faded out */
  [popover][data-toast] {
    position-area: none; inset: auto 1rem 1rem auto; min-width: 16rem;
  }
}
/* Two open toasts land on the same corner coordinates and sit on top of each
   other, and an open popover is in the top layer so no ordinary wrapper can
   stack them. Make the region the popover instead and the toasts plain
   children of it: <div popover=manual data-toast-region> with a
   <div data-toast> per message. */
[popover][data-toast-region] {
  position: fixed; inset: auto 1rem 1rem auto; margin: 0;
  display: flex; flex-direction: column; align-items: flex-end; gap: .6rem;
  background: none; border: 0; box-shadow: none; padding: 0;
  max-block-size: none; overflow: visible; pointer-events: none;
}
[popover][data-toast-region] > [data-toast] {
  pointer-events: auto; position: static; inset: auto;
  min-width: 16rem; max-width: min(24rem, 92dvw); padding: .8rem 1rem;
  background: var(--fertig-el); color: var(--fertig-fg); border: 1px solid var(--fertig-bd);
  border-radius: var(--fertig-rw); font-size: .95em;
  box-shadow: var(--fertig-up2), 0 24px 44px -18px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
}
@supports (position-area: block-end span-inline-end) {
  [popover][data-toast-region] { position-area: none; inset: auto 1rem 1rem auto }
}

/* list group: a plain <menu> outside a popover is a bordered list */
menu {
  list-style: none; padding: 0; margin: 1.1rem 0;
  border: 1px solid transparent; border-radius: var(--fertig-rw);
  background: var(--fertig-el); box-shadow: var(--fertig-up);
}
menu > li { padding: .55rem .85rem }
menu > li + li { border-top: 1px solid var(--fertig-bd) }
[popover] menu { border: 0; padding: 0; margin: 0; background: none; box-shadow: none }
[popover] menu > li { padding: 0 }
[popover] menu > li + li { border-top: 0 }

/* navigation lists: a <menu> inside a <nav> is navigation, not a list group.
   No rules between items, no bullets, and the link fills the row so the whole
   band is the hit target. This is what [data-layout=sidebar] below is for —
   the layout shipped without it, which left the obvious markup looking boxed. */
nav menu {
  border: 0; background: none; padding: 0; margin-block: 0 1.2rem;
}
nav menu > li { padding: 0 }
nav menu > li + li { border-top: 0 }
nav menu :is(a, button) {
  display: block; inline-size: 100%; text-align: start;
  padding: .38rem .6rem; border: 0; border-radius: var(--fertig-r);
  background: none; box-shadow: none; font: inherit;
  color: var(--fertig-fg); text-decoration: none; opacity: .72;
}
nav menu :is(a, button):hover { background: var(--fertig-face); opacity: 1 }
nav menu :is(a, button)[aria-current] {
  background: var(--fertig-face); color: var(--fertig-ac); font-weight: 600; opacity: 1;
}

/* app layout: a sticky sidebar beside the content */
[data-layout=sidebar] { display: grid; gap: 1.5rem; align-items: start }
@media (width >= 56rem) {
  [data-layout=sidebar] { grid-template-columns: 15rem minmax(0, 1fr) }
  [data-layout=sidebar] > :first-child { position: sticky; top: 4.5rem }
}
[data-layout=sidebar] > * { min-width: 0 }

/* carousel: scroll snapping, with real scroll buttons where they exist */
[data-carousel] {
  display: flex; gap: 1rem; overflow-x: auto; padding-bottom: .5rem;
  scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
}
[data-carousel] > * { scroll-snap-align: start; flex: 0 0 min(20rem, 82%) }

/* ---- the only classes (all optional) ------------------------ */
.muted  { color: var(--fertig-mut) }
.center { text-align: center }
.wide   { max-width: 80rem }
/* on the toolbar .wide cannot mean "be 80rem wide" — the bar stays full-bleed
   and its contents move out to the wide column, so an app screen's wordmark
   lines up with the app's own content rather than floating mid-screen. */
:is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav.wide {
  max-width: none; --fertig-nw: 80rem;
}
.row { display: flex; flex-wrap: wrap; gap: .7rem; align-items: center }
/* Form controls are full-width blocks by default, which is right in a form and
   wrong in a toolbar. Inside a .row they size to the row instead. */
.row :is(input:not([type=checkbox],[type=radio]), textarea) {
  width: auto; flex: 1 1 10rem; min-width: 0;
}
.row select { width: auto; flex: 0 1 auto; min-width: 0 }
.row :is(progress, meter) { width: auto; flex: 1 1 6rem }
.row > label { flex: none; margin-top: 0 }
.grid   { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)) }
/* a .grid is a block-level container that nothing else gives vertical rhythm
   to, so whatever follows one collides with it. Spacing the next sibling
   rather than the grid itself: a margin on .grid would collapse out through
   the top of a padding-less container and open a seam above it. */
.grid + * { margin-top: 1.1rem }
.card {
  container-type: inline-size;                 /* query it: @container (min-width: …) */
  border: 1px solid transparent; border-radius: var(--fertig-rw); padding: 1.1rem;
  background: var(--fertig-el); box-shadow: var(--fertig-up2);
  display: flex; flex-direction: column;      /* so footers line up across a row */
  gap: .85rem;                                /* spacing is the container's job */
}
.card > * { margin-block: 0 }

/* ---- layout utilities --------------------------------------
   A compact, predictable set for layout and width only — there is no colour
   or type scale, and no responsive variants, because those want a build step
   and this file does not have one. The margins are flow-relative (ms/me, not
   ml/mr), so a toolbar still works in Persian or Arabic. */
.flex { display: flex }
/* .grid is already above: auto-fit columns. Adding grid-cols-N pins the count. */
.flex-col { flex-direction: column }
.flex-wrap { flex-wrap: wrap }
.flex-1 { flex: 1; min-width: 0 }      /* min-width, or a <pre> inside blows the row open */
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)) }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)) }
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)) }
.items-start { align-items: start }
.items-center { align-items: center }
.items-end { align-items: end }
.justify-start { justify-content: start }
.justify-center { justify-content: center }
.justify-end { justify-content: end }
.justify-between { justify-content: space-between }
.gap-0 { gap: 0 }
.gap-1 { gap: .25rem }
.gap-2 { gap: .5rem }
.gap-3 { gap: .75rem }
.gap-4 { gap: 1rem }
.gap-6 { gap: 1.5rem }
.mx-auto { margin-inline: auto }
.ms-auto { margin-inline-start: auto }
.me-auto { margin-inline-end: auto }
.w-full { width: 100% }
/* max-inline-size, not max-width, so the scale still means "along the text" in
   a vertical writing mode. The full scale is present because a half-scale
   where .max-w-2xl works and .max-w-3xl silently does not is worse than none.
   They can constrain a page container or anything inside one. */
.max-w-xs   { max-inline-size: 20rem }
.max-w-sm   { max-inline-size: 24rem }
.max-w-md   { max-inline-size: 28rem }
.max-w-lg   { max-inline-size: 32rem }
.max-w-xl   { max-inline-size: 36rem }
.max-w-2xl  { max-inline-size: 42rem }
.max-w-3xl  { max-inline-size: 48rem }
.max-w-4xl  { max-inline-size: 56rem }
.max-w-5xl  { max-inline-size: 64rem }
.max-w-6xl  { max-inline-size: 72rem }
.max-w-7xl  { max-inline-size: 80rem }
.max-w-full { max-inline-size: 100% }
.max-w-none { max-inline-size: none }
/* A deliberate reading measure for long-form content. */
.max-w-prose { max-inline-size: 38rem }
.hidden { display: none }

.badge {
  border: 0; border-radius: var(--fertig-rp); background: var(--fertig-face);
  padding: .18em .65em; font: 650 .78em/1.45 var(--fertig-f); color: var(--fertig-mut);
}

/* card anatomy: an optional header, footer, and full-bleed media */
/* Header and footer are spacing hooks, not banded sub-surfaces. */
.card > :is(header, footer) { padding-inline: 0 }
.card > header {
  margin-top: -.1rem; font-weight: 650;
}
.card > footer {
  margin-top: auto; margin-bottom: -.1rem; color: var(--fertig-mut); font-size: .85em;
}
.card > :is(img, video):first-child {
  align-self: stretch; width: auto; max-width: none; margin: -1.1rem -1.1rem 0;
  aspect-ratio: 16 / 9; object-fit: cover;
  border: 0; border-radius: var(--fertig-rw) var(--fertig-rw) 0 0; box-shadow: none;
}
/* A background layer rather than a border or an inset shadow: a border would
   shift the card's geometry, and an inset shadow does not follow the corner
   radius cleanly at this size — it reads as a detached bar with gaps at the
   top corners. A gradient is clipped by the radius, so the tone hugs the curve. */
.card[data-tone] {
  background: linear-gradient(var(--fertig-ac), var(--fertig-ac)) top / 100% 3px no-repeat,
              var(--fertig-el);
}

/* a card that is itself a link or button reacts like one */
:is(a, button).card {
  display: flex; width: 100%;                 /* keep the column from .card */
  text-align: start; color: inherit;
  text-decoration: none; font: inherit;
}
:is(a, button).card:hover:not(:disabled) {
  border-color: color-mix(in srgb, var(--fertig-ac) 22%, var(--fertig-bd));
  box-shadow: var(--fertig-up2), 0 16px 30px -16px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
  translate: 0 -1px;
}
:is(a, button).card:active:not(:disabled) { translate: 0 0 }

/* ---- details ------------------------------------------------ */
/* beats .card/.row's display without !important, which a layer would let
   win over your own sheet; hidden=until-found must stay findable */
[hidden]:not([hidden=until-found]) { display: none }
@media (prefers-reduced-motion: no-preference) and (update: fast) { html { scroll-behavior: smooth } }
:target { scroll-margin-block-start: 4rem }     /* clears the toolbar */
:is(h1,h2,h3,h4):target { background: color-mix(in srgb, var(--fertig-ac) 12%, transparent) }
a[target=_blank]:not(:has(> img, > svg))::after { content: " ↗"; font-size: .85em; opacity: .6 }
a:has(> img), a:has(> code) { text-decoration: none }
sup, sub { line-height: 0; font-size: .75em }       /* stop them stretching lines */
q { quotes: "“" "”" "‘" "’" }
dfn { font-style: normal; font-weight: 700 }
ins { text-decoration: underline; text-decoration-style: wavy; text-decoration-color: var(--fertig-ac) }
del { color: var(--fertig-mut) }
address { font-style: normal; color: var(--fertig-mut) }
hgroup > * { margin: 0 }
hgroup > p { color: var(--fertig-mut) }
output { font-weight: 700 }
meter { width: 100%; height: .5rem }
optgroup { font-weight: 700; color: var(--fertig-mut) }
::file-selector-button { margin-inline-end: .8em }
::file-selector-button:hover { background: var(--fertig-face) }
:is(input,select,textarea)[aria-invalid=true],
:is(input,textarea):user-invalid { border-color: light-dark(#c0392b, #ff6b6b) }
:is(input,select,textarea)[aria-invalid=true] + small,
:is(input,textarea):user-invalid + small { color: light-dark(#c0392b, #ff6b6b) }
summary:hover { color: var(--fertig-ac) }
/* thin, unobtrusive scrollbars */
/* scrollbar-width is Safari 18.2, scrollbar-color Safari 26.2 — both younger
   than the floor, and both left ungated on purpose: there is no fallback to
   write. An engine without them paints its own scrollbar, which is exactly
   what a scrollbar should look like there anyway. Same reasoning for
   accent-color at the top of the sheet. */
/* Not `*`: the page's own scrollbar keeps the width the OS gave it. Thinning
   every scroller on the page narrows hit targets the sheet never made, which
   is a target-size problem for anyone using a pointer imprecisely. */
:is(pre, table, textarea, dialog, [popover], menu, fieldset,
    [role=group], [data-carousel], [data-layout=sidebar] > *) {
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--fertig-fg) 25%, transparent) transparent;
}

@page { margin: 2cm }
@media print {
  :root { --fertig-bg: #fff; --fertig-el: #fff; --fertig-fg: #000; --fertig-bd: #999 }
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav { display: none }
  /* the footer stays: the legal line, the contact and the citations live there,
     and a printout without them is missing the part people keep */
  body > *, body > :where(#root, #app, #__next, [data-fertig]) > * { border: 0 }
  p, blockquote, li { orphans: 3; widows: 3 }   /* no stranded single lines */
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .8em; color: #555 }
  pre, table, figure, details, tr, img { break-inside: avoid }
  h1, h2, h3, h4 { break-after: avoid }
}

/* WCAG 2.5.8: give touch pointers a real target */
@media (pointer: coarse) {
  button, [type=submit], [type=button], [type=reset],
  :where(a.primary, a[role=button]), select, summary,
  input:not([type=checkbox],[type=radio],[type=range],[type=color],[type=file]) {
    min-height: 44px;
  }
  input[type=checkbox], input[type=radio] { width: 1.4em; height: 1.4em }
  /* 44px is the AAA target (2.5.5). The small variant opts down to the 24px
     AA floor (2.5.8) rather than stopping being small on a phone. */
  :is(button, a, input, select, label)[data-size=sm] { min-height: 32px }
}

/* the OS is already inverting; stop images being inverted twice */
@media (inverted-colors: inverted) {
  img, video, iframe { filter: invert(1) hue-rotate(180deg) }
}

/* a wider gamut can hold a cleaner accent than sRGB can */
@media (color-gamut: p3) {
  @supports (color: oklch(60% .2 250)) {
    :root { --fertig-ac: light-dark(oklch(52% .21 258), oklch(76% .15 258)) }
  }
}

/* CJK text wants punctuation and Latin spacing handled */
:root { text-spacing-trim: trim-start; text-autospace: normal }

@media (prefers-contrast: more) {
  :root {
    --fertig-bd:  light-dark(#00000059, #ffffff6b);
    --fertig-mut: light-dark(#45454a,   #c6c6ce);
  }
}

@media (prefers-reduced-motion: no-preference) and (update: fast) {
  a, button, input, select, textarea, summary, tr,
  [type=submit], [type=button], [type=reset], .card, .badge,
  [role=tab], [role=option], [role=group] > *, [data-item], [popover] :is(a, button) {
    transition: box-shadow var(--fertig-dur-1) var(--fertig-ease),
                background-color var(--fertig-dur-1) var(--fertig-ease),
                border-color var(--fertig-dur-1) var(--fertig-ease),
                color var(--fertig-dur-1) var(--fertig-ease),
                translate .08s var(--fertig-ease-out);
  }
  /* the ring must never be animated — it has to be there the instant focus
     lands, not fade in after it */
  :focus-visible { transition: none }
}


/* ---- below the floor ---------------------------------------
   Everything above assumes light-dark(). An engine without it does not
   degrade to "unstyled but readable" — every colour token becomes invalid at
   computed-value time, so backgrounds go transparent and colour is inherited,
   and the page can land unreadable rather than plain. These are the same
   colours flattened to sRGB. Last in the layer, so it wins on order over the
   token block at the top. Nothing else needs a twin: an invalid --fertig-ac on a
   [data-tone] or [data-accent] element just inherits the one from :root. */
@supports not (color: light-dark(#000, #fff)) {
  :root {
    --fertig-bg: #f4f6f9; --fertig-el: #fff; --fertig-face: #eceff4; --fertig-fg: #151b24;
    --fertig-mut: #525962; --fertig-bd: #151b241a; --fertig-tb: #fbfcfe;
    --fertig-ac: #2850de; --fertig-on-ac: #f7f8fb;
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme=light]) {
      --fertig-bg: #0b0e14; --fertig-el: #151a22; --fertig-face: #202630; --fertig-fg: #eaeff7;
      --fertig-mut: #9da5b1; --fertig-bd: #eaeff71f; --fertig-tb: #131820;
      --fertig-ac: #90b5ff; --fertig-on-ac: #07071c;
    }
  }
  [data-theme=dark] {
    --fertig-bg: #0b0e14; --fertig-el: #151a22; --fertig-face: #202630; --fertig-fg: #eaeff7;
    --fertig-mut: #9da5b1; --fertig-bd: #eaeff71f; --fertig-tb: #131820;
    --fertig-ac: #90b5ff; --fertig-on-ac: #07071c;
  }
}

}

/* Windows High Contrast: the OS supplies the palette, so get out of the way.
   A later layer, so `*` beats every shadow in the sheet on layer order alone. */
@layer fertig-a11y {
  @media (forced-colors: active) {
    * { box-shadow: none }
    button, [type=submit], [type=button], [type=reset], input, select, textarea,
    .card, .badge, details, dialog, [popover], fieldset, table,
    [data-surface], [data-input-group], [data-empty], [data-item], [role=listbox] {
      border: 1px solid CanvasText;
    }
    /* Selected pills rely on fill and shadow in ordinary colours. Give them a
       real system-colour edge when both disappear in High Contrast. */
    [role=tab][aria-selected=true], [role=option][aria-selected=true] {
      border: 2px solid Highlight;
    }
    button[aria-pressed=true] { border-color: Highlight }
  }
}
