fertig 1.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/fertig.css CHANGED
@@ -1,4 +1,4 @@
1
- /*! fertig v1.1.0 — a classless CSS stylesheet. MIT */
1
+ /*! fertig v3.0.0 — a classless CSS stylesheet. MIT */
2
2
 
3
3
  /* Opinions, so you don't have to have them:
4
4
  system type · one accent · one measure · a window on a desktop.
@@ -9,8 +9,8 @@
9
9
  /* typed so they interpolate; untyped custom properties cannot animate */
10
10
  /* Registrations are global: they cannot be layered, scoped or overridden.
11
11
  Hence the prefix — a bare --a1 would silently retype a property of yours. */
12
- @property --fertig-a1 { syntax: "<number>"; inherits: true; initial-value: .08 }
13
- @property --fertig-a2 { syntax: "<number>"; inherits: true; initial-value: .06 }
12
+ @property --fertig-a1 { syntax: "<number>"; inherits: true; initial-value: .06 }
13
+ @property --fertig-a2 { syntax: "<number>"; inherits: true; initial-value: .05 }
14
14
 
15
15
  /* Two layers, in this order. Your own unlayered CSS wins over both regardless
16
16
  of specificity, so overriding never needs !important — and neither does this
@@ -24,107 +24,116 @@
24
24
  /* ---- tokens ------------------------------------------------- */
25
25
  :root {
26
26
  color-scheme: light dark; /* powers light-dark() below */
27
- accent-color: var(--ac);
28
- caret-color: var(--ac);
29
-
30
- --f: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
27
+ /* These two are also re-declared on the elements that consume them, further
28
+ down. They are inherited properties: declared only here, they resolve once
29
+ against the root's accent and every descendant inherits that *computed
30
+ colour* — so a [data-accent] or [data-tone] subtree, or any element that
31
+ sets --fertig-ac, got the root's tint on its checkboxes and its caret
32
+ while everything else in it followed the override. */
33
+ accent-color: var(--fertig-ac);
34
+ caret-color: var(--fertig-ac);
35
+
36
+ --fertig-f: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
31
37
  Roboto, "Helvetica Neue", Arial, sans-serif; /* yours */
32
- --fm: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
38
+ --fertig-fm: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
33
39
  "Liberation Mono", monospace; /* code */
34
- --w: 38rem; /* ~70 characters — yours */
35
- --r: 9px; /* control radius */
36
- --rs: 5px; /* small radius: inline chips and code */
37
- --rw: 14px; /* window radius */
40
+ --fertig-w: 38rem; /* ~70 characters — yours */
41
+ /* One radius, and the rest derived from it: set --fertig-r and the whole
42
+ sheet rescales together. Four independent numbers meant --fertig-r: 4px
43
+ left tight fields sitting inside cards that were still round. */
44
+ --fertig-r: 12px; /* control radius: fields, menu rows */
45
+ --fertig-rs: calc(var(--fertig-r) * .5); /* small: inline chips and code */
46
+ --fertig-rw: calc(var(--fertig-r) * 1.33); /* window: cards, dialogs, menus */
47
+ --fertig-rp: calc(infinity * 1px); /* pill: buttons, badges, switches, meters */
38
48
 
39
49
  /* ---- palette ------------------------------------------------
40
50
  Low chroma on purpose. Vivid palettes shout; these are meant to sit
41
- under text all day. OKLCH, so a step means the same thing in every hue. */
42
- --stone-100: oklch(97.5% .004 75); --stone-300: oklch(91% .006 75);
43
- --stone-500: oklch(72% .008 75); --stone-700: oklch(47% .009 75);
44
- --stone-900: oklch(26% .008 75);
51
+ under text all day. OKLCH, so a step means the same thing in every hue.
52
+ The neutrals are a cool graphite (hue ≈ 258) with a faint violet cast —
53
+ crisper and less dated than a warm stone, and better under the accent. */
54
+ --fertig-stone-100: oklch(97.5% .006 258); --fertig-stone-300: oklch(91% .009 258);
55
+ --fertig-stone-500: oklch(72% .012 258); --fertig-stone-700: oklch(47% .014 258);
56
+ --fertig-stone-900: oklch(26% .016 258);
45
57
 
46
- --sage-100: oklch(96% .022 155); --sage-300: oklch(87% .042 155);
47
- --sage-500: oklch(66% .058 155); --sage-700: oklch(47% .055 155);
48
- --sage-900: oklch(29% .038 155);
58
+ --fertig-sage-100: oklch(96% .024 160); --fertig-sage-300: oklch(87% .046 160);
59
+ --fertig-sage-500: oklch(66% .064 160); --fertig-sage-700: oklch(47% .06 160);
60
+ --fertig-sage-900: oklch(29% .042 160);
49
61
 
50
- --sky-100: oklch(96% .022 240); --sky-300: oklch(87% .045 240);
51
- --sky-500: oklch(64% .085 245); --sky-700: oklch(48% .095 250);
52
- --sky-900: oklch(30% .06 250);
62
+ --fertig-sky-100: oklch(96% .024 242); --fertig-sky-300: oklch(87% .05 244);
63
+ --fertig-sky-500: oklch(64% .095 246); --fertig-sky-700: oklch(48% .1 250);
64
+ --fertig-sky-900: oklch(30% .064 250);
53
65
 
54
- --clay-100: oklch(96% .022 45); --clay-300: oklch(88% .045 45);
55
- --clay-500: oklch(68% .085 45); --clay-700: oklch(50% .09 40);
56
- --clay-900: oklch(31% .055 40);
66
+ --fertig-clay-100: oklch(96% .024 45); --fertig-clay-300: oklch(88% .05 45);
67
+ --fertig-clay-500: oklch(68% .095 45); --fertig-clay-700: oklch(50% .098 40);
68
+ --fertig-clay-900: oklch(31% .06 40);
57
69
 
58
- --plum-100: oklch(96% .02 320); --plum-300: oklch(88% .038 320);
59
- --plum-500: oklch(66% .06 320); --plum-700: oklch(47% .065 320);
60
- --plum-900: oklch(29% .042 320);
70
+ --fertig-plum-100: oklch(96% .022 318); --fertig-plum-300: oklch(88% .042 318);
71
+ --fertig-plum-500: oklch(66% .066 318); --fertig-plum-700: oklch(47% .07 318);
72
+ --fertig-plum-900: oklch(29% .046 318);
61
73
 
62
- --gold-100: oklch(96.5% .028 90); --gold-300: oklch(89% .055 88);
63
- --gold-500: oklch(74% .085 85); --gold-700: oklch(55% .085 80);
64
- --gold-900: oklch(33% .05 80);
74
+ --fertig-gold-100: oklch(96.5% .03 92); --fertig-gold-300: oklch(89% .06 88);
75
+ --fertig-gold-500: oklch(74% .092 85); --fertig-gold-700: oklch(55% .092 80);
76
+ --fertig-gold-900: oklch(33% .056 80);
65
77
 
66
78
  /* ---- semantic ----------------------------------------------- */
67
- --bg: light-dark(oklch(93.5% .006 75), oklch(16% .006 75)); /* desktop */
68
- --el: light-dark(oklch(99.4% .002 75), oklch(21% .006 75)); /* paper */
69
- --face:light-dark(oklch(97.2% .005 75), oklch(25% .007 75)); /* chrome */
70
- --fg: light-dark(oklch(22% .008 75), oklch(95% .004 75));
79
+ --fertig-bg: light-dark(oklch(93.5% .008 258), oklch(15.5% .014 258)); /* desktop */
80
+ --fertig-el: light-dark(oklch(99% .004 258), oklch(21% .016 258)); /* paper */
81
+ --fertig-face:light-dark(oklch(97.2% .006 258), oklch(25% .018 258)); /* chrome */
82
+ --fertig-fg: light-dark(oklch(22% .02 258), oklch(95% .012 258));
71
83
  /* lifted from 50%/70%: the weakest pairing (muted on the desktop ground in
72
84
  light, muted on chrome in dark) was 4.96:1 and 5.99:1, a hair over AA.
73
- 47%/73% takes them to 5.65:1 and 6.70:1 while keeping a clear step down
74
- from --fg — AAA would need 41.5%, which is close enough to --fg that
75
- "muted" stops reading as secondary at all. */
76
- --mut: light-dark(oklch(47% .009 75), oklch(73% .008 75));
77
- --bd: light-dark(oklch(22% .008 75 / .13), oklch(95% .004 75 / .13));
78
- --tb: light-dark(oklch(97.2% .005 75 / .85), oklch(25% .007 75 / .85));
79
- /* indigo, not the azure every other classless sheet lands on. Measured:
80
- 6.28:1 as link text on paper and 6.39:1 for a white label on the fill in
81
- light; 8.72:1 and 10.26:1 in dark. Both sit inside sRGB. */
82
- --ac: light-dark(oklch(50% .19 275), oklch(78% .11 275)); /* yours */
83
- --on-ac: light-dark(#fff, #06121f); /* text on the accent, both themes */
85
+ 46%/72% takes them to 5.7:1 and 6.8:1 while keeping a clear step down
86
+ from --fertig-fg — AAA would need ~41%, which is close enough to
87
+ --fertig-fg that "muted" stops reading as secondary at all. */
88
+ --fertig-mut: light-dark(oklch(46% .018 258), oklch(72% .02 258));
89
+ --fertig-bd: light-dark(oklch(22% .02 258 / .13), oklch(95% .012 258 / .13));
90
+ --fertig-tb: light-dark(oklch(97.2% .006 258 / .85), oklch(25% .018 258 / .85));
91
+ /* a violet-indigo, not the azure every other classless sheet lands on.
92
+ Higher chroma than the old indigo so it reads electric without shouting.
93
+ Measured: 6.21:1 as link text on paper and 6.39:1 for a white label on
94
+ the fill in light; 8.62:1 in dark. Both sit inside sRGB. */
95
+ --fertig-ac: light-dark(oklch(50% .22 266), oklch(78% .13 266)); /* yours */
96
+ --fertig-on-ac: light-dark(#fff, #07071c); /* text on the accent, both themes */
84
97
 
85
98
  /* depth: hairline + soft shadow, macOS weight.
86
- --sh is an ordinary colour — override it with any notation you like, and
87
- every shadow in the sheet retints. The alphas are taken with color-mix
88
- against transparent rather than relative colour: same result, and it is
89
- three years older, which keeps Safari 17.5 inside the floor. */
90
- --sh: oklch(23.1% .007 286);
91
- /* The prefixed names are the API; the bare ones are read as a fallback so a
92
- sheet that set --a1 before 1.1.0 keeps working. --a1 itself stays
93
- unregistered, which is the point of the rename — an @property
94
- registration is global and cannot be scoped, so a two-character name was
95
- silently retyping a consumer's own property. */
96
- --fertig-a1: var(--a1, .08); --fertig-a2: var(--a2, .06);
97
- --sh1: color-mix(in srgb, var(--sh) calc(var(--fertig-a1) * 100%), transparent);
98
- --sh2: color-mix(in srgb, var(--sh) calc(var(--fertig-a2) * 100%), transparent);
99
- --up: 0 1px 1px var(--sh1);
100
- --up2: 0 1px 2px var(--sh1), 0 6px 16px -6px var(--sh2);
101
- --dn: inset 0 1px 2px color-mix(in srgb, var(--sh) 8%, transparent);
102
- --caps: .74em/1.4 var(--f);
99
+ --fertig-sh is an ordinary colour — override it with any notation you like, and
100
+ every shadow in the sheet retints. Lighter than a pure black so the shadows
101
+ read as diffuse atmosphere rather than dark bars. The alphas are taken with
102
+ color-mix against transparent rather than relative colour: same result, and
103
+ it is three years older, which keeps Safari 17.5 inside the floor. */
104
+ --fertig-sh: oklch(30% .012 270);
105
+ --fertig-a1: .06; --fertig-a2: .05;
106
+ --fertig-sh1: color-mix(in srgb, var(--fertig-sh) calc(var(--fertig-a1) * 100%), transparent);
107
+ --fertig-sh2: color-mix(in srgb, var(--fertig-sh) calc(var(--fertig-a2) * 100%), transparent);
108
+ --fertig-up: 0 1px 1px var(--fertig-sh1);
109
+ --fertig-up2: 0 1px 1px var(--fertig-sh1), 0 8px 22px -8px var(--fertig-sh2);
110
+ --fertig-dn: inset 0 1px 2px color-mix(in srgb, var(--fertig-sh) 7%, transparent);
111
+ --fertig-caps: .74em/1.4 var(--fertig-f);
103
112
 
104
113
  /* motion. One decelerating curve for things arriving, one accelerating for
105
114
  things leaving, and a symmetric one for state that toggles in place. */
106
- --ease-out: cubic-bezier(.22, 1, .36, 1);
107
- --ease-in: cubic-bezier(.55, 0, 1, .45);
108
- --ease: cubic-bezier(.4, 0, .2, 1);
109
- --dur-1: .12s; /* a control reacting under the pointer */
110
- --dur-2: .2s; /* something appearing or leaving */
115
+ --fertig-ease-out: cubic-bezier(.22, 1, .36, 1);
116
+ --fertig-ease-in: cubic-bezier(.55, 0, 1, .45);
117
+ --fertig-ease: cubic-bezier(.4, 0, .2, 1);
118
+ --fertig-dur-1: .12s; /* a control reacting under the pointer */
119
+ --fertig-dur-2: .2s; /* something appearing or leaving */
111
120
  }
112
121
  @media (prefers-color-scheme: dark) {
113
- :root:not([data-theme=light]) { --fertig-a1: var(--a1, .5); --fertig-a2: var(--a2, .45) }
122
+ :root:not([data-theme=light]) { --fertig-a1: .42; --fertig-a2: .38 }
114
123
  }
115
- /* --on-ac has to survive any --ac override, or a custom accent ships an
124
+ /* --fertig-on-ac has to survive any --fertig-ac override, or a custom accent ships an
116
125
  unreadable label. contrast-color() does that properly but has almost no
117
126
  support; relative colour reaches the same black-or-white decision from the
118
127
  accent's own lightness, and has been cross-engine since 2024. Both are
119
128
  additive — the measured light-dark() pair above stands if neither exists. */
120
129
  @supports (color: oklch(from red l c h)) {
121
- :root { --on-ac: oklch(from var(--ac) clamp(0, (l - .62) * -100, 1) 0 h) }
130
+ :root { --fertig-on-ac: oklch(from var(--fertig-ac) clamp(0, (l - .62) * -100, 1) 0 h) }
122
131
  }
123
132
  @supports (color: contrast-color(red)) {
124
- :root { --on-ac: contrast-color(var(--ac)) }
133
+ :root { --fertig-on-ac: contrast-color(var(--fertig-ac)) }
125
134
  }
126
- [data-theme=light] { color-scheme: light; --fertig-a1: var(--a1, .08); --fertig-a2: var(--a2, .06) }
127
- [data-theme=dark] { color-scheme: dark; --fertig-a1: var(--a1, .5); --fertig-a2: var(--a2, .45) }
135
+ [data-theme=light] { color-scheme: light; --fertig-a1: .06; --fertig-a2: .05 }
136
+ [data-theme=dark] { color-scheme: dark; --fertig-a1: .42; --fertig-a2: .38 }
128
137
 
129
138
  /* Named accents. The ramps already exist, so this is a mapping table: put
130
139
  data-accent on any element and everything inside it — links, focus rings,
@@ -132,13 +141,13 @@
132
141
  global, so one section of a page can differ from the rest.
133
142
  Each pair is measured: the light tone clears AA as link text on paper, the
134
143
  dark tone clears it on the dark ground. */
135
- [data-accent=indigo] { --ac: light-dark(oklch(50% .19 275), oklch(78% .11 275)) }
136
- [data-accent=sky] { --ac: light-dark(var(--sky-700), oklch(78% .085 245)) }
137
- [data-accent=sage] { --ac: light-dark(var(--sage-700), oklch(80% .075 155)) }
138
- [data-accent=clay] { --ac: light-dark(var(--clay-700), oklch(80% .085 45)) }
139
- [data-accent=plum] { --ac: light-dark(var(--plum-700), oklch(80% .065 320)) }
140
- [data-accent=gold] { --ac: light-dark(oklch(55% .085 80), oklch(84% .09 85)) }
141
- [data-accent=slate] { --ac: light-dark(oklch(35% .01 250), oklch(86% .006 250)) }
144
+ [data-accent=indigo] { --fertig-ac: light-dark(oklch(50% .19 275), oklch(78% .11 275)) }
145
+ [data-accent=sky] { --fertig-ac: light-dark(var(--fertig-sky-700), oklch(78% .085 245)) }
146
+ [data-accent=sage] { --fertig-ac: light-dark(var(--fertig-sage-700), oklch(80% .075 155)) }
147
+ [data-accent=clay] { --fertig-ac: light-dark(var(--fertig-clay-700), oklch(80% .085 45)) }
148
+ [data-accent=plum] { --fertig-ac: light-dark(var(--fertig-plum-700), oklch(80% .065 320)) }
149
+ [data-accent=gold] { --fertig-ac: light-dark(oklch(55% .085 80), oklch(84% .09 85)) }
150
+ [data-accent=slate] { --fertig-ac: light-dark(oklch(35% .01 250), oklch(86% .006 250)) }
142
151
 
143
152
  /* ---- base --------------------------------------------------- */
144
153
  *, ::before, ::after { box-sizing: border-box }
@@ -146,25 +155,25 @@
146
155
  because no engine ships an alternative today: this one (Safari has no
147
156
  unprefixed text-size-adjust), the autofill repaint below, and the progress
148
157
  pseudo-elements. Everything else here is unprefixed modern CSS. */
149
- html { font: 16px/1.65 var(--f); -webkit-text-size-adjust: 100%; tab-size: 2 }
158
+ html { font: 16px/1.65 var(--fertig-f); -webkit-text-size-adjust: 100%; tab-size: 2 }
150
159
  body {
151
- margin: 0; background: var(--bg); color: var(--fg);
160
+ margin: 0; background: var(--fertig-bg); color: var(--fertig-fg);
152
161
  overflow-wrap: break-word; /* a pasted URL cannot widen the page */
153
162
  font-variant-numeric: slashed-zero tabular-nums;
154
163
  }
155
- ::selection { background: color-mix(in srgb, var(--ac) 28%, transparent) }
164
+ ::selection { background: color-mix(in srgb, var(--fertig-ac) 28%, transparent) }
156
165
  /* The macOS ring. No border-radius here: an outline already follows the
157
166
  element's own corners, and setting one would reshape whatever takes focus —
158
- a focused dialog would snap from --rw to --r for as long as it is focused. */
167
+ a focused dialog would snap from --fertig-rw to --fertig-r for as long as it is focused. */
159
168
  :focus-visible {
160
- outline: 3px solid color-mix(in srgb, var(--ac) 45%, transparent);
169
+ outline: 3px solid color-mix(in srgb, var(--fertig-ac) 45%, transparent);
161
170
  outline-offset: 1px;
162
171
  }
163
172
  img, video, iframe { display: block; max-width: 100%; height: auto }
164
173
  /* svg is left inline: it is as often an icon inside a sentence or a button as
165
174
  it is a standalone figure, and display:block drops the icon onto its own row */
166
175
  svg { max-width: 100%; vertical-align: middle }
167
- img, video { border-radius: var(--r); box-shadow: var(--up2) }
176
+ img, video { border-radius: var(--fertig-r); box-shadow: var(--fertig-up2) }
168
177
 
169
178
  /* ---- layout: a window on a desktop --------------------------- */
170
179
  /* The shell is body's own children — or the children of the single wrapper a
@@ -174,37 +183,37 @@ img, video { border-radius: var(--r); box-shadow: var(--up2) }
174
183
  :where() so every selector keeps the specificity it had as `body > *`. */
175
184
  body > *,
176
185
  body > :where(#root, #app, #__next, [data-fertig]) > * {
177
- max-width: var(--w); margin-inline: auto; padding-inline: 1.9rem;
186
+ max-width: var(--fertig-w); margin-inline: auto; padding-inline: 1.9rem;
178
187
  }
179
188
  /* the wrapper is not itself a shell element; it just gets out of the way */
180
189
  body > :where(#root, #app, #__next, [data-fertig]) { max-width: none; padding-inline: 0 }
181
190
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > :is(header, main, footer) {
182
- background: var(--el); border-inline: 1px solid var(--bd);
191
+ background: var(--fertig-el); border-inline: 1px solid var(--fertig-bd);
183
192
  }
184
193
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > header { padding-block: 2.4rem .8rem }
185
194
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > main { padding-bottom: 2.8rem }
186
195
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > footer {
187
- padding-block: 1.2rem 2rem; color: var(--mut); font-size: .85em;
188
- border-top: 1px solid var(--bd); border-bottom: 1px solid var(--bd);
189
- border-radius: 0 0 var(--rw) var(--rw); margin-bottom: 2.5rem;
190
- box-shadow: 0 18px 30px -22px color-mix(in srgb, var(--sh) 50%, transparent);
196
+ padding-block: 1.2rem 2rem; color: var(--fertig-mut); font-size: .85em;
197
+ border-top: 1px solid var(--fertig-bd); border-bottom: 1px solid var(--fertig-bd);
198
+ border-radius: 0 0 var(--fertig-rw) var(--fertig-rw); margin-bottom: 2.5rem;
199
+ box-shadow: 0 20px 32px -24px color-mix(in srgb, var(--fertig-sh) 38%, transparent);
191
200
  }
192
201
  /* nav = unified toolbar: full-bleed, vibrant, contents on the measure */
193
202
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav {
194
203
  position: sticky; top: 0; z-index: 9; max-width: none;
195
204
  /* the toolbar is full-bleed but its contents line up with the page column.
196
- --nw is that column: it follows --w, so overriding the measure moves the
205
+ --fertig-nw is that column: it follows --fertig-w, so overriding the measure moves the
197
206
  toolbar with it, and .wide re-points it at the wide column instead. */
198
- --nw: var(--w);
199
- padding-inline: max(1.9rem, calc(50% - var(--nw) / 2 + 1.9rem),
207
+ --fertig-nw: var(--fertig-w);
208
+ padding-inline: max(1.9rem, calc(50% - var(--fertig-nw) / 2 + 1.9rem),
200
209
  env(safe-area-inset-left), env(safe-area-inset-right));
201
210
  display: flex; flex-wrap: wrap; gap: 1.1rem; align-items: center;
202
211
  padding-block: calc(.6rem + env(safe-area-inset-top, 0px)) .6rem;
203
- background: var(--tb); color: var(--fg);
212
+ background: var(--fertig-tb); color: var(--fertig-fg);
204
213
  backdrop-filter: saturate(180%) blur(20px);
205
- border-bottom: 1px solid var(--bd); font-size: .92em;
214
+ border-bottom: 1px solid var(--fertig-bd); font-size: .92em;
206
215
  }
207
- :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav a { color: var(--fg); text-decoration: none; opacity: .75 }
216
+ :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav a { color: var(--fertig-fg); text-decoration: none; opacity: .75 }
208
217
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav a:hover { opacity: 1 }
209
218
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav :is(strong, b) a { opacity: 1 } /* the wordmark is not a link among links */
210
219
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav ul {
@@ -218,7 +227,7 @@ h1 { font-size: 1.55rem; letter-spacing: -.015em }
218
227
  h2 { font-size: 1.25rem; letter-spacing: -.01em; margin-top: 2.4rem }
219
228
  h3 { font-size: 1.08rem; margin-top: 1.9rem }
220
229
  h4 { font-size: 1rem }
221
- h5, h6 { font: 700 var(--caps); text-transform: uppercase; letter-spacing: .1em; color: var(--mut) }
230
+ h5, h6 { font: 700 var(--fertig-caps); text-transform: uppercase; letter-spacing: .1em; color: var(--fertig-mut) }
222
231
  :is(h1,h2,h3,h4,h5,h6):first-child { margin-top: 0 }
223
232
  @supports (text-box: trim-both cap alphabetic) {
224
233
  h1, h2, h3, h4 { text-box: trim-both cap alphabetic } /* optical, not metric */
@@ -228,37 +237,37 @@ h5, h6 { font: 700 var(--caps); text-transform: uppercase; letter-spacing: .1em;
228
237
 
229
238
  p, ul, ol, dl, pre, blockquote, table, figure, details, fieldset, hr { margin: 1.1rem 0 }
230
239
  p { text-wrap: pretty }
231
- a { color: var(--ac); text-underline-offset: 3px; text-decoration-thickness: 1px }
240
+ a { color: var(--fertig-ac); text-underline-offset: 3px; text-decoration-thickness: 1px }
232
241
  a:hover { text-decoration-thickness: 2px } /* quiet — no invert mid-paragraph */
233
- small { font-size: .85em; color: var(--mut) }
234
- hr { border: 0; border-top: 1px solid var(--bd); margin: 2.2rem 0 }
242
+ small { font-size: .85em; color: var(--fertig-mut) }
243
+ hr { border: 0; border-top: 1px solid var(--fertig-bd); margin: 2.2rem 0 }
235
244
  mark { background: light-dark(#ffe58f, #5c4a15); color: inherit; border-radius: 3px; padding: 0 .15em }
236
245
  abbr[title] { text-decoration: underline dotted; cursor: help }
237
246
  blockquote {
238
- border-inline-start: 2px solid var(--bd);
239
- padding-block: .15rem; padding-inline: 1.2rem 0; color: var(--mut);
247
+ border-inline-start: 2px solid var(--fertig-bd);
248
+ padding-block: .15rem; padding-inline: 1.2rem 0; color: var(--fertig-mut);
240
249
  }
241
250
  ul, ol { padding-inline-start: 1.5rem }
242
- li::marker { color: var(--mut) }
251
+ li::marker { color: var(--fertig-mut) }
243
252
  /* minmax(0, 1fr), not 1fr: a bare 1fr track has min-width:auto, so a long
244
253
  value cannot shrink and pushes the page sideways on a narrow screen. */
245
254
  dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .35rem 1rem }
246
- dt { color: var(--mut) }
255
+ dt { color: var(--fertig-mut) }
247
256
  dd { margin: 0 }
248
- figcaption { font-size: .85em; color: var(--mut); text-align: center }
257
+ figcaption { font-size: .85em; color: var(--fertig-mut); text-align: center }
249
258
 
250
259
  /* ---- code --------------------------------------------------- */
251
- code, kbd, samp, pre { font-family: var(--fm); font-size: .9em }
260
+ code, kbd, samp, pre { font-family: var(--fertig-fm); font-size: .9em }
252
261
  :not(pre) > code, samp {
253
- background: var(--face); border: 1px solid var(--bd);
254
- border-radius: var(--rs); padding: .08em .35em;
262
+ background: var(--fertig-face); border: 1px solid var(--fertig-bd);
263
+ border-radius: var(--fertig-rs); padding: .08em .35em;
255
264
  }
256
265
  kbd {
257
- background: var(--face); border: 1px solid var(--bd); border-bottom-width: 2px;
258
- border-radius: var(--rs); padding: .08em .4em; box-shadow: var(--up);
266
+ background: var(--fertig-face); border: 1px solid var(--fertig-bd); border-bottom-width: 2px;
267
+ border-radius: var(--fertig-rs); padding: .08em .4em; box-shadow: var(--fertig-up);
259
268
  }
260
269
  pre {
261
- background: var(--face); border: 1px solid var(--bd); border-radius: var(--r);
270
+ background: var(--fertig-face); border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw);
262
271
  padding: 1rem; overflow-x: auto;
263
272
  }
264
273
  pre code { background: none; border: 0; padding: 0; font-size: 1em }
@@ -266,37 +275,37 @@ pre code { background: none; border: 0; padding: 0; font-size: 1em }
266
275
  /* Syntax theme. The sheet ships no highlighter — bring highlight.js or Prism
267
276
  and the tokens land in this palette, in both themes. */
268
277
  :root {
269
- --syn-comment: light-dark(#6b7280, #8b949e);
270
- --syn-key: light-dark(#7c3aed, #c4a7fb);
271
- --syn-str: light-dark(#15803d, #7ee787);
272
- --syn-num: light-dark(#b45309, #f0b866);
273
- --syn-fn: light-dark(#1d4ed8, #93c5fd);
274
- --syn-attr: light-dark(#b91c1c, #ff9492);
278
+ --fertig-syn-comment: light-dark(#6b7280, #8b949e);
279
+ --fertig-syn-key: light-dark(#7c3aed, #c4a7fb);
280
+ --fertig-syn-str: light-dark(#15803d, #7ee787);
281
+ --fertig-syn-num: light-dark(#b45309, #f0b866);
282
+ --fertig-syn-fn: light-dark(#1d4ed8, #93c5fd);
283
+ --fertig-syn-attr: light-dark(#b91c1c, #ff9492);
275
284
  }
276
285
  :is(.hljs-comment, .hljs-quote, .token.comment, .token.prolog, .token.doctype) {
277
- color: var(--syn-comment); font-style: italic;
286
+ color: var(--fertig-syn-comment); font-style: italic;
278
287
  }
279
288
  :is(.hljs-keyword, .hljs-selector-tag, .hljs-literal, .hljs-type, .hljs-built_in,
280
289
  .token.keyword, .token.atrule, .token.rule, .token.important, .token.selector) {
281
- color: var(--syn-key);
290
+ color: var(--fertig-syn-key);
282
291
  }
283
292
  :is(.hljs-string, .hljs-meta-string, .token.string, .token.attr-value, .token.char) {
284
- color: var(--syn-str);
293
+ color: var(--fertig-syn-str);
285
294
  }
286
295
  :is(.hljs-number, .hljs-symbol, .hljs-bullet, .token.number, .token.boolean,
287
296
  .token.constant, .token.unit) {
288
- color: var(--syn-num);
297
+ color: var(--fertig-syn-num);
289
298
  }
290
299
  :is(.hljs-title, .hljs-name, .hljs-section, .hljs-function,
291
300
  .token.tag, .token.function, .token.class-name) {
292
- color: var(--syn-fn);
301
+ color: var(--fertig-syn-fn);
293
302
  }
294
303
  :is(.hljs-attr, .hljs-attribute, .hljs-variable, .hljs-template-variable,
295
304
  .token.attr-name, .token.property, .token.variable) {
296
- color: var(--syn-attr);
305
+ color: var(--fertig-syn-attr);
297
306
  }
298
307
  :is(.hljs-punctuation, .hljs-operator, .token.punctuation, .token.operator) {
299
- color: var(--mut);
308
+ color: var(--fertig-mut);
300
309
  }
301
310
  :is(.hljs-emphasis, .token.italic) { font-style: italic }
302
311
  :is(.hljs-strong, .token.bold) { font-weight: 700 }
@@ -306,31 +315,31 @@ pre code { background: none; border: 0; padding: 0; font-size: 1em }
306
315
  /* ---- table -------------------------------------------------- */
307
316
  table { width: 100%; border-collapse: collapse; font-size: .92em }
308
317
  th, td {
309
- border-bottom: 1px solid var(--bd); padding-block: .55rem;
318
+ border-bottom: 1px solid var(--fertig-bd); padding-block: .55rem;
310
319
  padding-inline: 0 .7rem; text-align: start;
311
320
  }
312
321
  th:last-child, td:last-child { padding-inline-end: 0 }
313
322
  thead th {
314
- border-bottom: 1px solid color-mix(in srgb, var(--fg) 45%, transparent);
315
- font: 700 var(--caps); text-transform: uppercase; letter-spacing: .09em; color: var(--mut);
323
+ border-bottom: 1px solid color-mix(in srgb, var(--fertig-fg) 45%, transparent);
324
+ font: 700 var(--fertig-caps); text-transform: uppercase; letter-spacing: .09em; color: var(--fertig-mut);
316
325
  padding-bottom: .4rem;
317
326
  }
318
327
  tbody tr:last-child td { border-bottom: 0 }
319
328
  /* a totals row is not just another row */
320
- tfoot td { font-weight: 700; border-top: 1px solid var(--bd); border-bottom: 0 }
329
+ tfoot td { font-weight: 700; border-top: 1px solid var(--fertig-bd); border-bottom: 0 }
321
330
  /* our text-align was overriding HTML own alignment; honour it for numbers */
322
331
  :is(th, td)[align=right] { text-align: right }
323
332
  :is(th, td)[align=center] { text-align: center }
324
- tbody tr:hover { background: var(--face) }
333
+ tbody tr:hover { background: var(--fertig-face) }
325
334
  @media (width <= 40rem) {
326
335
  table { display: block; overflow-x: auto; overscroll-behavior-x: contain }
327
336
  }
328
- caption { text-align: start; color: var(--mut); font-size: .85em; padding-bottom: .35rem }
337
+ caption { text-align: start; color: var(--fertig-mut); font-size: .85em; padding-bottom: .35rem }
329
338
 
330
339
  /* ---- forms: fields sink, buttons rise ------------------------ */
331
340
  button, input, select, textarea { font: inherit; color: inherit }
332
- label { display: block; font-size: .85em; color: var(--mut) }
333
- label:has(input) { font-size: 1rem; color: var(--fg) }
341
+ label { display: block; font-size: .85em; color: var(--fertig-mut) }
342
+ label:has(input) { font-size: 1rem; color: var(--fertig-fg) }
334
343
  /* a label following a field belongs to the next one: give it air, except in a
335
344
  .row, where the toolbar is horizontal and the margin would knock it askew */
336
345
  :is(input, select, textarea) + label { margin-top: .9rem }
@@ -340,17 +349,22 @@ textarea { width: 100%; min-height: 5rem; resize: vertical }
340
349
  }
341
350
  input:not([type=checkbox],[type=radio],[type=range],[type=color],[type=file],[type=image],[type=submit],[type=button],[type=reset]),
342
351
  select, textarea {
343
- display: block; width: 100%; padding: .52em .7em; background: var(--el);
344
- border: 1px solid var(--bd); border-radius: var(--r); box-shadow: var(--dn);
352
+ display: block; width: 100%; padding: .52em .7em; background: var(--fertig-el);
353
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-r); box-shadow: var(--fertig-dn);
345
354
  }
346
355
  select {
347
356
  appearance: none; padding-inline-end: 2.2rem;
348
- background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
349
- linear-gradient(135deg, currentColor 50%, transparent 50%);
350
- background-position: calc(100% - 17px) 55%, calc(100% - 12.5px) 55%;
351
- background-size: 5px 5px; background-repeat: no-repeat;
352
- }
353
- select:dir(rtl) { background-position: 12.5px 55%, 17px 55% }
357
+ /* a stroked chevron, drawn as two 1.5px bars, so the engines without
358
+ appearance: base-select show the same mark as the ones with it */
359
+ background-image:
360
+ linear-gradient(45deg, transparent calc(50% - 1px), currentColor calc(50% - 1px),
361
+ currentColor calc(50% + .5px), transparent calc(50% + .5px)),
362
+ linear-gradient(-45deg, transparent calc(50% - 1px), currentColor calc(50% - 1px),
363
+ currentColor calc(50% + .5px), transparent calc(50% + .5px));
364
+ background-position: calc(100% - 17px) 54%, calc(100% - 12px) 54%;
365
+ background-size: 6px 6px; background-repeat: no-repeat;
366
+ }
367
+ select:dir(rtl) { background-position: 12px 54%, 17px 54% }
354
368
  select[multiple] { background-image: none; padding-inline-end: .6em }
355
369
 
356
370
  /* a <select> whose dropdown can finally be styled like the rest of the sheet */
@@ -364,83 +378,101 @@ select[multiple] { background-image: none; padding-inline-end: .6em }
364
378
  content: ""; inline-size: .45em; block-size: .45em; flex: none;
365
379
  border: solid currentColor; border-width: 0 1px 1px 0;
366
380
  rotate: 45deg; translate: 0 -.15em; opacity: .7;
367
- transition: rotate var(--dur-1) var(--ease), translate var(--dur-1) var(--ease);
381
+ transition: rotate var(--fertig-dur-1) var(--fertig-ease), translate var(--fertig-dur-1) var(--fertig-ease);
368
382
  }
369
383
  select:open::picker-icon { rotate: 225deg; translate: 0 .1em }
370
384
  ::picker(select) {
371
385
  min-width: anchor-size(width); /* never narrower than the field */
372
- border: 1px solid var(--bd); border-radius: var(--r); padding: .3rem;
373
- background: var(--el); margin-block-start: .35rem;
374
- box-shadow: var(--up2), 0 20px 40px -14px color-mix(in srgb, var(--sh) 40%, transparent);
386
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw); padding: .35rem;
387
+ background: var(--fertig-el); margin-block-start: .35rem;
388
+ box-shadow: var(--fertig-up2), 0 24px 44px -18px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
375
389
  }
376
390
  option {
377
391
  display: flex; align-items: center; gap: .5em; padding: .4em .6em;
378
- border-radius: calc(var(--r) - 2px);
392
+ border-radius: calc(var(--fertig-r) - 2px);
379
393
  }
380
- option:hover { background: var(--face) }
381
- option:checked { background: var(--ac); color: var(--on-ac) }
394
+ option:hover { background: var(--fertig-face) }
395
+ option:checked { background: var(--fertig-ac); color: var(--fertig-on-ac) }
382
396
  option::checkmark { order: 1; margin-inline-start: auto; content: "✓" }
397
+ /* The picker drops out of the field the way the menu popover does. It lives
398
+ in the top layer, so display and overlay have to transition discretely or
399
+ it would pop out on close with the fade never running. */
400
+ @media (prefers-reduced-motion: no-preference) and (update: fast) {
401
+ ::picker(select) {
402
+ opacity: 0; translate: 0 -4px; transform-origin: top center;
403
+ transition: opacity .13s var(--fertig-ease-out), translate .13s var(--fertig-ease-out),
404
+ display .13s allow-discrete, overlay .13s allow-discrete;
405
+ }
406
+ select:open::picker(select) { opacity: 1; translate: 0 }
407
+ @starting-style { select:open::picker(select) { opacity: 0; translate: 0 -4px } }
408
+ }
383
409
  }
384
- :is(input,select,textarea):hover:not(:disabled) { border-color: color-mix(in srgb, var(--fg) 25%, transparent) }
385
- :is(input,select,textarea):focus-visible { border-color: var(--ac) }
410
+ :is(input,select,textarea):hover:not(:disabled) { border-color: color-mix(in srgb, var(--fertig-fg) 25%, transparent) }
411
+ :is(input,select,textarea):focus-visible { border-color: var(--fertig-ac) }
386
412
  input[type=checkbox], input[type=radio] {
387
- width: 1.15em; height: 1.15em; margin: 0 .45em 0 0; vertical-align: -.18em;
413
+ width: 1.15em; height: 1.15em; margin: 0; margin-inline-end: .45em; vertical-align: -.18em;
388
414
  }
415
+ /* re-resolved here rather than inherited from :root, so a native control inside
416
+ a [data-accent] or [data-tone] block is tinted with that block's accent */
417
+ :is(input[type=checkbox], input[type=radio], input[type=range], progress, select[multiple]) {
418
+ accent-color: var(--fertig-ac);
419
+ }
420
+ :is(input, textarea, [contenteditable]) { caret-color: var(--fertig-ac) }
389
421
  label:has(input[type=checkbox]), label:has(input[type=radio]) { line-height: 1.9 }
390
422
  input[type=range] { width: 100%; height: 1.15em }
391
- ::placeholder { color: var(--mut) }
423
+ ::placeholder { color: var(--fertig-mut) }
392
424
  @supports selector(::spelling-error) {
393
425
  ::spelling-error { text-decoration: wavy underline light-dark(#c0392b, #ff6b6b) }
394
426
  ::grammar-error { text-decoration: wavy underline light-dark(#9a6700, #d29922) }
395
427
  }
396
- :is(input, textarea):read-only:not(:disabled) { background: var(--face); cursor: default }
428
+ :is(input, textarea):read-only:not(:disabled) { background: var(--fertig-face); cursor: default }
397
429
  label:has(+ :required)::after, label:has(:required)::after {
398
- content: " *"; color: var(--ac); /* mark what is actually required */
430
+ content: " *"; color: var(--fertig-ac); /* mark what is actually required */
399
431
  }
400
432
  /* the browser paints autofilled fields yellow; repaint them ourselves */
401
433
  input:autofill, input:autofill:hover, input:autofill:focus {
402
- -webkit-text-fill-color: var(--fg);
403
- box-shadow: var(--dn), inset 0 0 0 100px var(--el);
404
- caret-color: var(--ac);
434
+ -webkit-text-fill-color: var(--fertig-fg);
435
+ box-shadow: var(--fertig-dn), inset 0 0 0 100px var(--fertig-el);
436
+ caret-color: var(--fertig-ac);
405
437
  }
406
- :disabled { color: var(--mut); cursor: not-allowed; box-shadow: none }
438
+ :disabled { color: var(--fertig-mut); cursor: not-allowed; box-shadow: none }
407
439
  /* No notched border: the legend becomes the panel title. The float is not
408
440
  nostalgia — a legend keeps its magic placement under block, grid and flex
409
441
  fieldsets alike, and floating it is still the only way out. */
410
442
  fieldset {
411
- border: 1px solid var(--bd); border-radius: var(--r); padding: .9rem 1rem 1rem;
412
- background: var(--face);
443
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw); padding: .9rem 1rem 1rem;
444
+ background: var(--fertig-face);
413
445
  }
414
- fieldset:focus-within { border-color: color-mix(in srgb, var(--ac) 45%, var(--bd)) }
446
+ fieldset:focus-within { border-color: color-mix(in srgb, var(--fertig-ac) 45%, var(--fertig-bd)) }
415
447
  legend {
416
448
  float: left; width: 100%; margin-bottom: .8rem; padding: 0 0 .6rem;
417
- border-bottom: 1px solid var(--bd);
418
- font-size: .95em; font-weight: 700; color: var(--fg);
449
+ border-bottom: 1px solid var(--fertig-bd);
450
+ font-size: .95em; font-weight: 700; color: var(--fertig-fg);
419
451
  }
420
452
  legend + * { clear: left }
421
453
 
422
454
  button, [type=submit], [type=button], [type=reset],
423
455
  :where(a.primary, a[role=button]), ::file-selector-button {
424
456
  display: inline-block; text-decoration: none; text-align: center;
425
- padding: .48em 1.1em; background: var(--el); color: var(--fg); cursor: pointer;
426
- border: 1px solid var(--bd); border-radius: var(--r); box-shadow: var(--up);
457
+ padding: .48em 1.1em; background: var(--fertig-el); color: var(--fertig-fg); cursor: pointer;
458
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rp); box-shadow: var(--fertig-up);
427
459
  font: inherit; min-width: 5rem;
428
460
  }
429
- :is(button,[type=submit],[type=button],[type=reset],a.primary,a[role=button]):hover:not(:disabled) { background: var(--face) }
461
+ :is(button,[type=submit],[type=button],[type=reset],a.primary,a[role=button]):hover:not(:disabled) { background: var(--fertig-face) }
430
462
  :is(button,[type=submit],[type=button],[type=reset],a.primary,a[role=button]):active:not(:disabled) {
431
- box-shadow: var(--dn); translate: 0 .5px;
463
+ box-shadow: var(--fertig-dn); translate: 0 .5px;
432
464
  }
433
- [type=submit], .primary { background: var(--ac); border-color: transparent; color: var(--on-ac) }
465
+ [type=submit], .primary { background: var(--fertig-ac); border-color: transparent; color: var(--fertig-on-ac) }
434
466
  /* The filled button lightens on hover. color-mix does that in oklab, which is
435
467
  close enough and is Baseline; relative colour does it by adding to L
436
- directly, which holds the hue exactly when --ac is a wide-gamut override.
437
- Both derive from --ac, so a custom accent never needs a second declaration. */
468
+ directly, which holds the hue exactly when --fertig-ac is a wide-gamut override.
469
+ Both derive from --fertig-ac, so a custom accent never needs a second declaration. */
438
470
  :is([type=submit], .primary, a.primary):hover:not(:disabled) {
439
- background: color-mix(in oklab, var(--ac), #fff 9%);
471
+ background: color-mix(in oklab, var(--fertig-ac), #fff 9%);
440
472
  }
441
473
  @supports (color: oklch(from red l c h)) {
442
474
  :is([type=submit], .primary, a.primary):hover:not(:disabled) {
443
- background: oklch(from var(--ac) calc(l + .05) c h);
475
+ background: oklch(from var(--fertig-ac) calc(l + .05) c h);
444
476
  }
445
477
  }
446
478
 
@@ -453,18 +485,18 @@ button, [type=submit], [type=button], [type=reset],
453
485
  button[data-icon] { min-width: 0; padding: .38em .62em; line-height: 1 }
454
486
  :is(button, a, input, select, label)[data-block] { display: block; width: 100%; min-width: 0 }
455
487
  :is(button, a, input)[data-variant=ghost] { background: none; border-color: transparent; box-shadow: none }
456
- :is(button, a, input)[data-variant=ghost]:hover:not(:disabled) { background: var(--face); border-color: var(--bd) }
488
+ :is(button, a, input)[data-variant=ghost]:hover:not(:disabled) { background: var(--fertig-face); border-color: var(--fertig-bd) }
457
489
  :is(button, a, input)[data-variant=link] {
458
490
  background: none; border: 0; box-shadow: none; min-width: 0; padding-inline: .2em;
459
- color: var(--ac); text-decoration: underline; text-underline-offset: 3px;
491
+ color: var(--fertig-ac); text-decoration: underline; text-underline-offset: 3px;
460
492
  }
461
493
  :is(button, a, input)[data-variant=link]:hover:not(:disabled) { background: none; text-decoration-thickness: 2px }
462
- button[aria-pressed=true] { background: var(--face); color: var(--ac); font-weight: 700 }
494
+ button[aria-pressed=true] { background: var(--fertig-face); color: var(--fertig-ac); font-weight: 700 }
463
495
  button[aria-pressed=true]:hover:not(:disabled) {
464
- background: color-mix(in srgb, var(--fg) 7%, var(--face));
496
+ background: color-mix(in srgb, var(--fertig-fg) 7%, var(--fertig-face));
465
497
  }
466
498
  /* filled buttons get a lit top edge, the way a real control does */
467
- :is([type=submit], .primary) { box-shadow: var(--up), inset 0 1px 0 light-dark(#ffffff33, #ffffff26) }
499
+ :is([type=submit], .primary) { box-shadow: var(--fertig-up), inset 0 1px 0 light-dark(#ffffff33, #ffffff26) }
468
500
  :is([type=submit], .primary):active:not(:disabled) { box-shadow: inset 0 1px 3px #0006 }
469
501
 
470
502
  @supports (corner-shape: squircle) {
@@ -479,51 +511,79 @@ button[aria-pressed=true]:hover:not(:disabled) {
479
511
 
480
512
  /* ---- disclosure / misc -------------------------------------- */
481
513
  details {
482
- border: 1px solid var(--bd); border-radius: var(--r);
483
- padding: .7rem 1.1rem; background: var(--el);
514
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw);
515
+ padding: .7rem 1.1rem; background: var(--fertig-el);
484
516
  }
485
517
  summary { cursor: pointer; font-weight: 700; list-style: none }
486
- summary::before { content: "▸ "; color: var(--mut) }
518
+ summary::before { content: "▸ "; color: var(--fertig-mut) }
487
519
  details[open] > summary::before { content: "▾ " }
488
520
  details[open] > summary { margin-bottom: .6rem }
489
521
  dialog {
490
- background: var(--el); color: var(--fg); border: 1px solid var(--bd);
491
- border-radius: var(--rw); padding: 1.4rem; max-width: min(30rem, 92dvw);
492
- box-shadow: 0 32px 64px -16px color-mix(in srgb, var(--sh) 45%, transparent);
522
+ background: var(--fertig-el); color: var(--fertig-fg); border: 1px solid var(--fertig-bd);
523
+ border-radius: var(--fertig-rw); padding: 1.6rem; max-width: min(30rem, 92dvw);
524
+ box-shadow: 0 1px 1px color-mix(in srgb, var(--fertig-sh) 8%, transparent),
525
+ 0 28px 60px -22px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
493
526
  }
494
527
  dialog > :last-child:not(footer) { margin-bottom: 0 }
495
528
  dialog, [popover] { overscroll-behavior: contain }
496
- dialog::backdrop { background: color-mix(in srgb, var(--sh) 35%, transparent); backdrop-filter: blur(3px) }
529
+ dialog::backdrop { background: color-mix(in srgb, var(--fertig-sh) 32%, transparent); backdrop-filter: blur(4px) }
497
530
 
498
531
  /* dialog anatomy: an optional header and footer, pinned while the body scrolls.
499
532
  Sticky rather than a grid, because a classless sheet has no wrapper around
500
- the loose middle children to make a grid row out of. */
533
+ the loose middle children to make a grid row out of.
534
+ No dividing rules: a short dialog cut into three banded strips is the look
535
+ of a decade-old modal. The separation is spacing, and the hairline only
536
+ arrives when there is actually something scrolling under it — see below. */
501
537
  dialog:has(> :is(header, footer)) { max-block-size: min(44rem, 85dvh); overflow: auto }
502
538
  dialog > :is(header, footer) {
503
539
  position: sticky; z-index: 1;
504
- margin-inline: -1.4rem; padding: .7rem 1.4rem;
505
- background: var(--el); /* the dialog's own surface, so it reads as one sheet */
540
+ margin-inline: -1.6rem;
541
+ background: var(--fertig-el); /* the dialog's own surface, so it reads as one sheet */
506
542
  }
507
543
  dialog > header {
508
- inset-block-start: -1.4rem; margin-block: -1.4rem 1rem;
509
- font-weight: 700; border-bottom: 1px solid var(--bd);
510
- border-radius: var(--rw) var(--rw) 0 0;
544
+ inset-block-start: -1.6rem; margin-block: -1.6rem .35rem;
545
+ padding: 1.6rem 1.6rem .5rem; font-weight: 700;
546
+ border-radius: var(--fertig-rw) var(--fertig-rw) 0 0;
547
+ }
548
+ /* The title carries the header's weight, so it needs no margins of its own,
549
+ and it is UI text: it takes --fertig-f, the interface face, not whatever
550
+ display font the page gave its headings — a monospaced heading face reads
551
+ as a terminal once it sits on a dialog. :is([open], :modal) is there for
552
+ weight — a page's `.prose h3` outranks a bare element chain, and the title
553
+ would come out sized and lettered for the article column it is nowhere near. */
554
+ dialog:is([open], :modal) > header > :is(h1, h2, h3, h4, p) {
555
+ margin: 0; font-family: var(--fertig-f); font-size: 1.1rem; font-weight: 600;
556
+ line-height: 1.3; letter-spacing: -.01em;
511
557
  }
512
- /* the title carries the header's weight; it does not need its own margins */
513
- dialog > header > :is(h1, h2, h3, h4, p) { margin: 0; font-size: 1.05rem }
514
558
  dialog > footer {
515
- inset-block-end: -1.4rem; margin-block: 1rem -1.4rem;
516
- border-top: 1px solid var(--bd); border-radius: 0 0 var(--rw) var(--rw);
559
+ inset-block-end: -1.6rem; margin-block: 1.35rem -1.6rem;
560
+ padding: .5rem 1.6rem 1.6rem; border-radius: 0 0 var(--fertig-rw) var(--fertig-rw);
517
561
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
518
562
  justify-content: flex-end; /* actions sit at the end edge */
519
563
  }
564
+ /* The hairline is scroll state, not decoration: it appears under the header
565
+ once the body has scrolled, and leaves the footer once you reach the end.
566
+ A scroll timeline on a container with nothing to scroll is inactive, so a
567
+ dialog that fits shows no rules at all and no script decides that. */
568
+ @supports (animation-timeline: scroll()) {
569
+ dialog > header {
570
+ animation: fertig-rule-under linear both;
571
+ animation-timeline: scroll(nearest); animation-range: 0 1.2rem;
572
+ }
573
+ dialog > footer {
574
+ animation: fertig-rule-over linear both;
575
+ animation-timeline: scroll(nearest); animation-range: calc(100% - 1.2rem) 100%;
576
+ }
577
+ @keyframes fertig-rule-under { from { box-shadow: none } to { box-shadow: 0 1px 0 var(--fertig-bd) } }
578
+ @keyframes fertig-rule-over { from { box-shadow: 0 -1px 0 var(--fertig-bd) } to { box-shadow: none } }
579
+ }
520
580
  /* a note beside the buttons pushes them over, rather than sitting under them */
521
- dialog > footer > :is(p, span):first-child { margin: 0; margin-inline-end: auto; color: var(--mut) }
581
+ dialog > footer > :is(p, span):first-child { margin: 0; margin-inline-end: auto; color: var(--fertig-mut) }
522
582
  /* sheets come up, they do not appear */
523
583
  @media (prefers-reduced-motion: no-preference) and (update: fast) {
524
584
  dialog, dialog::backdrop {
525
- transition: opacity var(--dur-2) var(--ease-out), translate var(--dur-2) var(--ease-out),
526
- display var(--dur-2) allow-discrete, overlay var(--dur-2) allow-discrete;
585
+ transition: opacity var(--fertig-dur-2) var(--fertig-ease-out), translate var(--fertig-dur-2) var(--fertig-ease-out),
586
+ display var(--fertig-dur-2) allow-discrete, overlay var(--fertig-dur-2) allow-discrete;
527
587
  }
528
588
  dialog { opacity: 0; translate: 0 10px }
529
589
  dialog[open] { opacity: 1; translate: 0 }
@@ -533,24 +593,24 @@ dialog > footer > :is(p, span):first-child { margin: 0; margin-inline-end: auto;
533
593
  @starting-style { dialog[open]::backdrop { opacity: 0 } }
534
594
  [popover] {
535
595
  opacity: 0; translate: 0 -4px;
536
- transition: opacity .13s var(--ease-out), translate .13s var(--ease-out),
596
+ transition: opacity .13s var(--fertig-ease-out), translate .13s var(--fertig-ease-out),
537
597
  display .13s allow-discrete, overlay .13s allow-discrete;
538
598
  }
539
599
  [popover]:popover-open { opacity: 1; translate: 0 }
540
600
  @starting-style { [popover]:popover-open { opacity: 0; translate: 0 -4px } }
541
601
  }
542
602
  search { display: block; margin-block: 1.1rem } /* a landmark needs its own rhythm */
543
- ::target-text { background: color-mix(in srgb, var(--ac) 28%, transparent) }
603
+ ::target-text { background: color-mix(in srgb, var(--fertig-ac) 28%, transparent) }
544
604
  aside {
545
- border: 1px solid var(--bd); border-inline-start: 3px solid var(--ac);
546
- border-radius: var(--r); border-start-start-radius: 0; border-end-start-radius: 0;
547
- background: var(--el); padding: .7rem 1.1rem;
605
+ border: 1px solid var(--fertig-bd); border-inline-start: 3px solid var(--fertig-ac);
606
+ border-radius: var(--fertig-rw); border-start-start-radius: 0; border-end-start-radius: 0;
607
+ background: var(--fertig-el); padding: .7rem 1.1rem;
548
608
  margin-block: 1.1rem; /* or the next block sits flush against it */
549
609
  }
550
- progress { width: 100%; height: .5rem; border: 0; border-radius: calc(infinity * 1px); background: var(--face) }
551
- progress::-webkit-progress-bar { background: var(--face); border-radius: calc(infinity * 1px) }
552
- progress::-webkit-progress-value { background: var(--ac); border-radius: calc(infinity * 1px) }
553
- progress::-moz-progress-bar { background: var(--ac); border-radius: calc(infinity * 1px) }
610
+ progress { width: 100%; height: .5rem; border: 0; border-radius: var(--fertig-rp); background: var(--fertig-face) }
611
+ progress::-webkit-progress-bar { background: var(--fertig-face); border-radius: var(--fertig-rp) }
612
+ progress::-webkit-progress-value { background: var(--fertig-ac); border-radius: var(--fertig-rp) }
613
+ progress::-moz-progress-bar { background: var(--fertig-ac); border-radius: var(--fertig-rp) }
554
614
 
555
615
  /* details can only animate to auto height once keywords interpolate */
556
616
  @supports selector(::details-content) {
@@ -561,63 +621,71 @@ progress::-moz-progress-bar { background: var(--ac); border-radius: calc(infinit
561
621
  details { interpolate-size: allow-keywords }
562
622
  details::details-content {
563
623
  block-size: 0; overflow: clip;
564
- transition: block-size .28s var(--ease), content-visibility .28s allow-discrete;
624
+ transition: block-size .28s var(--fertig-ease), content-visibility .28s allow-discrete;
565
625
  }
566
626
  details[open]::details-content { block-size: auto }
567
627
  }
568
628
  }
569
629
 
570
- /* #core-omit-start */
571
630
  /* ---- components, addressed by ARIA rather than by class ----- */
572
631
 
573
632
  /* segmented control: <div role=group> of buttons */
574
633
  /* max-inline-size + scroll, or a group of four or more buttons pushes the
575
634
  whole page sideways on a narrow screen. It scrolls rather than wraps:
576
- wrapping would break the joined-pill run of radii into separate rows. */
577
- [role=group] { display: inline-flex; vertical-align: middle;
578
- max-inline-size: 100%; overflow-x: auto; overscroll-behavior-x: contain }
635
+ wrapping would break the joined-pill run of radii into separate rows.
636
+ The block axis is clipped rather than left to compute to auto alongside it:
637
+ Safari paints a vertical scrollbar for a sub-pixel of overflow, and the row
638
+ has no vertical overflow worth scrolling to in the first place. */
639
+ [role=group] { display: inline-flex; vertical-align: middle; max-inline-size: 100%;
640
+ overflow: auto clip; overscroll-behavior-x: contain }
579
641
  [role=group] > * { border-radius: 0; margin-inline-start: -1px; box-shadow: none; min-width: 0 }
642
+ /* The buttons fill the group exactly and the group clips on the block axis,
643
+ so an outset ring would be sliced off. Inside the edge, it is all there. */
644
+ [role=group] > :focus-visible { outline-offset: -3px }
580
645
  [role=group] > :first-child {
581
646
  margin-inline-start: 0;
582
- border-start-start-radius: var(--r); border-end-start-radius: var(--r);
647
+ border-start-start-radius: var(--fertig-rp); border-end-start-radius: var(--fertig-rp);
583
648
  }
584
649
  [role=group] > :last-child {
585
- border-start-end-radius: var(--r); border-end-end-radius: var(--r);
650
+ border-start-end-radius: var(--fertig-rp); border-end-end-radius: var(--fertig-rp);
586
651
  }
587
- [role=group] > [aria-pressed=true] { background: var(--face); color: var(--ac); font-weight: 700 }
652
+ [role=group] > [aria-pressed=true] { background: var(--fertig-face); color: var(--fertig-ac); font-weight: 700 }
653
+ /* The press nudge would push a button half a pixel past the scroll port and
654
+ flash a scrollbar, so joined buttons stay put and read the press by shadow. */
655
+ [role=group] > :active:not(:disabled) { translate: none }
588
656
 
589
657
  /* tabs: <div role=tablist> of <button role=tab aria-selected> */
590
- [role=tablist] { display: flex; gap: .1rem; border-bottom: 1px solid var(--bd); margin: 1.1rem 0 }
658
+ [role=tablist] { display: flex; gap: .1rem; border-bottom: 1px solid var(--fertig-bd); margin: 1.1rem 0 }
591
659
  [role=tab] {
592
- background: none; border: 0; box-shadow: none; min-width: 0; color: var(--mut);
593
- padding: .45em .9em; border-radius: var(--r) var(--r) 0 0; margin-bottom: -1px;
660
+ background: none; border: 0; box-shadow: none; min-width: 0; color: var(--fertig-mut);
661
+ padding: .45em .9em; border-radius: var(--fertig-r) var(--fertig-r) 0 0; margin-bottom: -1px;
594
662
  }
595
- [role=tab]:hover:not(:disabled) { background: var(--face); color: var(--fg) }
596
- [role=tab][aria-selected=true] { color: var(--fg); box-shadow: inset 0 -2px 0 var(--ac) }
663
+ [role=tab]:hover:not(:disabled) { background: var(--fertig-face); color: var(--fertig-fg) }
664
+ [role=tab][aria-selected=true] { color: var(--fertig-fg); box-shadow: inset 0 -2px 0 var(--fertig-ac) }
597
665
 
598
666
  /* switch: <input type=checkbox role=switch> */
599
667
  input[type=checkbox][switch],
600
668
  input[type=checkbox][role=switch] {
601
- appearance: none; width: 2.2em; height: 1.3em; border-radius: calc(infinity * 1px); border: 0;
602
- background: color-mix(in srgb, var(--fg) 20%, transparent);
669
+ appearance: none; width: 2.2em; height: 1.3em; border-radius: var(--fertig-rp); border: 0;
670
+ background: color-mix(in srgb, var(--fertig-fg) 20%, transparent);
603
671
  position: relative; cursor: pointer; vertical-align: -.3em; margin-inline-end: .5em;
604
672
  }
605
673
  input[switch]::after, input[role=switch]::after {
606
674
  content: ""; position: absolute; inset-block-start: .14em; inset-inline-start: .14em;
607
- width: 1.02em; height: 1.02em; border-radius: 50%; background: #fff; box-shadow: var(--up);
608
- transition: translate .16s var(--ease-out);
675
+ width: 1.02em; height: 1.02em; border-radius: 50%; background: #fff; box-shadow: var(--fertig-up);
676
+ transition: translate .16s var(--fertig-ease-out);
609
677
  }
610
- input[switch]:checked, input[role=switch]:checked { background: var(--ac) }
678
+ input[switch]:checked, input[role=switch]:checked { background: var(--fertig-ac) }
611
679
  /* translate is physical, so the knob is told which way to travel */
612
680
  input[switch]:checked::after, input[role=switch]:checked::after { translate: .9em 0 }
613
681
  :is(input[switch], input[role=switch]):checked:dir(rtl)::after { translate: -.9em 0 }
614
682
 
615
683
  /* menu: any [popover] */
616
684
  [popover] {
617
- background: var(--el); color: var(--fg); border: 1px solid var(--bd);
618
- border-radius: var(--r); padding: .3rem; min-width: 12rem;
685
+ background: var(--fertig-el); color: var(--fertig-fg); border: 1px solid var(--fertig-bd);
686
+ border-radius: var(--fertig-rw); padding: .35rem; min-width: 12rem;
619
687
  max-block-size: 60dvh; overflow: auto; font-size: .95em;
620
- box-shadow: var(--up2), 0 20px 40px -14px color-mix(in srgb, var(--sh) 40%, transparent);
688
+ box-shadow: var(--fertig-up2), 0 24px 44px -18px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
621
689
  }
622
690
  /* anchor to the invoking button; without support it centres, which is fine.
623
691
  Deliberately NOT scoped to :popover-open — the close is transitioned, so a
@@ -644,14 +712,14 @@ input[switch]:checked::after, input[role=switch]:checked::after { translate: .9e
644
712
  [popover] :is(a, button):not(:disabled) {
645
713
  display: block; width: 100%; text-align: start; background: none; border: 0;
646
714
  box-shadow: none; min-width: 0; padding: .4em .6em; text-decoration: none;
647
- color: inherit; border-radius: calc(var(--r) - 2px); font-weight: 400;
715
+ color: inherit; border-radius: calc(var(--fertig-r) - 2px); font-weight: 400;
648
716
  }
649
- [popover] :is(a, button):hover:not(:disabled) { background: var(--ac); color: var(--on-ac) }
717
+ [popover] :is(a, button):hover:not(:disabled) { background: var(--fertig-ac); color: var(--fertig-on-ac) }
650
718
  [popover] hr { margin: .3rem 0 }
651
719
 
652
720
  /* breadcrumbs: <nav aria-label="Breadcrumb"><ol> */
653
721
  nav[aria-label] ol { display: flex; flex-wrap: wrap; gap: .5em; list-style: none; padding: 0; font-size: .9em }
654
- nav[aria-label] ol li + li::before { content: "/"; margin-inline-end: .5em; color: var(--mut) }
722
+ nav[aria-label] ol li + li::before { content: "/"; margin-inline-end: .5em; color: var(--fertig-mut) }
655
723
 
656
724
  /* busy: [aria-busy=true] */
657
725
  [aria-busy=true] { cursor: progress }
@@ -676,16 +744,18 @@ nav[aria-label] ol li + li::before { content: "/"; margin-inline-end: .5em; colo
676
744
  inset-block-end: 100%; inset-inline-start: 50%;
677
745
  translate: -50% -.45em; z-index: 10; white-space: nowrap; pointer-events: none;
678
746
  background: light-dark(#1d1d1f, #3a3a40); color: #fff; font-size: .8em;
679
- padding: .25em .55em; border-radius: var(--r); box-shadow: var(--up2);
747
+ padding: .25em .55em; border-radius: var(--fertig-r); box-shadow: var(--fertig-up2);
680
748
  }
681
749
  /* translate is physical; the offset has to be told which way to go */
682
750
  [data-tooltip]:dir(rtl)::after { translate: 50% -.45em }
683
751
 
684
752
  /* tones: retint any component by redefining its accent */
685
- [data-tone=ok] { --ac: light-dark(var(--sage-700), oklch(76% .085 155)) }
686
- [data-tone=warn] { --ac: light-dark(var(--gold-700), oklch(80% .095 85)) }
687
- [data-tone=err] { --ac: light-dark(var(--clay-700), oklch(75% .105 35)) }
688
- [data-tone] .badge, .badge[data-tone] { color: var(--ac); border-color: var(--ac) }
753
+ [data-tone=ok] { --fertig-ac: light-dark(var(--fertig-sage-700), oklch(76% .085 155)) }
754
+ [data-tone=warn] { --fertig-ac: light-dark(var(--fertig-gold-700), oklch(80% .095 85)) }
755
+ /* err is the one tone that does not come from the palette: clay at this
756
+ lightness reads as brown, and a destructive button has to read as red. */
757
+ [data-tone=err] { --fertig-ac: light-dark(oklch(52% .16 27), oklch(74% .13 25)) }
758
+ [data-tone] .badge, .badge[data-tone] { color: var(--fertig-ac); border-color: var(--fertig-ac) }
689
759
 
690
760
  /* ---- parity with the component libraries --------------------- */
691
761
 
@@ -693,18 +763,18 @@ nav[aria-label] ol li + li::before { content: "/"; margin-inline-end: .5em; colo
693
763
  [data-avatar] {
694
764
  display: inline-grid; place-items: center; flex: none; overflow: hidden;
695
765
  inline-size: 2.5rem; aspect-ratio: 1; border-radius: 50%;
696
- background: var(--face); border: 1px solid var(--bd);
697
- font-size: .8em; font-weight: 700; color: var(--mut); object-fit: cover;
766
+ background: var(--fertig-face); border: 1px solid var(--fertig-bd);
767
+ font-size: .8em; font-weight: 700; color: var(--fertig-mut); object-fit: cover;
698
768
  }
699
769
  [data-avatar=sm] { inline-size: 1.75rem; font-size: .7em }
700
770
  [data-avatar=lg] { inline-size: 3.5rem; font-size: 1em }
701
771
 
702
772
  /* skeleton: put it on the element that is still loading */
703
773
  [data-skeleton] {
704
- color: transparent; border-radius: var(--r); pointer-events: none;
774
+ color: transparent; border-radius: var(--fertig-r); pointer-events: none;
705
775
  user-select: none; min-height: 1em;
706
- background: linear-gradient(90deg, var(--face),
707
- color-mix(in srgb, var(--fg) 9%, var(--face)), var(--face));
776
+ background: linear-gradient(90deg, var(--fertig-face),
777
+ color-mix(in srgb, var(--fertig-fg) 9%, var(--fertig-face)), var(--fertig-face));
708
778
  background-size: 200% 100%;
709
779
  }
710
780
  [data-skeleton] * { visibility: hidden }
@@ -713,7 +783,13 @@ nav[aria-label] ol li + li::before { content: "/"; margin-inline-end: .5em; colo
713
783
  @keyframes fertig-shimmer { to { background-position: -200% 0 } }
714
784
  }
715
785
 
716
- /* sheet / drawer: the same <dialog>, docked to an edge */
786
+ /* sheet / drawer: the same <dialog>, docked to an edge.
787
+ Docked to a physical edge on purpose — "right" means the right edge of the
788
+ screen, not the end of the writing direction. inset is physical, so a drawer
789
+ slides in from the edge its name promises no matter the direction. The
790
+ translate is physical for the same reason: 100% of the drawer's own inline
791
+ width moves it off the edge it came from. RTL readers still get the same
792
+ right/left drawer as anyone else; direction would make "right" ambiguous. */
717
793
  dialog[data-side] { max-width: none; margin: 0; border-radius: 0; padding: 1.4rem }
718
794
  dialog[data-side=right] {
719
795
  inset: 0 0 0 auto; block-size: 100dvh; inline-size: min(28rem, 92dvw);
@@ -725,7 +801,7 @@ dialog[data-side=left] {
725
801
  }
726
802
  dialog[data-side=bottom] {
727
803
  inset: auto 0 0 0; inline-size: 100%; max-block-size: 85dvh;
728
- border-radius: var(--rw) var(--rw) 0 0; border-bottom: 0;
804
+ border-radius: var(--fertig-rw) var(--fertig-rw) 0 0; border-bottom: 0;
729
805
  }
730
806
  @media (prefers-reduced-motion: no-preference) and (update: fast) {
731
807
  dialog[data-side=right] { translate: 100% 0 }
@@ -765,9 +841,9 @@ dialog[data-side=bottom] {
765
841
  [popover][data-toast-region] > [data-toast] {
766
842
  pointer-events: auto; position: static; inset: auto;
767
843
  min-width: 16rem; max-width: min(24rem, 92dvw); padding: .8rem 1rem;
768
- background: var(--el); color: var(--fg); border: 1px solid var(--bd);
769
- border-radius: var(--r); font-size: .95em;
770
- box-shadow: var(--up2), 0 20px 40px -14px color-mix(in srgb, var(--sh) 40%, transparent);
844
+ background: var(--fertig-el); color: var(--fertig-fg); border: 1px solid var(--fertig-bd);
845
+ border-radius: var(--fertig-rw); font-size: .95em;
846
+ box-shadow: var(--fertig-up2), 0 24px 44px -18px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
771
847
  }
772
848
  @supports (position-area: block-end span-inline-end) {
773
849
  [popover][data-toast-region] { position-area: none; inset: auto 1rem 1rem auto }
@@ -776,10 +852,10 @@ dialog[data-side=bottom] {
776
852
  /* list group: a plain <menu> outside a popover is a bordered list */
777
853
  menu {
778
854
  list-style: none; padding: 0; margin: 1.1rem 0;
779
- border: 1px solid var(--bd); border-radius: var(--r); background: var(--el);
855
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw); background: var(--fertig-el);
780
856
  }
781
857
  menu > li { padding: .55rem .85rem }
782
- menu > li + li { border-top: 1px solid var(--bd) }
858
+ menu > li + li { border-top: 1px solid var(--fertig-bd) }
783
859
  [popover] menu { border: 0; padding: 0; margin: 0; background: none }
784
860
  [popover] menu > li { padding: 0 }
785
861
  [popover] menu > li + li { border-top: 0 }
@@ -795,13 +871,13 @@ nav menu > li { padding: 0 }
795
871
  nav menu > li + li { border-top: 0 }
796
872
  nav menu :is(a, button) {
797
873
  display: block; inline-size: 100%; text-align: start;
798
- padding: .38rem .6rem; border: 0; border-radius: var(--r);
874
+ padding: .38rem .6rem; border: 0; border-radius: var(--fertig-r);
799
875
  background: none; box-shadow: none; font: inherit;
800
- color: var(--fg); text-decoration: none; opacity: .72;
876
+ color: var(--fertig-fg); text-decoration: none; opacity: .72;
801
877
  }
802
- nav menu :is(a, button):hover { background: var(--face); opacity: 1 }
878
+ nav menu :is(a, button):hover { background: var(--fertig-face); opacity: 1 }
803
879
  nav menu :is(a, button)[aria-current] {
804
- background: var(--face); color: var(--ac); font-weight: 600; opacity: 1;
880
+ background: var(--fertig-face); color: var(--fertig-ac); font-weight: 600; opacity: 1;
805
881
  }
806
882
 
807
883
  /* app layout: a sticky sidebar beside the content */
@@ -819,16 +895,14 @@ nav menu :is(a, button)[aria-current] {
819
895
  }
820
896
  [data-carousel] > * { scroll-snap-align: start; flex: 0 0 min(20rem, 82%) }
821
897
 
822
- /* #core-omit-end */
823
-
824
898
  /* ---- the only classes (all optional) ------------------------ */
825
- .muted { color: var(--mut) }
899
+ .muted { color: var(--fertig-mut) }
826
900
  .center { text-align: center }
827
901
  .wide { max-width: 66rem }
828
902
  /* on the toolbar .wide cannot mean "be 66rem wide" — the bar stays full-bleed
829
903
  and its contents move out to the wide column, so an app screen's wordmark
830
904
  lines up with the app's own content rather than floating mid-screen. */
831
- :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav.wide { max-width: none; --nw: 66rem }
905
+ :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav.wide { max-width: none; --fertig-nw: 66rem }
832
906
  .row { display: flex; flex-wrap: wrap; gap: .7rem; align-items: center }
833
907
  /* Form controls are full-width blocks by default, which is right in a form and
834
908
  wrong in a toolbar. Inside a .row they size to the row instead. */
@@ -846,8 +920,8 @@ nav menu :is(a, button)[aria-current] {
846
920
  .grid + * { margin-top: 1.1rem }
847
921
  .card {
848
922
  container-type: inline-size; /* query it: @container (min-width: …) */
849
- border: 1px solid var(--bd); border-radius: var(--r); padding: 1.1rem;
850
- background: var(--el); box-shadow: var(--up2);
923
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rw); padding: 1.1rem;
924
+ background: var(--fertig-el); box-shadow: var(--fertig-up2);
851
925
  display: flex; flex-direction: column; /* so footers line up across a row */
852
926
  gap: .85rem; /* spacing is the container's job */
853
927
  }
@@ -904,12 +978,13 @@ nav menu :is(a, button)[aria-current] {
904
978
  .max-w-full { max-inline-size: 100% }
905
979
  .max-w-none { max-inline-size: none }
906
980
  /* the measure the sheet already sets prose on, as a class you can reach for */
907
- .max-w-prose { max-inline-size: var(--w) }
981
+ .max-w-prose { max-inline-size: var(--fertig-w) }
908
982
  .hidden { display: none }
909
983
 
910
984
  .badge {
911
- border: 1px solid var(--bd); border-radius: calc(infinity * 1px); background: var(--face);
912
- padding: .1em .6em; font: 700 var(--caps); color: var(--mut);
985
+ border: 1px solid var(--fertig-bd); border-radius: var(--fertig-rp); background: var(--fertig-face);
986
+ padding: .1em .6em; font: 700 var(--fertig-caps); color: var(--fertig-mut);
987
+ text-transform: uppercase; letter-spacing: .07em; /* a label, like h5/h6 and th */
913
988
  }
914
989
 
915
990
  /* card anatomy: an optional header, footer, and full-bleed media */
@@ -919,25 +994,25 @@ nav menu :is(a, button)[aria-current] {
919
994
  .card > :is(header, footer) { margin-inline: -1.1rem; padding-inline: 1.1rem }
920
995
  .card > header {
921
996
  margin-top: -.25rem; padding-bottom: .7rem; font-weight: 600;
922
- border-bottom: 1px solid var(--bd);
997
+ border-bottom: 1px solid var(--fertig-bd);
923
998
  }
924
999
  .card > footer {
925
1000
  margin-top: auto; margin-bottom: -.25rem; padding-top: .7rem;
926
- color: var(--mut); font-size: .85em;
927
- border-top: 1px solid var(--bd);
1001
+ color: var(--fertig-mut); font-size: .85em;
1002
+ border-top: 1px solid var(--fertig-bd);
928
1003
  }
929
1004
  .card > :is(img, video):first-child {
930
1005
  align-self: stretch; width: auto; max-width: none; margin: -1.1rem -1.1rem 0;
931
1006
  aspect-ratio: 16 / 9; object-fit: cover;
932
- border: 0; border-radius: var(--r) var(--r) 0 0; box-shadow: none;
1007
+ border: 0; border-radius: var(--fertig-rw) var(--fertig-rw) 0 0; box-shadow: none;
933
1008
  }
934
1009
  /* A background layer rather than a border or an inset shadow: a border would
935
1010
  shift the card's geometry, and an inset shadow does not follow the corner
936
1011
  radius cleanly at this size — it reads as a detached bar with gaps at the
937
1012
  top corners. A gradient is clipped by the radius, so the tone hugs the curve. */
938
1013
  .card[data-tone] {
939
- background: linear-gradient(var(--ac), var(--ac)) top / 100% 3px no-repeat,
940
- var(--el);
1014
+ background: linear-gradient(var(--fertig-ac), var(--fertig-ac)) top / 100% 3px no-repeat,
1015
+ var(--fertig-el);
941
1016
  }
942
1017
 
943
1018
  /* a card that is itself a link or button reacts like one */
@@ -947,8 +1022,8 @@ nav menu :is(a, button)[aria-current] {
947
1022
  text-decoration: none; font: inherit;
948
1023
  }
949
1024
  :is(a, button).card:hover:not(:disabled) {
950
- border-color: color-mix(in srgb, var(--ac) 45%, var(--bd));
951
- box-shadow: var(--up2), 0 12px 26px -14px color-mix(in srgb, var(--sh) 40%, transparent);
1025
+ border-color: color-mix(in srgb, var(--fertig-ac) 45%, var(--fertig-bd));
1026
+ box-shadow: var(--fertig-up2), 0 16px 30px -16px color-mix(in srgb, var(--fertig-sh) 28%, transparent);
952
1027
  translate: 0 -2px;
953
1028
  }
954
1029
  :is(a, button).card:active:not(:disabled) { translate: 0 0 }
@@ -958,26 +1033,26 @@ nav menu :is(a, button)[aria-current] {
958
1033
  win over your own sheet; hidden=until-found must stay findable */
959
1034
  [hidden]:not([hidden=until-found]) { display: none }
960
1035
  @media (prefers-reduced-motion: no-preference) and (update: fast) { html { scroll-behavior: smooth } }
961
- :target { scroll-margin-top: 4rem } /* clears the toolbar */
962
- :is(h1,h2,h3,h4):target { background: color-mix(in srgb, var(--ac) 12%, transparent) }
1036
+ :target { scroll-margin-block-start: 4rem } /* clears the toolbar */
1037
+ :is(h1,h2,h3,h4):target { background: color-mix(in srgb, var(--fertig-ac) 12%, transparent) }
963
1038
  a[target=_blank]:not(:has(> img, > svg))::after { content: " ↗"; font-size: .85em; opacity: .6 }
964
1039
  a:has(> img), a:has(> code) { text-decoration: none }
965
1040
  sup, sub { line-height: 0; font-size: .75em } /* stop them stretching lines */
966
1041
  q { quotes: "“" "”" "‘" "’" }
967
1042
  dfn { font-style: normal; font-weight: 700 }
968
- ins { text-decoration: underline; text-decoration-style: wavy; text-decoration-color: var(--ac) }
969
- del { color: var(--mut) }
970
- address { font-style: normal; color: var(--mut) }
1043
+ ins { text-decoration: underline; text-decoration-style: wavy; text-decoration-color: var(--fertig-ac) }
1044
+ del { color: var(--fertig-mut) }
1045
+ address { font-style: normal; color: var(--fertig-mut) }
971
1046
  hgroup > * { margin: 0 }
972
- hgroup > p { color: var(--mut) }
1047
+ hgroup > p { color: var(--fertig-mut) }
973
1048
  output { font-weight: 700 }
974
1049
  meter { width: 100%; height: .5rem }
975
- optgroup { font-weight: 700; color: var(--mut) }
1050
+ optgroup { font-weight: 700; color: var(--fertig-mut) }
976
1051
  ::file-selector-button { margin-inline-end: .8em }
977
- ::file-selector-button:hover { background: var(--face) }
1052
+ ::file-selector-button:hover { background: var(--fertig-face) }
978
1053
  input:user-invalid, textarea:user-invalid { border-color: light-dark(#c0392b, #ff6b6b) }
979
1054
  input:user-invalid + small { color: light-dark(#c0392b, #ff6b6b) }
980
- summary:hover { color: var(--ac) }
1055
+ summary:hover { color: var(--fertig-ac) }
981
1056
  /* thin scrollbars, macOS weight */
982
1057
  /* scrollbar-width is Safari 18.2, scrollbar-color Safari 26.2 — both younger
983
1058
  than the floor, and both left ungated on purpose: there is no fallback to
@@ -990,12 +1065,12 @@ summary:hover { color: var(--ac) }
990
1065
  :is(pre, table, textarea, dialog, [popover], menu, fieldset,
991
1066
  [role=group], [data-carousel], [data-layout=sidebar] > *) {
992
1067
  scrollbar-width: thin;
993
- scrollbar-color: color-mix(in srgb, var(--fg) 25%, transparent) transparent;
1068
+ scrollbar-color: color-mix(in srgb, var(--fertig-fg) 25%, transparent) transparent;
994
1069
  }
995
1070
 
996
1071
  @page { margin: 2cm }
997
1072
  @media print {
998
- :root { --bg: #fff; --el: #fff; --fg: #000; --bd: #999 }
1073
+ :root { --fertig-bg: #fff; --fertig-el: #fff; --fertig-fg: #000; --fertig-bd: #999 }
999
1074
  :is(body, body > :where(#root, #app, #__next, [data-fertig])) > nav { display: none }
1000
1075
  /* the footer stays: the legal line, the contact and the citations live there,
1001
1076
  and a printout without them is missing the part people keep */
@@ -1028,7 +1103,7 @@ summary:hover { color: var(--ac) }
1028
1103
  /* a wider gamut can hold a cleaner accent than sRGB can */
1029
1104
  @media (color-gamut: p3) {
1030
1105
  @supports (color: oklch(60% .2 250)) {
1031
- :root { --ac: light-dark(oklch(52% .21 258), oklch(76% .15 258)) }
1106
+ :root { --fertig-ac: light-dark(oklch(52% .21 258), oklch(76% .15 258)) }
1032
1107
  }
1033
1108
  }
1034
1109
 
@@ -1037,14 +1112,14 @@ summary:hover { color: var(--ac) }
1037
1112
 
1038
1113
  /* translucency is decorative; drop it when it is unwanted */
1039
1114
  @media (prefers-reduced-transparency: reduce) {
1040
- body > nav { background: var(--face); backdrop-filter: none }
1115
+ body > nav { background: var(--fertig-face); backdrop-filter: none }
1041
1116
  dialog::backdrop { backdrop-filter: none }
1042
1117
  }
1043
1118
 
1044
1119
  @media (prefers-contrast: more) {
1045
1120
  :root {
1046
- --bd: light-dark(#00000059, #ffffff6b);
1047
- --mut: light-dark(#45454a, #c6c6ce);
1121
+ --fertig-bd: light-dark(#00000059, #ffffff6b);
1122
+ --fertig-mut: light-dark(#45454a, #c6c6ce);
1048
1123
  }
1049
1124
  }
1050
1125
 
@@ -1052,11 +1127,11 @@ summary:hover { color: var(--ac) }
1052
1127
  a, button, input, select, textarea, summary, tr,
1053
1128
  [type=submit], [type=button], [type=reset], .card, .badge,
1054
1129
  [role=tab], [role=group] > *, [popover] :is(a, button) {
1055
- transition: box-shadow var(--dur-1) var(--ease),
1056
- background-color var(--dur-1) var(--ease),
1057
- border-color var(--dur-1) var(--ease),
1058
- color var(--dur-1) var(--ease),
1059
- translate .08s var(--ease-out);
1130
+ transition: box-shadow var(--fertig-dur-1) var(--fertig-ease),
1131
+ background-color var(--fertig-dur-1) var(--fertig-ease),
1132
+ border-color var(--fertig-dur-1) var(--fertig-ease),
1133
+ color var(--fertig-dur-1) var(--fertig-ease),
1134
+ translate .08s var(--fertig-ease-out);
1060
1135
  }
1061
1136
  /* the ring must never be animated — it has to be there the instant focus
1062
1137
  lands, not fade in after it */
@@ -1070,25 +1145,25 @@ summary:hover { color: var(--ac) }
1070
1145
  computed-value time, so backgrounds go transparent and colour is inherited,
1071
1146
  and the page can land unreadable rather than plain. These are the same
1072
1147
  colours flattened to sRGB. Last in the layer, so it wins on order over the
1073
- token block at the top. Nothing else needs a twin: an invalid --ac on a
1148
+ token block at the top. Nothing else needs a twin: an invalid --fertig-ac on a
1074
1149
  [data-tone] or [data-accent] element just inherits the one from :root. */
1075
1150
  @supports not (color: light-dark(#000, #fff)) {
1076
1151
  :root {
1077
- --bg: #ece9e5; --el: #fefdfc; --face: #f8f5f2; --fg: #1d1a17;
1078
- --mut: #5e5a55; --bd: #1d1a1721; --tb: #f8f5f2d9;
1079
- --ac: #494fcc; --on-ac: #fff;
1152
+ --fertig-bg: #e6eaef; --fertig-el: #fafcfe; --fertig-face: #f3f6fa; --fertig-fg: #151b24;
1153
+ --fertig-mut: #525962; --fertig-bd: #151b2421; --fertig-tb: #f3f6fad9;
1154
+ --fertig-ac: #2850de; --fertig-on-ac: #fff;
1080
1155
  }
1081
1156
  @media (prefers-color-scheme: dark) {
1082
1157
  :root:not([data-theme=light]) {
1083
- --bg: #0f0d0a; --el: #1a1815; --face: #24211e; --fg: #f0eeec;
1084
- --mut: #aba7a2; --bd: #f0eeec21; --tb: #24211ed9;
1085
- --ac: #a3b2fe; --on-ac: #06121f;
1158
+ --fertig-bg: #090c12; --fertig-el: #141920; --fertig-face: #1c222a; --fertig-fg: #eaeff7;
1159
+ --fertig-mut: #9da5b1; --fertig-bd: #eaeff721; --fertig-tb: #1c222ad9;
1160
+ --fertig-ac: #90b5ff; --fertig-on-ac: #07071c;
1086
1161
  }
1087
1162
  }
1088
1163
  [data-theme=dark] {
1089
- --bg: #0f0d0a; --el: #1a1815; --face: #24211e; --fg: #f0eeec;
1090
- --mut: #aba7a2; --bd: #f0eeec21; --tb: #24211ed9;
1091
- --ac: #a3b2fe; --on-ac: #06121f;
1164
+ --fertig-bg: #090c12; --fertig-el: #141920; --fertig-face: #1c222a; --fertig-fg: #eaeff7;
1165
+ --fertig-mut: #9da5b1; --fertig-bd: #eaeff721; --fertig-tb: #1c222ad9;
1166
+ --fertig-ac: #90b5ff; --fertig-on-ac: #07071c;
1092
1167
  }
1093
1168
  }
1094
1169